Import kcontacts_16.04.2.orig.tar.xz
authorMaximiliano Curia <maxy@debian.org>
Sat, 18 Jun 2016 07:32:47 +0000 (08:32 +0100)
committerMaximiliano Curia <maxy@debian.org>
Sat, 18 Jun 2016 07:32:47 +0000 (08:32 +0100)
[dgit import orig kcontacts_16.04.2.orig.tar.xz]

169 files changed:
.arcconfig [new file with mode: 0644]
.krazy [new file with mode: 0644]
.reviewboardrc [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
KF5ContactsConfig.cmake.in [new file with mode: 0644]
Mainpage.dox [new file with mode: 0644]
README [new file with mode: 0644]
README.AddressFormat [new file with mode: 0644]
autotests/CMakeLists.txt [new file with mode: 0644]
autotests/addresseetest.cpp [new file with mode: 0644]
autotests/addresseetest.h [new file with mode: 0644]
autotests/addresstest.cpp [new file with mode: 0644]
autotests/addresstest.h [new file with mode: 0644]
autotests/calendarurltest.cpp [new file with mode: 0644]
autotests/calendarurltest.h [new file with mode: 0644]
autotests/contactgrouptest.cpp [new file with mode: 0644]
autotests/data/v4_0.vcard1.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard2.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard3.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard4.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard5.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard6.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard7.vcf.ref [new file with mode: 0644]
autotests/data/v4_0.vcard8.vcf.ref [new file with mode: 0644]
autotests/data/vcard1.vcf [new file with mode: 0644]
autotests/data/vcard1.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard1.vcf.ref [new file with mode: 0644]
autotests/data/vcard10.vcf [new file with mode: 0644]
autotests/data/vcard10.vcf.ref [new file with mode: 0644]
autotests/data/vcard11.vcf [new file with mode: 0644]
autotests/data/vcard11.vcf.ref [new file with mode: 0644]
autotests/data/vcard12.vcf [new file with mode: 0644]
autotests/data/vcard12.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard12.vcf.ref [new file with mode: 0644]
autotests/data/vcard13.vcf [new file with mode: 0644]
autotests/data/vcard13.vcf.ref [new file with mode: 0644]
autotests/data/vcard14.vcf [new file with mode: 0644]
autotests/data/vcard14.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard14.vcf.ref [new file with mode: 0644]
autotests/data/vcard2.vcf [new file with mode: 0644]
autotests/data/vcard2.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard2.vcf.ref [new file with mode: 0644]
autotests/data/vcard3.vcf [new file with mode: 0644]
autotests/data/vcard3.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard3.vcf.ref [new file with mode: 0644]
autotests/data/vcard4.vcf [new file with mode: 0644]
autotests/data/vcard4.vcf.ref [new file with mode: 0644]
autotests/data/vcard5.vcf [new file with mode: 0644]
autotests/data/vcard5.vcf.ref [new file with mode: 0644]
autotests/data/vcard6.vcf [new file with mode: 0644]
autotests/data/vcard6.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard6.vcf.ref [new file with mode: 0644]
autotests/data/vcard7.vcf [new file with mode: 0644]
autotests/data/vcard7.vcf.2_1ref [new file with mode: 0644]
autotests/data/vcard7.vcf.ref [new file with mode: 0644]
autotests/data/vcard8.vcf [new file with mode: 0644]
autotests/data/vcard8.vcf.ref [new file with mode: 0644]
autotests/data/vcard9.vcf [new file with mode: 0644]
autotests/data/vcard9.vcf.ref [new file with mode: 0644]
autotests/emailtest.cpp [new file with mode: 0644]
autotests/emailtest.h [new file with mode: 0644]
autotests/gendertest.cpp [new file with mode: 0644]
autotests/gendertest.h [new file with mode: 0644]
autotests/geotest.cpp [new file with mode: 0644]
autotests/geotest.h [new file with mode: 0644]
autotests/impptest.cpp [new file with mode: 0644]
autotests/impptest.h [new file with mode: 0644]
autotests/keytest.cpp [new file with mode: 0644]
autotests/keytest.h [new file with mode: 0644]
autotests/langtest.cpp [new file with mode: 0644]
autotests/langtest.h [new file with mode: 0644]
autotests/ldifconvertertest.cpp [new file with mode: 0644]
autotests/ldifconvertertest.h [new file with mode: 0644]
autotests/phonenumbertest.cpp [new file with mode: 0644]
autotests/phonenumbertest.h [new file with mode: 0644]
autotests/picturetest.cpp [new file with mode: 0644]
autotests/picturetest.h [new file with mode: 0644]
autotests/relatedtest.cpp [new file with mode: 0644]
autotests/relatedtest.h [new file with mode: 0644]
autotests/resourcelocatorurltest.cpp [new file with mode: 0644]
autotests/resourcelocatorurltest.h [new file with mode: 0644]
autotests/secrecytest.cpp [new file with mode: 0644]
autotests/secrecytest.h [new file with mode: 0644]
autotests/soundtest.cpp [new file with mode: 0644]
autotests/soundtest.h [new file with mode: 0644]
autotests/testlock.h [new file with mode: 0644]
autotests/testroundtrip.cpp [new file with mode: 0644]
autotests/testroundtrip.qrc [new file with mode: 0644]
autotests/timezonetest.cpp [new file with mode: 0644]
autotests/timezonetest.h [new file with mode: 0644]
autotests/vcarddragtest.cpp [new file with mode: 0644]
metainfo.yaml [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/Messages.sh [new file with mode: 0644]
src/address.cpp [new file with mode: 0644]
src/address.h [new file with mode: 0644]
src/addressee.cpp [new file with mode: 0644]
src/addressee.h [new file with mode: 0644]
src/addresseehelper.cpp [new file with mode: 0644]
src/addresseehelper.h [new file with mode: 0644]
src/addresseelist.cpp [new file with mode: 0644]
src/addresseelist.h [new file with mode: 0644]
src/calendarurl.cpp [new file with mode: 0644]
src/calendarurl.h [new file with mode: 0644]
src/contactgroup.cpp [new file with mode: 0644]
src/contactgroup.h [new file with mode: 0644]
src/contactgroup.xsd [new file with mode: 0644]
src/contactgrouptool.cpp [new file with mode: 0644]
src/contactgrouptool.h [new file with mode: 0644]
src/converter/CMakeLists.txt [new file with mode: 0644]
src/converter/ldifconverter.cpp [new file with mode: 0644]
src/converter/ldifconverter.h [new file with mode: 0644]
src/converter/vcardconverter.cpp [new file with mode: 0644]
src/converter/vcardconverter.h [new file with mode: 0644]
src/countrytransl.map [new file with mode: 0644]
src/email.cpp [new file with mode: 0644]
src/email.h [new file with mode: 0644]
src/field.cpp [new file with mode: 0644]
src/field.h [new file with mode: 0644]
src/gender.cpp [new file with mode: 0644]
src/gender.h [new file with mode: 0644]
src/geo.cpp [new file with mode: 0644]
src/geo.h [new file with mode: 0644]
src/impp.cpp [new file with mode: 0644]
src/impp.h [new file with mode: 0644]
src/key.cpp [new file with mode: 0644]
src/key.h [new file with mode: 0644]
src/lang.cpp [new file with mode: 0644]
src/lang.h [new file with mode: 0644]
src/ldapdn.cpp [new file with mode: 0644]
src/ldapdn_p.h [new file with mode: 0644]
src/ldif.cpp [new file with mode: 0644]
src/ldif_p.h [new file with mode: 0644]
src/phonenumber.cpp [new file with mode: 0644]
src/phonenumber.h [new file with mode: 0644]
src/picture.cpp [new file with mode: 0644]
src/picture.h [new file with mode: 0644]
src/related.cpp [new file with mode: 0644]
src/related.h [new file with mode: 0644]
src/resourcelocatorurl.cpp [new file with mode: 0644]
src/resourcelocatorurl.h [new file with mode: 0644]
src/secrecy.cpp [new file with mode: 0644]
src/secrecy.h [new file with mode: 0644]
src/sortmode.cpp [new file with mode: 0644]
src/sortmode.h [new file with mode: 0644]
src/sound.cpp [new file with mode: 0644]
src/sound.h [new file with mode: 0644]
src/timezone.cpp [new file with mode: 0644]
src/timezone.h [new file with mode: 0644]
src/vcarddrag.cpp [new file with mode: 0644]
src/vcarddrag.h [new file with mode: 0644]
src/vcardparser/CMakeLists.txt [new file with mode: 0644]
src/vcardparser/vcard.cpp [new file with mode: 0644]
src/vcardparser/vcard.h [new file with mode: 0644]
src/vcardparser/vcardline.cpp [new file with mode: 0644]
src/vcardparser/vcardline.h [new file with mode: 0644]
src/vcardparser/vcardparser.cpp [new file with mode: 0644]
src/vcardparser/vcardparser.h [new file with mode: 0644]
src/vcardtool.cpp [new file with mode: 0644]
src/vcardtool.h [new file with mode: 0644]
tests/CMakeLists.txt [new file with mode: 0644]
tests/README.testing [new file with mode: 0644]
tests/checkvcard.pl [new file with mode: 0755]
tests/testread.cpp [new file with mode: 0644]
tests/testread2.cpp [new file with mode: 0644]
tests/testutils.cpp [new file with mode: 0644]
tests/testutils.h [new file with mode: 0644]
tests/testwrite.cpp [new file with mode: 0644]

diff --git a/.arcconfig b/.arcconfig
new file mode 100644 (file)
index 0000000..20d53ee
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "phabricator.uri" : "https://phabricator.kde.org/project/profile/34/",
+  "history.immutable" : true
+}
diff --git a/.krazy b/.krazy
new file mode 100644 (file)
index 0000000..80b3c47
--- /dev/null
+++ b/.krazy
@@ -0,0 +1 @@
+SKIP /scripts/|/tests/|/testroundtrip
diff --git a/.reviewboardrc b/.reviewboardrc
new file mode 100644 (file)
index 0000000..8d7e2e9
--- /dev/null
@@ -0,0 +1,5 @@
+REVIEWBOARD_URL = "https://git.reviewboard.kde.org"
+#REPOSITORY = "git://anongit.kde.org/kcontacts"
+BRANCH = "master"
+TARGET_GROUPS = "kdepimlibs"
+TARGET_PEOPLE = "mlaurent"
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9abb646
--- /dev/null
@@ -0,0 +1,67 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(KContacts)
+
+# ECM setup
+find_package(ECM 5.19.0 CONFIG REQUIRED)
+set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${KContacts_SOURCE_DIR}/cmake)
+
+include(GenerateExportHeader)
+include(ECMGenerateHeaders)
+include(ECMGeneratePriFile)
+include(ECMPackageConfigHelpers)
+include(ECMSetupVersion)
+include(FeatureSummary)
+include(KDEInstallDirs)
+include(KDECMakeSettings)
+include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
+include(ECMQtDeclareLoggingCategory)
+
+set(KF5_VERSION "5.19.0")
+set(KCONTACTS_LIB_VERSION "5.2.2")
+
+ecm_setup_version(${KCONTACTS_LIB_VERSION} VARIABLE_PREFIX KContacts
+                        VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kcontacts_version.h"
+                        PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ContactsConfigVersion.cmake"
+                        SOVERSION 5
+)
+
+########### Find packages ###########
+find_package(Qt5 5.4 REQUIRED COMPONENTS DBus Gui)
+find_package(KF5 ${KF5_VERSION} REQUIRED COMPONENTS CoreAddons I18n Config Codecs)
+
+########### Targets ###########
+
+add_definitions(-DTRANSLATION_DOMAIN=\"kcontacts5\")
+add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII")
+add_subdirectory(src)
+
+if(BUILD_TESTING)
+    add_subdirectory(autotests)
+    add_subdirectory(tests)
+endif()
+
+########### CMake Config Files ###########
+set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Contacts")
+
+ecm_configure_package_config_file(
+  "${CMAKE_CURRENT_SOURCE_DIR}/KF5ContactsConfig.cmake.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/KF5ContactsConfig.cmake"
+  INSTALL_DESTINATION  ${CMAKECONFIG_INSTALL_DIR}
+)
+
+install(FILES
+  "${CMAKE_CURRENT_BINARY_DIR}/KF5ContactsConfig.cmake"
+  "${CMAKE_CURRENT_BINARY_DIR}/KF5ContactsConfigVersion.cmake"
+  DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
+  COMPONENT Devel
+)
+
+install(EXPORT KF5ContactsTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5ContactsTargets.cmake NAMESPACE KF5::)
+
+install(FILES
+   ${CMAKE_CURRENT_BINARY_DIR}/kcontacts_version.h
+  DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel
+)
+
+feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..2d2d780
--- /dev/null
@@ -0,0 +1,510 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/KF5ContactsConfig.cmake.in b/KF5ContactsConfig.cmake.in
new file mode 100644 (file)
index 0000000..2047983
--- /dev/null
@@ -0,0 +1,5 @@
+@PACKAGE_INIT@
+
+find_dependency(KF5CoreAddons "@KF5_VERSION@")
+
+include("${CMAKE_CURRENT_LIST_DIR}/KF5ContactsTargets.cmake")
diff --git a/Mainpage.dox b/Mainpage.dox
new file mode 100644 (file)
index 0000000..fafd789
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+  Mainpage.dox -- for now, copied from README
+ */
+
+/**
+@mainpage LIBKContacts - new address book API for KDE
+
+@section purpose Purpose
+
+libkabc provides an API for address book data. This can be used by all KDE
+application using data of this type, e.g. KAddressBook, KMail, KOrganizer,
+KPilot etc. It is meant as replacement for libkab (in kdebase/kab).
+
+@section features Features
+
+- Value based interface, addressbook entry data is implicitly shared.
+- Address book entries are identified by a unique id.
+- vCard backend (RFC 2425 / RFC 2426).
+- Locking mechanism to support concurrent access to the address book by
+  multiple processes.
+- Notification on change of addressbook by other process.
+- Dialog for selecting address book entries, supports mouse and keyboard
+  selection, supports automatic name completion.
+- GUI client for viewing, modifying address book data. This is aimed at
+  developers not end users.
+- Tool for converting data, written with libkab, to libkabc format.
+- Multiple backends (resources) for storing entries e.g. LDAP
+
+@authors Cornelius Schumacher \<schumacher@kde.org\>
+
+<b>LICENCE: LGPL</b>
+*/
+// DOXYGEN_REFERENCES = kdecore kdeui kresources
+// DOXYGEN_TOOLTIP = "An API for the KDE Addressbook."
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..5376317
--- /dev/null
+++ b/README
@@ -0,0 +1,28 @@
+KContacts - new address book API for KDE
+
+PURPOSE:
+
+KCcontacts provides an API for address book data. This can be used by all
+application using data of this type, e.g. KAddressBook, KMail, KOrganizer,
+etc. 
+
+FEATURES:
+
+- Value based interface, addressbook entry data is implicitly shared.
+- Address book entries are identified by a unique id.
+- vCard backend (RFC 2425 / RFC 2426).
+- Locking mechanism to support concurrent access to the address book by
+  multiple processes.
+- Notification on change of addressbook by other process.
+- Dialog for selecting address book entries, supports mouse and keyboard
+  selection, supports automatic name completion.
+- GUI client for viewing, modifying address book data. This is aimed at
+  developers not end users.
+- Tool for converting data, written with libkab, to libkabc format.
+- Multiple backends (resources) for storing entries e.g. LDAP
+
+AUTHOR: Cornelius Schumacher <schumacher@kde.org>
+
+LICENCE: LGPL
+
+DATE: 13 Oct 2001
diff --git a/README.AddressFormat b/README.AddressFormat
new file mode 100644 (file)
index 0000000..6c53f5c
--- /dev/null
@@ -0,0 +1,66 @@
+Address formats can be a tricky thing. KContacts tries very hard to perfectly fit
+the needs of ~95% of users and to be at least sufficient for the other 5%.
+
+The formatting of an address depends on the destination country as well as on
+the origin country of a letter. Basically, the part indicating the destination
+country follows the rules of the country of origin, all the rest follows the
+rules of the destination country. So we need to store for every country a) the
+country positioning and b) the address formatting.
+
+Address formats should usually be stored in a country's entry.desktop. There we
+store the country position in field "AddressCountryPosition" and the address
+format in a field "AddressFormat". Note that for most countries one field
+"AddressFormat" is sufficient for personal as well as company addresses
+(because personal addresses look just like business addresses without company);
+however, in some countries (eg. Hungary) business addresses differ in their
+structure. In this case you have the possibility of adding another field
+"BusinessAddressFormat" which will be preferred for formatting of business
+addresses; if KContacts can't find such a field, it will fall back to
+"AddressFormat". (Please use BusinessAddressFormat ONLY if you really need to)
+
+The format consists mainly of tags that will be replaced by address fields.
+The list of tags may grow in the future, the format *might* change in the near
+future, but I hope not.
+
+Any comments very very welcome to kde-pim@kde.org or to jost@schenck.de.
+
+-Jost.
+
+Fields AddressFormat and BusinessAddressFormat
+------------------------------------------------
+%n  = real name
+%N  = REAL NAME
+%cm = company
+%CM = COMPANY
+%s  = street
+%S  = STREET
+%z  = zip code
+%l  = location
+%L  = LOCATION
+%r  = region
+%R  = REGION
+%p  = post office box
+%,  = conditional comma+whitespace, 
+      will be left out if the value left or right of it is purged
+%w  = conditional whitespace,
+      will be left out if the value left or right of it is purged
+%0(...) = the text inside the brackets will be completely purged if not
+      at least one tag inside it evaluates to something. Example: when the
+      address doesn't have a postbox, the string %0(PO Box %p) will not 
+      evaluate to "PO Box " but to an empty string.
+\n  = newline
+
+Field AddressCountryPosition
+------------------------------------------------
+below = country name below rest of address
+BELOW = country name below in capital letters
+above = country name above rest of address
+ABOVE = country name above in capital letters
+
+Some Tips
+------------------------------------------------
+- You sometimes have three fields in a line which can all be empty. If you eg.
+separate them all with conditional whitespace (same goes for cond. comma) like
+in "%z%w%r%w%l" and only the middle value (here: region) is empty, there will
+be no whitespace at all between the outer values (here: zipcode and location).
+To avoid this, combine two of these values with purge brackets: %0(%z%w%r)%w%l. 
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7c18331
--- /dev/null
@@ -0,0 +1,14 @@
+include(ECMAddTests)
+
+set(QT_REQUIRED_VERSION "5.2.0")
+find_package(Qt5Test ${QT_REQUIRED_VERSION} CONFIG REQUIRED)
+
+########### next target ###############
+
+set(testroundtrip_srcs testroundtrip.cpp)
+qt5_add_resources(testroundtrip_srcs testroundtrip.qrc)
+add_executable(testroundtrip ${testroundtrip_srcs})
+add_test(NAME kcontacts-testroundtrip COMMAND testroundtrip)
+target_link_libraries(testroundtrip KF5Contacts Qt5::Test)
+
+ecm_add_tests(addresseetest.cpp impptest.cpp emailtest.cpp addresstest.cpp geotest.cpp keytest.cpp phonenumbertest.cpp picturetest.cpp secrecytest.cpp soundtest.cpp timezonetest.cpp  contactgrouptest.cpp vcarddragtest.cpp langtest.cpp gendertest.cpp  calendarurltest.cpp ldifconvertertest.cpp relatedtest.cpp resourcelocatorurltest.cpp NAME_PREFIX "kcontacts-" LINK_LIBRARIES KF5Contacts Qt5::Test KF5::ConfigCore Qt5::Gui)
diff --git a/autotests/addresseetest.cpp b/autotests/addresseetest.cpp
new file mode 100644 (file)
index 0000000..156d642
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+//krazy:excludeall=contractions
+
+#include "addresseetest.h"
+#include "kcontacts/addressee.h"
+#include <qtest.h>
+
+QTEST_MAIN(AddresseeTest)
+
+void AddresseeTest::emptyTest()
+{
+    KContacts::Addressee addressee;
+    QVERIFY(addressee.isEmpty());
+
+    KContacts::Addressee addresseeWithMail;
+    addresseeWithMail.insertEmail(QStringLiteral("foo@bar.org"));
+    QVERIFY(!addresseeWithMail.isEmpty());
+}
+
+void AddresseeTest::storeTest()
+{
+    KContacts::Addressee addressee;
+
+    KContacts::Picture logo(QStringLiteral("http://scottlandyard.info/pics/logo.png"));
+    KContacts::Picture photo(QStringLiteral("http://scottlandyard.info/~sinclair/photo.png"));
+    KContacts::Sound sound(QStringLiteral("http://scottlandyard.info/~sinclair/sound.wav"));
+
+    QStringList emails;
+    emails << QStringLiteral("john@sinclair.com") << QStringLiteral("ghosthunter@sinclair.com");
+
+    KContacts::Key::List keys;
+    keys << KContacts::Key(QStringLiteral("SecretKey"));
+
+    QStringList categories;
+    categories << QStringLiteral("Helper") << QStringLiteral("Friend");
+
+    QStringList customs;
+    customs << QStringLiteral("X-Danger: high");
+
+    KContacts::Gender gender(QStringLiteral("H"));
+    addressee.setGender(gender);
+    KContacts::Lang lang(QLatin1String("lang"));
+    addressee.setLangs(KContacts::Lang::List() << lang);
+
+    addressee.setUid(QStringLiteral("My uid"));
+    addressee.setName(QStringLiteral("John Sinclair"));
+    addressee.setFormattedName(QStringLiteral("Sinclair, John"));
+    addressee.setFamilyName(QStringLiteral("Sinclair"));
+    addressee.setGivenName(QStringLiteral("John"));
+    addressee.setAdditionalName(QStringLiteral("Bob"));
+    addressee.setPrefix(QStringLiteral("Sir"));
+    addressee.setSuffix(QStringLiteral("II"));
+    addressee.setNickName(QStringLiteral("ghosthunter"));
+    addressee.setBirthday(QDateTime(QDate(1982, 7, 19)));
+    addressee.setMailer(QStringLiteral("mutt"));
+    addressee.setTimeZone(KContacts::TimeZone(2));
+    addressee.setGeo(KContacts::Geo(42, 23));
+    addressee.setTitle(QStringLiteral("Ghost Hunter"));
+    addressee.setRole(QStringLiteral("Leader"));
+    addressee.setOrganization(QStringLiteral("Scottland Yard"));
+    addressee.setNote(QStringLiteral("Don't cross black deads way..."));
+    addressee.setProductId(QStringLiteral("ProductId45"));
+    addressee.setRevision(QDateTime(QDate(1982, 9, 15)));
+    addressee.setSortString(QStringLiteral("Name"));
+    KContacts::ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("www.scottlandyard.info")));
+    addressee.setUrl(url);
+    addressee.setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public));
+    addressee.setLogo(logo);
+    addressee.setPhoto(photo);
+    addressee.setSound(sound);
+    addressee.setEmails(emails);
+    addressee.setKeys(keys);
+    addressee.setCategories(categories);
+    addressee.setCustoms(customs);
+    addressee.setKind(QStringLiteral("foo"));
+    addressee.setChanged(false);
+    KContacts::Impp imp;
+    imp.setType(KContacts::Impp::GaduGadu);
+    imp.setAddress(QStringLiteral("foo@kde.org"));
+    KContacts::Impp::List listImp;
+    listImp << imp;
+    addressee.setImppList(listImp);
+
+    QVERIFY(addressee.imppList() == listImp);
+    QVERIFY(addressee.langs() == (KContacts::Lang::List() << lang));
+    QVERIFY(addressee.gender() == gender);
+    QVERIFY(addressee.uid() == QStringLiteral("My uid"));
+    QVERIFY(addressee.name() == QStringLiteral("John Sinclair"));
+    QVERIFY(addressee.formattedName() == QStringLiteral("Sinclair, John"));
+    QVERIFY(addressee.familyName() == QStringLiteral("Sinclair"));
+    QVERIFY(addressee.givenName() == QStringLiteral("John"));
+    QVERIFY(addressee.additionalName() == QStringLiteral("Bob"));
+    QVERIFY(addressee.prefix() == QStringLiteral("Sir"));
+    QVERIFY(addressee.suffix() == QStringLiteral("II"));
+    QVERIFY(addressee.nickName() == QStringLiteral("ghosthunter"));
+    QVERIFY(addressee.birthday() == QDateTime(QDate(1982, 7, 19)));
+    QVERIFY(addressee.mailer() == QStringLiteral("mutt"));
+    QVERIFY(addressee.timeZone() == KContacts::TimeZone(2));
+    QVERIFY(addressee.geo() == KContacts::Geo(42, 23));
+    QVERIFY(addressee.title() == QStringLiteral("Ghost Hunter"));
+    QVERIFY(addressee.role() == QStringLiteral("Leader"));
+    QVERIFY(addressee.organization() == QStringLiteral("Scottland Yard"));
+    QVERIFY(addressee.note() == QStringLiteral("Don't cross black deads way..."));
+    QVERIFY(addressee.productId() == QStringLiteral("ProductId45"));
+    QVERIFY(addressee.revision() == QDateTime(QDate(1982, 9, 15)));
+    QVERIFY(addressee.sortString() == QStringLiteral("Name"));
+    QVERIFY(addressee.url() == url);
+    QVERIFY(addressee.secrecy() == KContacts::Secrecy(KContacts::Secrecy::Public));
+    QVERIFY(addressee.logo() == logo);
+    QVERIFY(addressee.photo() == photo);
+    QVERIFY(addressee.sound() == sound);
+    QVERIFY(addressee.emails() == emails);
+    QVERIFY(addressee.keys() == keys);
+    QVERIFY(addressee.categories() == categories);
+    QVERIFY(addressee.customs() == customs);
+    QVERIFY(addressee.changed() == false);
+    QCOMPARE(addressee.kind(), QStringLiteral("foo"));
+}
+
+void AddresseeTest::equalsTest()
+{
+    KContacts::Addressee addressee1, addressee2;
+
+    KContacts::Picture logo(QStringLiteral("http://scottlandyard.info/pics/logo.png"));
+    KContacts::Picture photo(QStringLiteral("http://scottlandyard.info/~sinclair/photo.png"));
+    KContacts::Sound sound(QStringLiteral("http://scottlandyard.info/~sinclair/sound.wav"));
+
+    QStringList emails;
+    emails << QStringLiteral("john@sinclair.com") << QStringLiteral("ghosthunter@sinclair.com");
+
+    KContacts::Key::List keys;
+    keys << KContacts::Key(QStringLiteral("SecretKey"));
+
+    QStringList categories;
+    categories << QStringLiteral("Helper") << QStringLiteral("Friend");
+
+    QStringList customs;
+    customs << QStringLiteral("X-Danger: high");
+
+    addressee1.setUid(QStringLiteral("My uid"));
+    addressee1.setName(QStringLiteral("John Sinclair"));
+    addressee1.setFormattedName(QStringLiteral("Sinclair, John"));
+    addressee1.setFamilyName(QStringLiteral("Sinclair"));
+    addressee1.setGivenName(QStringLiteral("John"));
+    addressee1.setAdditionalName(QStringLiteral("Bob"));
+    addressee1.setPrefix(QStringLiteral("Sir"));
+    addressee1.setSuffix(QStringLiteral("II"));
+    addressee1.setNickName(QStringLiteral("ghosthunter"));
+    addressee1.setBirthday(QDateTime(QDate(1982, 7, 19)));
+    addressee1.setMailer(QStringLiteral("mutt"));
+    addressee1.setTimeZone(KContacts::TimeZone(2));
+    addressee1.setGeo(KContacts::Geo(42, 23));
+    addressee1.setTitle(QStringLiteral("Ghost Hunter"));
+    addressee1.setRole(QStringLiteral("Leader"));
+    addressee1.setOrganization(QStringLiteral("Scottland Yard"));
+    addressee1.setNote(QStringLiteral("Don't cross black deads way..."));
+    addressee1.setProductId(QStringLiteral("ProductId45"));
+    addressee1.setRevision(QDateTime(QDate(1982, 9, 15)));
+    addressee1.setSortString(QStringLiteral("Name"));
+    KContacts::ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("www.scottlandyard.info")));
+    addressee1.setUrl(url);
+    addressee1.setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public));
+    addressee1.setLogo(logo);
+    addressee1.setPhoto(photo);
+    addressee1.setSound(sound);
+    addressee1.setEmails(emails);
+    addressee1.setKeys(keys);
+    addressee1.setCategories(categories);
+    addressee1.setCustoms(customs);
+    addressee1.setChanged(false);
+
+    addressee2.setUid(QStringLiteral("My uid"));
+    addressee2.setName(QStringLiteral("John Sinclair"));
+    addressee2.setFormattedName(QStringLiteral("Sinclair, John"));
+    addressee2.setFamilyName(QStringLiteral("Sinclair"));
+    addressee2.setGivenName(QStringLiteral("John"));
+    addressee2.setAdditionalName(QStringLiteral("Bob"));
+    addressee2.setPrefix(QStringLiteral("Sir"));
+    addressee2.setSuffix(QStringLiteral("II"));
+    addressee2.setNickName(QStringLiteral("ghosthunter"));
+    addressee2.setBirthday(QDateTime(QDate(1982, 7, 19)));
+    addressee2.setMailer(QStringLiteral("mutt"));
+    addressee2.setTimeZone(KContacts::TimeZone(2));
+    addressee2.setGeo(KContacts::Geo(42, 23));
+    addressee2.setTitle(QStringLiteral("Ghost Hunter"));
+    addressee2.setRole(QStringLiteral("Leader"));
+    addressee2.setOrganization(QStringLiteral("Scottland Yard"));
+    addressee2.setNote(QStringLiteral("Don't cross black deads way..."));
+    addressee2.setProductId(QStringLiteral("ProductId45"));
+    addressee2.setRevision(QDateTime(QDate(1982, 9, 15)));
+    addressee2.setSortString(QStringLiteral("Name"));
+    addressee2.setUrl(url);
+    addressee2.setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public));
+    addressee2.setLogo(logo);
+    addressee2.setPhoto(photo);
+    addressee2.setSound(sound);
+    addressee2.setEmails(emails);
+    addressee2.setKeys(keys);
+    addressee2.setCategories(categories);
+    addressee2.setCustoms(customs);
+    addressee2.setChanged(false);
+
+    QVERIFY(addressee1 == addressee2);
+}
+
+void AddresseeTest::differsTest()
+{
+    KContacts::Addressee addressee1, addressee2;
+
+    addressee1.setNameFromString(QStringLiteral("John Sinclair"));
+    addressee2.setNameFromString(QStringLiteral("Suko"));
+
+    QVERIFY(addressee1 != addressee2);
+}
+
+void AddresseeTest::assignmentTest()
+{
+    KContacts::Addressee addressee1, addressee2;
+
+    KContacts::Picture logo(QStringLiteral("http://scottlandyard.info/pics/logo.png"));
+    KContacts::Picture photo(QStringLiteral("http://scottlandyard.info/~sinclair/photo.png"));
+    KContacts::Sound sound(QStringLiteral("http://scottlandyard.info/~sinclair/sound.wav"));
+
+    QStringList emails;
+    emails << QStringLiteral("john@sinclair.com") << QStringLiteral("ghosthunter@sinclair.com");
+
+    KContacts::Key::List keys;
+    keys << KContacts::Key(QStringLiteral("SecretKey"));
+
+    QStringList categories;
+    categories << QStringLiteral("Helper") << QStringLiteral("Friend");
+
+    QStringList customs;
+    customs << QStringLiteral("X-Danger: high");
+
+    addressee1.setUid(QStringLiteral("My uid"));
+    addressee1.setName(QStringLiteral("John Sinclair"));
+    addressee1.setFormattedName(QStringLiteral("Sinclair, John"));
+    addressee1.setFamilyName(QStringLiteral("Sinclair"));
+    addressee1.setGivenName(QStringLiteral("John"));
+    addressee1.setAdditionalName(QStringLiteral("Bob"));
+    addressee1.setPrefix(QStringLiteral("Sir"));
+    addressee1.setSuffix(QStringLiteral("II"));
+    addressee1.setNickName(QStringLiteral("ghosthunter"));
+    addressee1.setBirthday(QDateTime(QDate(1982, 7, 19)));
+    addressee1.setMailer(QStringLiteral("mutt"));
+    addressee1.setTimeZone(KContacts::TimeZone(2));
+    addressee1.setGeo(KContacts::Geo(42, 23));
+    addressee1.setTitle(QStringLiteral("Ghost Hunter"));
+    addressee1.setRole(QStringLiteral("Leader"));
+    addressee1.setOrganization(QStringLiteral("Scottland Yard"));
+    addressee1.setNote(QStringLiteral("Don't cross black deads way..."));
+    addressee1.setProductId(QStringLiteral("ProductId45"));
+    addressee1.setRevision(QDateTime(QDate(1982, 9, 15)));
+    addressee1.setSortString(QStringLiteral("Name"));
+    KContacts::ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("www.scottlandyard.info")));
+    addressee1.setUrl(url);
+    addressee1.setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public));
+    addressee1.setLogo(logo);
+    addressee1.setPhoto(photo);
+    addressee1.setSound(sound);
+    addressee1.setEmails(emails);
+    addressee1.setKeys(keys);
+    addressee1.setCategories(categories);
+    addressee1.setCustoms(customs);
+    addressee1.setChanged(false);
+
+    addressee2 = addressee1;
+
+    QVERIFY(addressee1 == addressee2);
+}
+
+void AddresseeTest::serializeTest()
+{
+    KContacts::Addressee addressee1, addressee2;
+
+    KContacts::Picture logo(QStringLiteral("http://scottlandyard.info/pics/logo.png"));
+    KContacts::Picture photo(QStringLiteral("http://scottlandyard.info/~sinclair/photo.png"));
+    KContacts::Sound sound(QStringLiteral("http://scottlandyard.info/~sinclair/sound.wav"));
+
+    QStringList emails;
+    emails << QStringLiteral("john@sinclair.com") << QStringLiteral("ghosthunter@sinclair.com");
+
+    KContacts::Key::List keys;
+    keys << KContacts::Key(QStringLiteral("SecretKey"));
+
+    QStringList categories;
+    categories << QStringLiteral("Helper") << QStringLiteral("Friend");
+
+    QStringList customs;
+    customs << QStringLiteral("FirstApp-FirstKey:FirstValue")
+            << QStringLiteral("SecondApp-SecondKey:SecondValue")
+            << QStringLiteral("ThirdApp-ThirdKey:ThirdValue");
+
+    addressee1.setUid(QStringLiteral("My uid"));
+    addressee1.setName(QStringLiteral("John Sinclair"));
+    addressee1.setFormattedName(QStringLiteral("Sinclair, John"));
+    addressee1.setFamilyName(QStringLiteral("Sinclair"));
+    addressee1.setGivenName(QStringLiteral("John"));
+    addressee1.setAdditionalName(QStringLiteral("Bob"));
+    addressee1.setPrefix(QStringLiteral("Sir"));
+    addressee1.setSuffix(QStringLiteral("II"));
+    addressee1.setNickName(QStringLiteral("ghosthunter"));
+    addressee1.setBirthday(QDateTime(QDate(1982, 7, 19)));
+    addressee1.setMailer(QStringLiteral("mutt"));
+    addressee1.setTimeZone(KContacts::TimeZone(2));
+    addressee1.setGeo(KContacts::Geo(42, 23));
+    addressee1.setTitle(QStringLiteral("Ghost Hunter"));
+    addressee1.setRole(QStringLiteral("Leader"));
+    addressee1.setOrganization(QStringLiteral("Scottland Yard"));
+    addressee1.setNote(QStringLiteral("Don't cross black deads way..."));
+    addressee1.setProductId(QStringLiteral("ProductId45"));
+    addressee1.setRevision(QDateTime(QDate(1982, 9, 15)));
+    addressee1.setSortString(QStringLiteral("Name"));
+    KContacts::ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("www.scottlandyard.info")));
+
+    addressee1.setUrl(url);
+    addressee1.setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Public));
+    addressee1.setLogo(logo);
+    addressee1.setPhoto(photo);
+    addressee1.setSound(sound);
+    addressee1.setEmails(emails);
+    addressee1.setKeys(keys);
+    addressee1.setCategories(categories);
+    addressee1.setCustoms(customs);
+    addressee1.setChanged(false);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << addressee1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> addressee2;
+
+    QVERIFY(addressee1 == addressee2);
+}
+
+void AddresseeTest::fullEmailTest()
+{
+    KContacts::Addressee a;
+    QStringList emails;
+    emails << QStringLiteral("foo@bar.com");
+    a.setEmails(emails);
+    a.setFormattedName(QStringLiteral("firstname \"nickname\" lastname"));
+    QCOMPARE(a.fullEmail(),
+             QStringLiteral("\"firstname \\\"nickname\\\" lastname\" <foo@bar.com>"));
+}
+
+void AddresseeTest::nameFromStringTest()
+{
+    KContacts::Addressee a;
+    a.setNameFromString(QStringLiteral("Firstname Lastname"));
+    QCOMPARE(a.givenName(), QStringLiteral("Firstname"));
+    QCOMPARE(a.familyName(), QStringLiteral("Lastname"));
+    QCOMPARE(a.formattedName(), QStringLiteral("Firstname Lastname"));
+}
+
+void AddresseeTest::customFieldsTest()
+{
+    KContacts::Addressee a;
+
+    // test for empty
+    QVERIFY(a.customs().isEmpty());
+
+    // test insert
+    a.insertCustom(QStringLiteral("MyApp"), QStringLiteral("MyKey"), QStringLiteral("MyValue"));
+    QCOMPARE(a.customs().count(), 1);
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("MyKey")), QStringLiteral("MyValue"));
+
+    a.insertCustom(QStringLiteral("MyApp"), QStringLiteral("MyKey"), QStringLiteral("YourValue"));
+    QCOMPARE(a.customs().count(), 1);   // still one, we overwrite...
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("MyKey")), QStringLiteral("YourValue"));
+
+    // test query non-existing app/key
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("UnknownKey")), QString());
+    QCOMPARE(a.custom(QStringLiteral("UnknownApp"), QStringLiteral("MyKey")), QString());
+
+    // test insert with different key
+    a.insertCustom(QStringLiteral("MyApp"), QStringLiteral("AnotherKey"), QStringLiteral("OtherValue"));
+    QCOMPARE(a.customs().count(), 2);
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("AnotherKey")), QStringLiteral("OtherValue"));
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("MyKey")), QStringLiteral("YourValue"));
+
+    // test insert with different app
+    a.insertCustom(QStringLiteral("OtherApp"), QStringLiteral("OtherKey"), QStringLiteral("OurValue"));
+    QCOMPARE(a.customs().count(), 3);
+    QCOMPARE(a.custom(QStringLiteral("OtherApp"), QStringLiteral("OtherKey")), QStringLiteral("OurValue"));
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("AnotherKey")), QStringLiteral("OtherValue"));
+    QCOMPARE(a.custom(QStringLiteral("MyApp"), QStringLiteral("MyKey")), QStringLiteral("YourValue"));
+
+#if 0 //Order is not defined now as we use a QHash
+    // test customs
+    QCOMPARE(a.customs().at(0), QStringLiteral("MyApp-MyKey:YourValue"));
+    QCOMPARE(a.customs().at(1), QStringLiteral("MyApp-AnotherKey:OtherValue"));
+    QCOMPARE(a.customs().at(2), QStringLiteral("OtherApp-OtherKey:OurValue"));
+#endif
+    // test equal operator
+    KContacts::Addressee b;
+    b.setUid(a.uid());
+    b.insertCustom(QStringLiteral("OtherApp"), QStringLiteral("OtherKey"), QStringLiteral("OurValue"));
+    b.insertCustom(QStringLiteral("MyApp"), QStringLiteral("MyKey"), QStringLiteral("YourValue"));
+    b.insertCustom(QStringLiteral("MyApp"), QStringLiteral("AnotherKey"), QStringLiteral("OtherValue"));
+
+    QCOMPARE(a, b);
+
+    b.insertCustom(QStringLiteral("MyApp"), QStringLiteral("AnotherKey"), QStringLiteral("WrongValue"));
+    QVERIFY(a != b);
+
+    // test setCustoms
+    KContacts::Addressee c;
+    c.insertCustom(QStringLiteral("ThisApp"), QStringLiteral("ShouldNotBe"), QStringLiteral("There"));
+    QCOMPARE(c.customs().count(), 1);
+
+    const QStringList testData = QStringList() << QStringLiteral("FirstApp-FirstKey:FirstValue")
+                                 << QStringLiteral("SecondApp-SecondKey:SecondValue")
+                                 << QStringLiteral("ThirdApp-ThirdKey:ThirdValue");
+
+    c.setCustoms(testData);
+    QCOMPARE(c.customs().count(), 3);
+
+    QCOMPARE(c.custom(QStringLiteral("FirstApp"), QStringLiteral("FirstKey")), QStringLiteral("FirstValue"));
+    QCOMPARE(c.custom(QStringLiteral("SecondApp"), QStringLiteral("SecondKey")), QStringLiteral("SecondValue"));
+    QCOMPARE(c.custom(QStringLiteral("ThirdApp"), QStringLiteral("ThirdKey")), QStringLiteral("ThirdValue"));
+
+    // test remove
+    QCOMPARE(c.customs().count(), 3);
+    c.removeCustom(QStringLiteral("UnknownApp"), QStringLiteral("FirstKey"));
+    QCOMPARE(c.customs().count(), 3);
+    c.removeCustom(QStringLiteral("FirstApp"), QStringLiteral("UnknownKey"));
+    QCOMPARE(c.customs().count(), 3);
+    c.removeCustom(QStringLiteral("FirstApp"), QStringLiteral("FirstKey"));
+    QCOMPARE(c.customs().count(), 2);
+}
+
+void AddresseeTest::parseEmailAddress_data()
+{
+    QTest::addColumn<QString>("inputEmail");
+    QTest::addColumn<QString>("email");
+    QTest::addColumn<QString>("name");
+    QTest::newRow("simpleemail") << QStringLiteral("foo@kde.org") << QStringLiteral("foo@kde.org") << QString();
+    QTest::newRow("email") << QStringLiteral("foo <foo@kde.org>") << QStringLiteral("foo@kde.org") << QStringLiteral("foo");
+    QTest::newRow("namewithdoublequote") << QStringLiteral("\"foo\" <foo@kde.org>") << QStringLiteral("foo@kde.org") << QStringLiteral("foo");
+}
+
+void AddresseeTest::parseEmailAddress()
+{
+    QFETCH(QString, inputEmail);
+    QFETCH(QString, email);
+    QFETCH(QString, name);
+
+    QString parsedName;
+    QString parsedEmail;
+    KContacts::Addressee::parseEmailAddress(inputEmail, parsedName, parsedEmail);
+    QCOMPARE(parsedEmail, email);
+    QCOMPARE(parsedName, name);
+}
diff --git a/autotests/addresseetest.h b/autotests/addresseetest.h
new file mode 100644 (file)
index 0000000..fb161ac
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ADDRESSEE_TEST_H
+#define ADDRESSEE_TEST_H
+
+#include <QtCore/QObject>
+
+class AddresseeTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void emptyTest();
+    void storeTest();
+    void equalsTest();
+    void differsTest();
+    void assignmentTest();
+    void serializeTest();
+    void fullEmailTest();
+    void nameFromStringTest();
+    void customFieldsTest();
+    void parseEmailAddress_data();
+    void parseEmailAddress();
+};
+
+#endif
diff --git a/autotests/addresstest.cpp b/autotests/addresstest.cpp
new file mode 100644 (file)
index 0000000..3632934
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "addresstest.h"
+#include "kcontacts/address.h"
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <qtest.h>
+#include <QStandardPaths>
+#include <QFileInfo>
+
+QTEST_MAIN(AddressTest)
+
+void AddressTest::initTestCase()
+{
+     setenv("LANG", "C", 1);
+}
+
+void AddressTest::emptyTest()
+{
+    KContacts::Address address;
+
+    QVERIFY(address.isEmpty());
+}
+
+void AddressTest::storeTest()
+{
+    KContacts::Address address;
+
+    address.setId(QStringLiteral("My Id"));
+    address.setType(KContacts::Address::Home);
+    address.setPostOfficeBox(QStringLiteral("1234"));
+    address.setExtended(QStringLiteral("My Extended Label"));
+    address.setStreet(QStringLiteral("My Street"));
+    address.setLocality(QStringLiteral("My Locality"));
+    address.setRegion(QStringLiteral("My Region"));
+    address.setPostalCode(QStringLiteral("My PostalCode"));
+    address.setCountry(QStringLiteral("My Country"));
+    address.setLabel(QStringLiteral("My Label"));
+
+    QCOMPARE(address.id(), QStringLiteral("My Id"));
+    QCOMPARE(address.type(), KContacts::Address::Home);
+    QCOMPARE(address.postOfficeBox(), QStringLiteral("1234"));
+    QCOMPARE(address.extended(), QStringLiteral("My Extended Label"));
+    QCOMPARE(address.street(), QStringLiteral("My Street"));
+    QCOMPARE(address.locality(), QStringLiteral("My Locality"));
+    QCOMPARE(address.region(), QStringLiteral("My Region"));
+    QCOMPARE(address.postalCode(), QStringLiteral("My PostalCode"));
+    QCOMPARE(address.country(), QStringLiteral("My Country"));
+    QCOMPARE(address.label(), QStringLiteral("My Label"));
+}
+
+void AddressTest::equalsTest()
+{
+    KContacts::Address address1, address2;
+
+    address1.setId(QStringLiteral("My Id"));
+    address1.setType(KContacts::Address::Home);
+    address1.setPostOfficeBox(QStringLiteral("1234"));
+    address1.setExtended(QStringLiteral("My Extended Label"));
+    address1.setStreet(QStringLiteral("My Street"));
+    address1.setLocality(QStringLiteral("My Locality"));
+    address1.setRegion(QStringLiteral("My Region"));
+    address1.setPostalCode(QStringLiteral("My Postalcode"));
+    address1.setCountry(QStringLiteral("My country"));
+    address1.setLabel(QStringLiteral("My Label"));
+
+    address2.setId(QStringLiteral("My Id"));
+    address2.setType(KContacts::Address::Home);
+    address2.setPostOfficeBox(QStringLiteral("1234"));
+    address2.setExtended(QStringLiteral("My Extended Label"));
+    address2.setStreet(QStringLiteral("My Street"));
+    address2.setLocality(QStringLiteral("My Locality"));
+    address2.setRegion(QStringLiteral("My Region"));
+    address2.setPostalCode(QStringLiteral("My Postalcode"));
+    address2.setCountry(QStringLiteral("My country"));
+    address2.setLabel(QStringLiteral("My Label"));
+
+    QVERIFY(address1 == address2);
+}
+
+void AddressTest::differsTest()
+{
+    KContacts::Address address1(KContacts::Address::Home);
+    KContacts::Address address2(KContacts::Address::Work);
+
+    QVERIFY(address1 != address2);
+}
+
+void AddressTest::assignmentTest()
+{
+    KContacts::Address address1, address2;
+
+    address1.setId(QStringLiteral("My Id"));
+    address1.setType(KContacts::Address::Home);
+    address1.setPostOfficeBox(QStringLiteral("1234"));
+    address1.setExtended(QStringLiteral("My Extended Label"));
+    address1.setStreet(QStringLiteral("My Street"));
+    address1.setLocality(QStringLiteral("My Locality"));
+    address1.setRegion(QStringLiteral("My Region"));
+    address1.setPostalCode(QStringLiteral("My Postalcode"));
+    address1.setCountry(QStringLiteral("My country"));
+    address1.setLabel(QStringLiteral("My Label"));
+
+    address2 = address1;
+
+    QVERIFY(address1 == address2);
+}
+
+void AddressTest::serializeTest()
+{
+    KContacts::Address address1, address2;
+
+    address1.setType(KContacts::Address::Work);
+    address1.setPostOfficeBox(QStringLiteral("1234"));
+    address1.setExtended(QStringLiteral("My Extended Label"));
+    address1.setStreet(QStringLiteral("My Street"));
+    address1.setLocality(QStringLiteral("My Locality"));
+    address1.setRegion(QStringLiteral("My Region"));
+    address1.setPostalCode(QStringLiteral("My Postalcode"));
+    address1.setCountry(QStringLiteral("My country"));
+    address1.setLabel(QStringLiteral("My Label"));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << address1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> address2;
+
+    QVERIFY(address1 == address2);
+}
+
+void AddressTest::formatTest()
+{
+    {
+        // check availability of country to ISO code mapping data file
+        const QString mapfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kcontacts/countrytransl.map"));
+        QVERIFY2(!mapfile.isEmpty(), "Country to ISO code mapping data file does not exist");
+
+        QFileInfo fileInfo(mapfile);
+        QVERIFY2(fileInfo.exists(), "Country to ISO code mapping data file does not exist");
+        QVERIFY2(fileInfo.isReadable(), "Country to ISO code mapping data file is not readable");
+    }
+
+    {
+        const QString templateFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/locale/countries/de/country.desktop"));
+        QVERIFY(!templateFile.isEmpty());
+        const KConfig templateConfig(templateFile);
+        QVERIFY2(templateConfig.hasGroup("KCM Locale"),
+                 "Address format config for 'de' is missing expected 'KCM Locale' group");
+        const KConfigGroup templateGroup = templateConfig.group("KCM Locale");
+        QVERIFY2(templateGroup.hasKey("AddressFormat"),
+                 "Address format config is missing expected key 'AddressFormat'");
+        const QString addressTemplate = templateGroup.readEntry("AddressFormat");
+        QVERIFY2(!addressTemplate.isEmpty(),
+                 "Address format template for 'de' is empty");
+
+        KContacts::Address address;
+        address.setStreet(QStringLiteral("Lummerlandstr. 1"));
+        address.setPostalCode(QStringLiteral("12345"));
+        address.setLocality(QStringLiteral("Lummerstadt"));
+        address.setCountry(QStringLiteral("Germany"));
+
+        const QString result(QStringLiteral("Jim Knopf\nLummerlandstr. 1\n"
+                                            "12345 Lummerstadt\n\nGERMANY"));
+
+        QCOMPARE(address.formattedAddress(QStringLiteral("Jim Knopf")), result);
+    }
+
+    {
+        KContacts::Address address;
+        address.setStreet(QStringLiteral("457 Foobar Ave"));
+        address.setPostalCode(QStringLiteral("1A2B3C"));
+        address.setLocality(QStringLiteral("Nervousbreaktown"));
+        address.setRegion(QStringLiteral("DC"));
+        address.setCountry(QStringLiteral("United States of America"));
+
+        const QString result(
+            QStringLiteral("Huck Finn\n457 Foobar Ave\nNervousbreaktown,"
+                           "  DC 1A2B3C\n\nUNITED STATES OF AMERICA"));
+        QCOMPARE(address.formattedAddress(QStringLiteral("Huck Finn")), result);
+    }
+
+    {
+        KContacts::Address address;
+        address.setStreet(QStringLiteral("Lummerlandstr. 1"));
+        address.setPostalCode(QStringLiteral("12345"));
+        address.setLocality(QStringLiteral("Lummerstadt"));
+        address.setCountry(QStringLiteral("Deutschland"));
+
+        const QString result(QStringLiteral("Jim Knopf\nLummerlandstr. 1\n"
+                                            "12345 Lummerstadt\n\nDEUTSCHLAND"));
+
+        QCOMPARE(address.formattedAddress(QStringLiteral("Jim Knopf")), result);
+    }
+
+    {
+        KContacts::Address address;
+        address.setStreet(QStringLiteral("Lummerlandstr. 1"));
+        address.setPostalCode(QStringLiteral("12345"));
+        address.setLocality(QStringLiteral("Lummerstadt"));
+        address.setCountry(QString());
+
+        const QString result(QStringLiteral("Jim Knopf\nLummerlandstr. 1\nLummerstadt, 12345"));
+
+        QCOMPARE(address.formattedAddress(QStringLiteral("Jim Knopf")), result);
+    }
+}
diff --git a/autotests/addresstest.h b/autotests/addresstest.h
new file mode 100644 (file)
index 0000000..bd19441
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ADDRESS_TEST_H
+#define ADDRESS_TEST_H
+
+#include <QtCore/QObject>
+
+class AddressTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void initTestCase();
+    void emptyTest();
+    void storeTest();
+    void equalsTest();
+    void differsTest();
+    void assignmentTest();
+    void serializeTest();
+    void formatTest();
+};
+
+#endif
diff --git a/autotests/calendarurltest.cpp b/autotests/calendarurltest.cpp
new file mode 100644 (file)
index 0000000..73dd3ad
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "calendarurltest.h"
+
+#include "calendarurl.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+using namespace KContacts;
+Q_DECLARE_METATYPE(KContacts::CalendarUrl::CalendarType)
+CalendarUrlTest::CalendarUrlTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+CalendarUrlTest::~CalendarUrlTest()
+{
+
+}
+
+void CalendarUrlTest::shouldHaveDefaultValue()
+{
+    CalendarUrl calendarUrl(CalendarUrl::FBUrl);
+    QVERIFY(!calendarUrl.isValid());
+    QVERIFY(calendarUrl.url().isEmpty());
+    QVERIFY(calendarUrl.parameters().isEmpty());
+}
+
+void CalendarUrlTest::shouldAssignValue()
+{
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    CalendarUrl calendarUrl(CalendarUrl::FBUrl);
+    calendarUrl.setParameters(params);
+    QVERIFY(!calendarUrl.isValid());
+    QCOMPARE(calendarUrl.type(), CalendarUrl::FBUrl);
+    QVERIFY(calendarUrl.url().isEmpty());
+    QVERIFY(!calendarUrl.parameters().isEmpty());
+    QCOMPARE(calendarUrl.parameters(), params);
+}
+
+void CalendarUrlTest::shouldAssignExternal()
+{
+    CalendarUrl calendarUrl(CalendarUrl::FBUrl);
+    const CalendarUrl::CalendarType type = CalendarUrl::CALUri;
+    calendarUrl.setType(type);
+    QVERIFY(calendarUrl.url().isEmpty());
+    QCOMPARE(calendarUrl.type(), type);
+}
+
+void CalendarUrlTest::shouldSerialized()
+{
+    for (int i = 0; i < CalendarUrl::EndCalendarType; ++i) {
+        CalendarUrl calendarUrl;
+        CalendarUrl result;
+        CalendarUrl::CalendarType type = static_cast<CalendarUrl::CalendarType>(i);
+        calendarUrl.setType(type);
+        QMap<QString, QStringList> params;
+        params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+        params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+        calendarUrl.setParameters(params);
+        calendarUrl.setUrl(QUrl(QStringLiteral("mailto:foo@kde.org")));
+
+        QByteArray data;
+        QDataStream s(&data, QIODevice::WriteOnly);
+        s << calendarUrl;
+
+        QDataStream t(&data, QIODevice::ReadOnly);
+        t >> result;
+
+        QVERIFY(calendarUrl == result);
+    }
+}
+
+void CalendarUrlTest::shouldEqualCalendarUrl()
+{
+    for (int i = 0; i < CalendarUrl::EndCalendarType; ++i) {
+        CalendarUrl calendarUrl;
+        CalendarUrl result;
+        CalendarUrl::CalendarType type = static_cast<CalendarUrl::CalendarType>(i);
+        calendarUrl.setType(type);
+        QMap<QString, QStringList> params;
+        params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+        params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+        calendarUrl.setUrl(QUrl(QStringLiteral("mailto:foo@kde.org")));
+        calendarUrl.setParameters(params);
+
+        result = calendarUrl;
+        QVERIFY(calendarUrl == result);
+    }
+}
+
+void CalendarUrlTest::shouldParseCalendarUrl()
+{
+    for (int i = CalendarUrl::Unknown + 1; i < CalendarUrl::EndCalendarType; ++i) {
+        CalendarUrl::CalendarType type = static_cast<CalendarUrl::CalendarType>(i);
+        QByteArray baType;
+        switch (type) {
+        case CalendarUrl::Unknown:
+        case CalendarUrl::EndCalendarType:
+            break;
+        case CalendarUrl::FBUrl:
+            baType = QByteArray("FBURL");
+            break;
+        case CalendarUrl::CALUri:
+            baType = QByteArray("CALURI");
+            break;
+        case CalendarUrl::CALADRUri:
+            baType = QByteArray("CALADRURI");
+            break;
+
+        }
+
+        QByteArray vcarddata("BEGIN:VCARD\n"
+                             "VERSION:3.0\n"
+                             "N:LastName;FirstName;;;\n"
+                             "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n");
+        vcarddata += baType;
+        vcarddata += QByteArray(
+                         ";PREF=1:https://sherlockholmes.com/calendar/sherlockholmes\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+        KContacts::VCardTool vcard;
+        const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+        QCOMPARE(lst.count(), 1);
+        QCOMPARE(lst.at(0).calendarUrlList().count(), 1);
+        const CalendarUrl calurl = lst.at(0).calendarUrlList().at(0);
+        QCOMPARE(calurl.type(), type);
+        QCOMPARE(calurl.url(), QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+        QVERIFY(!calurl.parameters().isEmpty());
+    }
+}
+
+void CalendarUrlTest::shouldGenerateVCard_data()
+{
+    QTest::addColumn<KContacts::CalendarUrl::CalendarType>("type");
+    QTest::addColumn<QByteArray>("value");
+    for (int i = CalendarUrl::Unknown + 1; i < CalendarUrl::EndCalendarType; ++i) {
+        KContacts::CalendarUrl::CalendarType type = static_cast<KContacts::CalendarUrl::CalendarType>(i);
+        QByteArray baType;
+        switch (type) {
+        case CalendarUrl::Unknown:
+        case CalendarUrl::EndCalendarType:
+            break;
+        case CalendarUrl::FBUrl:
+            baType = QByteArray("FBURL");
+            break;
+        case CalendarUrl::CALUri:
+            baType = QByteArray("CALURI");
+            break;
+        case CalendarUrl::CALADRUri:
+            baType = QByteArray("CALADRURI");
+            break;
+
+        }
+        QTest::newRow(baType.constData()) << type << baType;
+    }
+}
+
+void CalendarUrlTest::shouldGenerateVCard()
+{
+    QFETCH(KContacts::CalendarUrl::CalendarType, type);
+    QFETCH(QByteArray, value);
+
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    CalendarUrl url;
+    url.setType(type);
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    addr.insertCalendarUrl(url);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected;
+    // Different order
+    if (type == KContacts::CalendarUrl::FBUrl) {
+        expected = QByteArray("BEGIN:VCARD\r\n"
+                              "VERSION:4.0\r\n"
+                              "EMAIL:foo@kde.org\r\n");
+        expected += value + QByteArray(":https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                                       "N:;;;;\r\n"
+                                       "UID:testuid\r\n"
+                                       "END:VCARD\r\n\r\n");
+    } else {
+        expected = QByteArray("BEGIN:VCARD\r\n"
+                              "VERSION:4.0\r\n");
+        expected += value + QByteArray(":https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                                       "EMAIL:foo@kde.org\r\n"
+                                       "N:;;;;\r\n"
+                                       "UID:testuid\r\n"
+                                       "END:VCARD\r\n\r\n");
+    }
+
+    QCOMPARE(ba, expected);
+}
+
+void CalendarUrlTest::shouldGenerateVCardWithParameter()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    CalendarUrl url;
+    url.setType(CalendarUrl::CALUri);
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    url.setParameters(params);
+    addr.insertCalendarUrl(url);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected = QByteArray("BEGIN:VCARD\r\n"
+                                     "VERSION:4.0\r\n"
+                                     "CALURI;FOO2=bla2,blo2:https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                                     "EMAIL:foo@kde.org\r\n"
+                                     "N:;;;;\r\n"
+                                     "UID:testuid\r\n"
+                                     "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+void CalendarUrlTest::shouldNotGeneratedAttributeForVcard3()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    CalendarUrl url;
+    url.setType(CalendarUrl::CALUri);
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    url.setParameters(params);
+    addr.insertCalendarUrl(url);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected = QByteArray("BEGIN:VCARD\r\n"
+                                     "VERSION:3.0\r\n"
+                                     "EMAIL:foo@kde.org\r\n"
+                                     "N:;;;;\r\n"
+                                     "UID:testuid\r\n"
+                                     "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+void CalendarUrlTest::shouldGenerateMultiCalendarUrl()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    CalendarUrl url;
+    url.setType(CalendarUrl::CALUri);
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    url.setParameters(params);
+    addr.insertCalendarUrl(url);
+
+    url.setType(CalendarUrl::FBUrl);
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes2")));
+    params.clear();
+
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    url.setParameters(params);
+    addr.insertCalendarUrl(url);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected = QByteArray("BEGIN:VCARD\r\n"
+                                     "VERSION:4.0\r\n"
+                                     "CALURI;FOO2=bla2,blo2:https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                                     "EMAIL:foo@kde.org\r\n"
+                                     "FBURL;FOO1=bla1,blo1:https://sherlockholmes.com/calendar/sherlockholmes2\r\n"
+                                     "N:;;;;\r\n"
+                                     "UID:testuid\r\n"
+                                     "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+
+}
+
+QTEST_MAIN(CalendarUrlTest)
+
diff --git a/autotests/calendarurltest.h b/autotests/calendarurltest.h
new file mode 100644 (file)
index 0000000..418e91d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef CALENDARURLTEST_H
+#define CALENDARURLTEST_H
+
+#include <QObject>
+
+class CalendarUrlTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit CalendarUrlTest(QObject *parent = Q_NULLPTR);
+    ~CalendarUrlTest();
+
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldAssignExternal();
+    void shouldSerialized();
+    void shouldEqualCalendarUrl();
+    void shouldParseCalendarUrl();
+    void shouldGenerateVCard_data();
+    void shouldGenerateVCard();
+    void shouldGenerateVCardWithParameter();
+    void shouldNotGeneratedAttributeForVcard3();
+    void shouldGenerateMultiCalendarUrl();
+};
+
+#endif // CALENDARURLTEST_H
diff --git a/autotests/contactgrouptest.cpp b/autotests/contactgrouptest.cpp
new file mode 100644 (file)
index 0000000..89563db
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+  This file is part of the KContacts framework.
+  Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include <qtest.h>
+
+#include "kcontacts/contactgroup.h"
+#include "kcontacts/contactgrouptool.h"
+
+#include <QBuffer>
+
+using namespace KContacts;
+
+class ContactGroupTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void contactGroupContactReference();
+    void contactGroupContactGroupReference();
+    void contactGroupData();
+    void contactGroup();
+    void testGroupRoundTrip();
+    void testGroupListRoundTrip();
+};
+
+QTEST_MAIN(ContactGroupTest)
+
+void ContactGroupTest::contactGroupContactReference()
+{
+    const QLatin1String uid("MyReferenceId");
+    const QLatin1String gid("GID");
+    const QLatin1String preferredEMail("tokoe@kde.org");
+    const QLatin1String customKey("MyCustomKey");
+    const QLatin1String customValue("MyCustomValue");
+
+    // uid test
+    {
+        ContactGroup::ContactReference ref(uid);
+        QCOMPARE(ref.uid(), uid);
+    }
+
+    // uid test
+    {
+        ContactGroup::ContactReference ref;
+        ref.setUid(uid);
+        QCOMPARE(ref.uid(), uid);
+    }
+
+    // preferredEmail test
+    {
+        ContactGroup::ContactReference ref(uid);
+        ref.setPreferredEmail(preferredEMail);
+        QCOMPARE(ref.preferredEmail(), preferredEMail);
+    }
+
+    // custom test
+    {
+        ContactGroup::ContactReference ref(uid);
+        ref.insertCustom(customKey, customValue);
+        QCOMPARE(ref.custom(customKey), customValue);
+        ref.removeCustom(customKey);
+        QCOMPARE(ref.custom(customKey), QString());
+    }
+
+    // assignment test
+    {
+        ContactGroup::ContactReference ref(uid);
+        ref.setGid(gid);
+        ref.setPreferredEmail(preferredEMail);
+        ref.insertCustom(customKey, customValue);
+
+        ContactGroup::ContactReference ref2(ref);
+        QCOMPARE(ref.uid(), ref2.uid());
+        QCOMPARE(ref.gid(), ref2.gid());
+        QCOMPARE(ref.preferredEmail(), ref2.preferredEmail());
+        QCOMPARE(ref.custom(customKey), ref2.custom(customKey));
+
+        QVERIFY(ref == ref2);
+    }
+
+    // const test
+    {
+        ContactGroup::ContactReference ref(uid);
+        ref.setPreferredEmail(preferredEMail);
+        ref.insertCustom(customKey, customValue);
+
+        const ContactGroup::ContactReference constRef(ref);
+        constRef.uid();
+        constRef.preferredEmail();
+        constRef.custom(customKey);
+        constRef.gid();
+    }
+}
+
+void ContactGroupTest::contactGroupContactGroupReference()
+{
+    const QLatin1String uid("MyReferenceId");
+    const QLatin1String customKey("MyCustomKey");
+    const QLatin1String customValue("MyCustomValue");
+
+    // uid test
+    {
+        ContactGroup::ContactGroupReference ref(uid);
+        QCOMPARE(ref.uid(), uid);
+    }
+
+    // uid test
+    {
+        ContactGroup::ContactGroupReference ref;
+        ref.setUid(uid);
+        QCOMPARE(ref.uid(), uid);
+    }
+
+    // custom test
+    {
+        ContactGroup::ContactGroupReference ref(uid);
+        ref.insertCustom(customKey, customValue);
+        QCOMPARE(ref.custom(customKey), customValue);
+        ref.removeCustom(customKey);
+        QCOMPARE(ref.custom(customKey), QString());
+    }
+
+    // assignment test
+    {
+        ContactGroup::ContactGroupReference ref(uid);
+        ref.insertCustom(customKey, customValue);
+
+        ContactGroup::ContactGroupReference ref2(ref);
+        QCOMPARE(ref.uid(), ref2.uid());
+        QCOMPARE(ref.custom(customKey), ref2.custom(customKey));
+
+        QVERIFY(ref == ref2);
+    }
+
+    // const test
+    {
+        ContactGroup::ContactGroupReference ref(uid);
+        ref.insertCustom(customKey, customValue);
+
+        const ContactGroup::ContactGroupReference constRef(ref);
+        constRef.uid();
+        constRef.custom(customKey);
+    }
+}
+
+void ContactGroupTest::contactGroupData()
+{
+    const QLatin1String name("Tobias Koenig");
+    const QLatin1String email("tokoe@kde.org");
+    const QLatin1String customKey("MyCustomKey");
+    const QLatin1String customValue("MyCustomValue");
+
+    // name/email test
+    {
+        ContactGroup::Data data(name, email);
+        QCOMPARE(data.name(), name);
+        QCOMPARE(data.email(), email);
+    }
+
+    // name test
+    {
+        ContactGroup::Data data;
+        data.setName(name);
+        QCOMPARE(data.name(), name);
+    }
+
+    // email test
+    {
+        ContactGroup::Data data;
+        data.setEmail(email);
+        QCOMPARE(data.email(), email);
+    }
+
+    // custom test
+    {
+        ContactGroup::Data data(name, email);
+        data.insertCustom(customKey, customValue);
+        QCOMPARE(data.custom(customKey), customValue);
+        data.removeCustom(customKey);
+        QCOMPARE(data.custom(customKey), QString());
+    }
+
+    // assignment test
+    {
+        ContactGroup::Data data(name, email);
+        data.insertCustom(customKey, customValue);
+
+        ContactGroup::Data data2(data);
+        QCOMPARE(data.name(), data2.name());
+        QCOMPARE(data.email(), data2.email());
+        QCOMPARE(data.custom(customKey), data2.custom(customKey));
+
+        QVERIFY(data == data2);
+    }
+
+    // const test
+    {
+        ContactGroup::Data data(name, email);
+        data.insertCustom(customKey, customValue);
+
+        const ContactGroup::Data constData(data);
+        constData.name();
+        constData.email();
+        constData.custom(customKey);
+    }
+}
+
+void ContactGroupTest::contactGroup()
+{
+    const QLatin1String groupName("MyGroupName");
+    const QLatin1String groupId("MyGroupID");
+    const QLatin1String name("Tobias Koenig");
+    const QLatin1String email("tokoe@kde.org");
+    const QLatin1String uid("MyUid");
+
+    // name test
+    {
+        ContactGroup group(groupName);
+        QCOMPARE(group.name(), groupName);
+    }
+
+    // id test
+    {
+        ContactGroup group(groupName);
+        group.setId(groupId);
+        QCOMPARE(group.id(), groupId);
+    }
+
+    // contact reference test
+    {
+        ContactGroup group(groupName);
+        QCOMPARE(group.contactReferenceCount(), (unsigned int)0);
+
+        ContactGroup::ContactReference ref(uid);
+        ref.setPreferredEmail(email);
+
+        group.append(ref);
+        QCOMPARE(group.contactReferenceCount(), (unsigned int)1);
+
+        const ContactGroup::ContactReference ref2 = group.contactReference(0);
+        QCOMPARE(ref, ref2);
+
+        group.remove(ref);
+        QCOMPARE(group.contactReferenceCount(), (unsigned int)0);
+    }
+
+    // contact group reference test
+    {
+        ContactGroup group(groupName);
+        QCOMPARE(group.contactGroupReferenceCount(), (unsigned int)0);
+
+        ContactGroup::ContactGroupReference ref(uid);
+
+        group.append(ref);
+        QCOMPARE(group.contactGroupReferenceCount(), (unsigned int)1);
+
+        const ContactGroup::ContactGroupReference ref2 = group.contactGroupReference(0);
+        QCOMPARE(ref, ref2);
+
+        group.remove(ref);
+        QCOMPARE(group.contactGroupReferenceCount(), (unsigned int)0);
+    }
+
+    // data test
+    {
+        ContactGroup group(groupName);
+        QCOMPARE(group.dataCount(), (unsigned int)0);
+
+        ContactGroup::Data data(name, email);
+
+        group.append(data);
+        QCOMPARE(group.dataCount(), (unsigned int)1);
+
+        const ContactGroup::Data data2 = group.data(0);
+        QCOMPARE(data, data2);
+
+        group.remove(data);
+        QCOMPARE(group.dataCount(), (unsigned int)0);
+    }
+
+    // mimetype test
+    {
+        ContactGroup group(groupName);
+        QCOMPARE(group.mimeType(), QLatin1String("application/x-vnd.kde.contactgroup"));
+    }
+}
+
+void ContactGroupTest::testGroupRoundTrip()
+{
+    // TODO should also test empty group
+
+    ContactGroup group(QLatin1String("TestGroup"));
+    group.append(ContactGroup::ContactReference(QLatin1String("Xggdjetw")));
+    ContactGroup::ContactReference gidReference;
+    gidReference.setGid(QLatin1String("gid"));
+    group.append(gidReference);
+    group.append(ContactGroup::ContactGroupReference(QLatin1String("aaXggdjetw")));
+    group.append(ContactGroup::Data(QLatin1String("Tobias Koenig"),
+                                    QLatin1String("tokoe@kde.org")));
+    group.append(ContactGroup::Data(QLatin1String("Kevin Krammer"),
+                                    QLatin1String("kevin.krammer@gmx.at")));
+
+    QBuffer buffer;
+    buffer.open(QIODevice::WriteOnly);
+
+    QString errorMessage;
+    bool result = ContactGroupTool::convertToXml(group, &buffer, &errorMessage);
+
+    QVERIFY(result);
+    QVERIFY(errorMessage.isEmpty());
+    buffer.close();
+    QVERIFY(buffer.size() > 0);
+    buffer.open(QIODevice::ReadOnly);
+
+    ContactGroup group2;
+    result = ContactGroupTool::convertFromXml(&buffer, group2, &errorMessage);
+    QVERIFY(result);
+    QVERIFY(errorMessage.isEmpty());
+    QCOMPARE(group, group2);
+}
+
+void ContactGroupTest::testGroupListRoundTrip()
+{
+    // TODO should also test empty list
+
+    ContactGroup::List list;
+
+    ContactGroup group1(QLatin1String("TestGroup1"));
+    group1.append(ContactGroup::ContactReference(QLatin1String("Xggdjetw")));
+    group1.append(ContactGroup::Data(QLatin1String("Tobias Koenig"),
+                                     QLatin1String("tokoe@kde.org")));
+    group1.append(ContactGroup::Data(QLatin1String("Kevin Krammer"),
+                                     QLatin1String("kevin.krammer@gmx.at")));
+
+    list.append(group1);
+
+    ContactGroup group2(QLatin1String("TestGroup2"));
+    group2.append(ContactGroup::ContactReference(QLatin1String("Xggdjetw")));
+    group2.append(ContactGroup::Data(QLatin1String("Tobias Koenig"),
+                                     QLatin1String("tokoe@kde.org")));
+    group2.append(ContactGroup::Data(QLatin1String("Kevin Krammer"),
+                                     QLatin1String("kevin.krammer@gmx.at")));
+
+    list.append(group2);
+
+    QBuffer buffer;
+    buffer.open(QIODevice::WriteOnly);
+
+    QString errorMessage;
+    bool result = ContactGroupTool::convertToXml(list, &buffer, &errorMessage);
+
+    QVERIFY(result);
+    QVERIFY(errorMessage.isEmpty());
+    buffer.close();
+    QVERIFY(buffer.size() > 0);
+
+    buffer.open(QIODevice::ReadOnly);
+
+    ContactGroup::List list2;
+    result = ContactGroupTool::convertFromXml(&buffer, list2, &errorMessage);
+    QVERIFY(result);
+    QVERIFY(errorMessage.isEmpty());
+    QVERIFY(list2.size() == 2);
+    QCOMPARE(list2[0], group1);
+    QCOMPARE(list2[1], group2);
+}
+
+#include "contactgrouptest.moc"
diff --git a/autotests/data/v4_0.vcard1.vcf.ref b/autotests/data/v4_0.vcard1.vcf.ref
new file mode 100644 (file)
index 0000000..41030ef
--- /dev/null
@@ -0,0 +1,15 @@
+BEGIN:VCARD
+VERSION:4.0
+ADR;TYPE=parcel;TYPE=postal;TYPE=work:;;6544 Battleford Drive;Raleigh;NC;27
+ 613-3502;U.S.A.
+EMAIL;TYPE=PREF:Frank_Dawson@Lotus.com
+EMAIL:fdawson@earthlink.net
+FN:Frank Dawson
+N:;;;;
+ORG:Lotus Development Corporation
+TEL;TYPE=MSG;TYPE=VOICE;TYPE=WORK:+1-919-676-9515
+TEL;TYPE=FAX;TYPE=WORK:+1-919-676-9564
+UID:gDVbA6LY3t
+URL:http://home.earthlink.net/~fdawson
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard2.vcf.ref b/autotests/data/v4_0.vcard2.vcf.ref
new file mode 100644 (file)
index 0000000..446da45
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD
+VERSION:4.0
+ADR;TYPE=work:;;501 E. Middlefield Rd.;Mountain View;CA; 94043;U.S.A.
+EMAIL:howes@netscape.com
+FN:Tim Howes
+N:;;;;
+ORG:Netscape Communications Corp.
+TEL;TYPE=MSG;TYPE=VOICE;TYPE=WORK:+1-415-937-3419
+TEL;TYPE=FAX;TYPE=WORK:+1-415-528-4164
+UID:1UgvYejJMs
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard3.vcf.ref b/autotests/data/v4_0.vcard3.vcf.ref
new file mode 100644 (file)
index 0000000..b91a5d7
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD
+VERSION:4.0
+EMAIL:geiseri@yahoo.com
+FN:ian geiser
+N:geiser;ian;;;
+ORG:Source eXtreme
+TITLE:VP of Engineering
+UID:ebvq9Wo9rO
+X-mozilla-cpt:;0
+X-mozilla-html:FALSE
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard4.vcf.ref b/autotests/data/v4_0.vcard4.vcf.ref
new file mode 100644 (file)
index 0000000..5e628d3
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:VCARD
+VERSION:4.0
+EMAIL:hello@ximian.com
+FN:
+LABEL;TYPE=work:401 Park Drive  3 West\nBoston\, MA 02215\nUSA
+NOTE:Welcome to the Ximian Addressbook.
+ORG:Ximian\, Inc.
+TEL;TYPE=VOICE;TYPE=WORK:(617) 236-0442
+TEL;TYPE=FAX;TYPE=WORK:(617) 236-8630
+UID:pas-id-3E136F9B00000000
+URL:www.ximian.com/
+X-EVOLUTION-FILE-AS:Ximian\, Inc.
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard5.vcf.ref b/autotests/data/v4_0.vcard5.vcf.ref
new file mode 100644 (file)
index 0000000..10f6749
--- /dev/null
@@ -0,0 +1,313 @@
+BEGIN:VCARD
+VERSION:4.0
+ADR;TYPE=home;TYPE=pref:;;Buehlauer Strasse 45;Dresden;Sachsen;01327;German
+ y
+ADR;TYPE=work:;;Prager Strasse 55;Dresden;Sachsen;01066;Germany
+BDAY:1955-07-19T00:00:00Z
+CATEGORIES:Friend
+CLASS:CONFIDENTIAL
+EMAIL;TYPE=PREF:heiko.mustermann@freenet.org
+EMAIL:heiko8@tst.inf.tu-dresden.de
+FN:Mustermann\, Heiko
+GEO:19.283333;166.616669
+LOGO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVU
+ AAAOW0lEQVR4nO1df2hb1xX+OlyQwQEJXJAgBQlaiEwDsdqO2l1H47RjMe2gbjOoszG2dozG7aC
+ YMIqTjS7NYKSmbE1StsQpWxMXmsSF1WhlTeSwJlZY06gsSVXWUAkakKAGPajBDxrQ/jjnez8k27
+ EjJdyU+/0jPz29X9L57jnnO+deAxYWFhYWFhYWFu3Hbe082XT2ZF3+WgAAdHZ0AgBqTg0A4H7DT
+ 7oAgNSdKQDAg99/sK33caPB53RqFQBA5PYIAMBdkOf6yc9+ct3P852W786irWjJMs/8+0wdACpV
+ sZTS50V5vSLb1UoVQMCCvnFDx0c65f3BHw4CABJ3RL19mwYfM4Y1e/+0V5h/VbbPXzwPACiVSgA
+ A15XnikVjAIB44Dli3TIKjL+2e0XPYxliGFZshSdzZ+r8OyKGjdl8AQCQfe84AKBaFUaUrojlRL
+ vEUpx5h0eGjo9EZH+kQ7Yz9/d71+vdkAYApO+W16d+/NQNZ8ye8T11ACiV5TkcR5jufCX3XyrLc
+ 9XmxUe6bvi5XH3OaNRnSKRL9vWsywAAstNTyz6HZYhhsD+IYbjmMPDxfz6uA0BMqQcAR96eAgBM
+ vSevtTkJa6tzQnVSNn2XOLSZ03ljHHQ7MLxlSIa2kg5pOrTVHHHu/hANRDrke4t2y3cy+KgEMPs
+ P7F/0O7EMMQzXtNxLn16qA8DUu1nvvcm3DgEAFlxxbo4jFpFYK4woXix8qxhxLYxuH6sDQG5Gvq
+ NSueLtcxwdNTTASemoUTi3+HdkGWIYlrTkj2clzC1ckGRv3xv7vH2VK7QAlUDuktA0f/bG+YpL5
+ 4WpDDXv7TdPbjm472AdACbe9L+r4mdMHuW+42uTAIByuWwZciugY6kdER3zzn8iMkFt3pc93Kvy
+ d0QzvHYxI5h8Qq/BiK1YLur78nJw4mBIznj2V88ueg+HjxyrA0BEdE7EumLePp773u/e25b7jyb
+ kfAObh7z3SleELS40SVZ/u+2F0ToA7H99PHRtyxDD0GQZZ9RKKxUZ+/YdmAAAFD4peJ9hbF2dq7
+ aHGSpn12p+dJI7PQsAcDS3YcxfmZPXnrt65F40P+pd3wsgwF69x/xHedlWNkfX+PlUep0ck0omA
+ ACxO+R10w82tfRch8leAJOas82ek+cho/sfkGtnT8xYhpiMJh9CiytVZayjYIgO37KCmWgrOPy3
+ w3UAKKpsf/zd496+wkVhpOu5Lje0XS6X9X5lbJ49KxZIJqSSEu+Xrsj9V1Xw5OcBIPJBDoCfPw0
+ 9IVn0sanpOgA8NXR9JYBILO7/rd8n/RV9SM1tPg6wDDEOTQxhFEKLYjbuBljBX9tjzyox/Q+xQO
+ crOf7I20cAAIVz+cCnVNJu8Alo3Nb43nEjoSfa8dIOAMDs6Zy+3wcAyM3kvCtUyZ6qXLd0WZj6z
+ E+fAeAX4FZbYo5Fmt9zXfeanwEsQ4xDE0Nqbi203ZhztANVZUb2PdF+imqZLgLX0OvSd7lLbPO+
+ OFan1B8wGotEE3pCYfpWtX4A2L1rZ+gcHN+Pa8EtEpX3mR9tGlieKSf/dVJ9Ysl7j/fhMYTfuDa
+ ANMIyxDA0Z+r6Qy7MC1Mi3lge1PijTYetBNNTkjUfOjQJAMi+LzE6S7nJtX50krknE74hZQZbij
+ o19U7eKcfQAKuaP7lam+hRdZXl19kZX7Xe+PBG2acM7YzE9Bpi1UfeFN9G7Y6+77EfLR59UdGtV
+ X2G8D7IEPrf7PtZq2XdCmhiCBsUYnGxLJfxP5rzkI0Pb6wDwMypmWXHVupOk0eFEfQZIy+MAQB6
+ 79OGhnSvd0yk4c5o4c7Xamlrwj7Nqck9uVf5eblGVnONqIY1W38+4h3DyJH1i6j6jFi3+B1GaPm
+ 8RGFsa9qrqu7zI6KfHXtHmM8KKq8ZfNZg/rMcLEMMQ5NlH/zLwVDz28SbomVRUxI0RDZJZrqici
+ biYmHFz8Q6cmox6bR8bsfOVwD44ylzH47dgK9lTf5dqpOzZ0V1Hnh0AIDfXMdmO7Zx0h8MaO2aT
+ JtQf7D16Se9a9CS0+uEofQVmV7ZjqhPmXhrIvQcnV2doeemv6UfIysAeNoVg9TMfdLqZH3ILYIl
+ x36Oi/v+Knp+/mwhsFfHcR0X2W3Su0Gy4ZrG3tU5GaOHtwhzXv3jqwB8ZgFiaezamFVWAMDO30u
+ OQAsjfvFL8QHpdFLOoPE8FQXG99UvxecMbhameOrCnB8tbh0WtjA3IZuYl6TXC1PoY/b9eRwAUL
+ hYDH3emXdD20EwX4p3SzS4VKWQsAwxDPYHMQxLlnDpJNlbSyEO8BvjvDKrJnZFlcxJ+biGkRT6Y
+ nf45VMAqHypXfMa0uY+8JM2Fpycr+QzfQ+JM0+nhPoMBHit3FFxuHT6FB8rGsYnusWZVsoBAVOH
+ QzrlkoqlcW1qSyQkOMmsE+c9sk2Gy+d//Xzo+Ago40RD3wsARKMrG6oIyxDDsCRDOAtodLsU44M
+ d3QtXKcnzLGELoXN7cos4y3jcl0QAYOFrOZ4ySLGooWqgOYBs4XVp+Qw9i2XZX1GyZtIJvQdhRi
+ Il23S4ntgYSNB67xcWTs2ouDmn97FRrlX6MlzUSt8jo8XQlq0AgMm3J8Ln1OfmnBBg9U2DliGGY
+ UmGEON7pE1lVNtWAGD2IwlPK1fF6jhziGMxE8eR54QhnWtUatY5hpS5ixfCPmexsDHRLedOeDK6
+ YOARsdLxabHqVJIiozbvqQzvsZnvJ33r7dkgfuXQBWHC6NMSIrvqZ+grOTOsX+evPLlZRMlCXsv
+ GGsZn1osgOt7Q2rMaWIYYhmsyhAj+6tue2ybNZ59ruVfl5k614igL+90NvoNz9LT5jkJgKiVWS5
+ 8CAOVSGYAv9FU0eezXaItsHHkkCQBI360RnDKBzODx7iKNGcPa1FCZk3JArZzT53FC5yB4DxGN8
+ IY04d3+m+23AcDMqZmma6wWliGGYcUMCWL/G+HJJsNbhkPtPBxrGwv5pcufAgDcrzlPXayX430w
+ D2GMTx/CQlkqqUzokGO8MpZGUwtagqbASTAScuZ8ATOVlGa7wT6Jnnb/QacTaO4CFR0JFsXiiXj
+ oGrte3lUHgJ2/29ly46BliGG4LoY0YvLoZMgyjr9zXCKyTrEozuemsEfRjtEZfUq+4MvWjFxojd
+ ympTNqamq+iKjP8LJlln6VGQGxkj4hpS2lbJYeXC++xZuQpOxjO2hCi3f0lSxVtAOWIYahLQxpB
+ KXmijJjQed1R9nbptbNAhbbgRBoQYomxAobW0M5jvuWrpGQRj7MyFlsYg6RSDCS863ZnZf3EmuF
+ VcNPDMv9/FNKsfsPHAoxf/RFmbpWLLBtVa5JNvY90FcHWpueYRliGNrKkC/+90Ud8EucLjR7dtQ
+ 3qALLaCz7vsT9nO6V7vVXcqBi6jYUqHB7wyvCDWccz6kGpFU1ZqNCENWaKAWOMjZzn8RsbOTDgU
+ Ohz690vZJWYBliGNr6i1/6r0zM9LQqVXGZI3yqeQjfZ4ae2SCWyaY3QbhldOB7kqHHEuGaSiNe+
+ a3UXlh2zeiaKYOPS7nW19uA8dekPB3TXGf0xVEAgXKv1oCok+VOCcs2DbY2oWc5WIYYhrb4kOms
+ tFgWL0tUlf9QNB0ygcwoXw5XzQY3D9YBIK7M4MQZwM87HM1dSmzP1KgrFgv7joUFNjlwqSF5efV
+ 1YUHurFj32PYx75gdL+uUhbyuf6UZeq9WFznJv6KqL33fjYRliGFoiSGc0FLUxQW8VsolmsAa4U
+ VCSRnng1PlqBPFNO/gKnVs76RP6WRuo35q6HHJsqkKsPZOto7v2e1dY+QF8RkDfaqIdZCVmsOo+
+ swIjbX/GwnLEMPQEkPatZoomdE47QsAoHX6aDRcm+B4X/K6YbT+oYxg/ZttopzKxsmhgF8zrzR0
+ 0bDjhp/l8iL508s3lbcDliGGwf4ghsGIFXWONaxHAvgrDMVUhmGLEfuA4xoiT70rgQQdrteBr6X
+ bktcoF94G/GY6hrO5U9pipAEAz1mttmfFipXAMsQw3BD5fbVwuJbUGl868UJMXdNEa0eIdocLUJ
+ wH8souSfI8eV7DYcr2lPqDgUNlPtzKSqawIMXQ+3rn418PLEMMgxE+hNirCxkDQK82SrgqBqY5K
+ 1eZUanK+94ahrpaUfGCyCCDm0VMpDBImb4SaHKgD/FWFVVmMGFl6Tl3Qgpok0eXXwS5HbAMMQxG
+ MSSIsZekXNp7v8zKymzo0T26WpHO42MjNxsmGHWx6a6/rz/0uWDBi+coFJRdnDHLiI4rTqxQCmo
+ HLEMMgxFR1mJgMOQ1YHO7S14b1zQhQ5iHsLXoiM6g9SbTICDPdIRbiLw5kyqpNK72djNgGWIYjG
+ VIPB5e+cBrauvSdh5tGWKbEHMJFrn6HxDf0bNOfE+Na7cEWaFPT+m+oNMjip9J/pE90Xrz9GphG
+ WIYjGUIW/zZyMysOcrGuTjXVAwXjbgST0WjKbZ70nXkC77VFy9r/jEXZteC077W0NXCMsQwGJuH
+ NGJsp+QlfZqXsHWHKi4n1xS0YZvToWc+lPLr+U+k2BSqgV31MnNjvgfLEMNgjGWsFFytiM3WcZW
+ BOUWgoj6kqv/Ii00RXBm7GljViBFXwaD/d2IZYhiMjbKWgreUhlb3OAFz4CHZz6aGWHd4hTnHFc
+ WWjeBAeJKpKbAMMQy3HEO45Mfecfl3qNSyuB5iRtt/XP43hK5w3nK+4rcBNa7FZQIsQwyDMdFFq
+ xh9UZb+6HtIlr1wF8SHFM6JPsVGueDiMzej8W21sAwxDPYHMQy3nFNfCmltrKuWRDrhv0HlUMWV
+ 8Rrn1JsGyxDDYLS1XA927ZZWooL+MzC29sycuHmNCq3AMsQwfGt8CFHISwEqskbDWwPlkeVgGWJ
+ xczH20lidTXe3AixDLCwsLCwsviX4P68/8cprSSqmAAAAAElFTkSuQmCC
+N:Mustermann;Heiko;;;
+NICKNAME:clown
+NOTE:I'm a friendly note text\,\nplease copy me to your address book...
+ORG:KDE
+PHOTO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pV
+ UAAAOW0lEQVR4nO1df2hb1xX+OlyQwQEJXJAgBQlaiEwDsdqO2l1H47RjMe2gbjOoszG2dozG7a
+ CYMIqTjS7NYKSmbE1StsQpWxMXmsSF1WhlTeSwJlZY06gsSVXWUAkakKAGPajBDxrQ/jjnez8k2
+ 7EjJdyU+/0jPz29X9L57jnnO+deAxYWFhYWFhYWFu3Hbe082XT2ZF3+WgAAdHZ0AgBqTg0A4H7D
+ T7oAgNSdKQDAg99/sK33caPB53RqFQBA5PYIAMBdkOf6yc9+ct3P852W786irWjJMs/8+0wdACp
+ VsZTS50V5vSLb1UoVQMCCvnFDx0c65f3BHw4CABJ3RL19mwYfM4Y1e/+0V5h/VbbPXzwPACiVSg
+ AA15XnikVjAIB44Dli3TIKjL+2e0XPYxliGFZshSdzZ+r8OyKGjdl8AQCQfe84AKBaFUaUrojlR
+ LvEUpx5h0eGjo9EZH+kQ7Yz9/d71+vdkAYApO+W16d+/NQNZ8ye8T11ACiV5TkcR5jufCX3XyrL
+ c9XmxUe6bvi5XH3OaNRnSKRL9vWsywAAstNTyz6HZYhhsD+IYbjmMPDxfz6uA0BMqQcAR96eAgB
+ MvSevtTkJa6tzQnVSNn2XOLSZ03ljHHQ7MLxlSIa2kg5pOrTVHHHu/hANRDrke4t2y3cy+KgEMP
+ sP7F/0O7EMMQzXtNxLn16qA8DUu1nvvcm3DgEAFlxxbo4jFpFYK4woXix8qxhxLYxuH6sDQG5Gv
+ qNSueLtcxwdNTTASemoUTi3+HdkGWIYlrTkj2clzC1ckGRv3xv7vH2VK7QAlUDuktA0f/bG+YpL
+ 54WpDDXv7TdPbjm472AdACbe9L+r4mdMHuW+42uTAIByuWwZciugY6kdER3zzn8iMkFt3pc93Kv
+ yd0QzvHYxI5h8Qq/BiK1YLur78nJw4mBIznj2V88ueg+HjxyrA0BEdE7EumLePp773u/e25b7jy
+ bkfAObh7z3SleELS40SVZ/u+2F0ToA7H99PHRtyxDD0GQZZ9RKKxUZ+/YdmAAAFD4peJ9hbF2dq
+ 7aHGSpn12p+dJI7PQsAcDS3YcxfmZPXnrt65F40P+pd3wsgwF69x/xHedlWNkfX+PlUep0ck0om
+ AACxO+R10w82tfRch8leAJOas82ek+cho/sfkGtnT8xYhpiMJh9CiytVZayjYIgO37KCmWgrOPy
+ 3w3UAKKpsf/zd496+wkVhpOu5Lje0XS6X9X5lbJ49KxZIJqSSEu+Xrsj9V1Xw5OcBIPJBDoCfPw
+ 09IVn0sanpOgA8NXR9JYBILO7/rd8n/RV9SM1tPg6wDDEOTQxhFEKLYjbuBljBX9tjzyox/Q+xQ
+ OcrOf7I20cAAIVz+cCnVNJu8Alo3Nb43nEjoSfa8dIOAMDs6Zy+3wcAyM3kvCtUyZ6qXLd0WZj6
+ zE+fAeAX4FZbYo5Fmt9zXfeanwEsQ4xDE0Nqbi203ZhztANVZUb2PdF+imqZLgLX0OvSd7lLbPO
+ +OFan1B8wGotEE3pCYfpWtX4A2L1rZ+gcHN+Pa8EtEpX3mR9tGlieKSf/dVJ9Ysl7j/fhMYTfuD
+ aANMIyxDA0Z+r6Qy7MC1Mi3lge1PijTYetBNNTkjUfOjQJAMi+LzE6S7nJtX50krknE74hZQZbi
+ jo19U7eKcfQAKuaP7lam+hRdZXl19kZX7Xe+PBG2acM7YzE9Bpi1UfeFN9G7Y6+77EfLR59UdGt
+ VX2G8D7IEPrf7PtZq2XdCmhiCBsUYnGxLJfxP5rzkI0Pb6wDwMypmWXHVupOk0eFEfQZIy+MAQB
+ 679OGhnSvd0yk4c5o4c7Xamlrwj7Nqck9uVf5eblGVnONqIY1W38+4h3DyJH1i6j6jFi3+B1GaP
+ m8RGFsa9qrqu7zI6KfHXtHmM8KKq8ZfNZg/rMcLEMMQ5NlH/zLwVDz28SbomVRUxI0RDZJZrqic
+ ibiYmHFz8Q6cmox6bR8bsfOVwD44ylzH47dgK9lTf5dqpOzZ0V1Hnh0AIDfXMdmO7Zx0h8MaO2a
+ TJtQf7D16Se9a9CS0+uEofQVmV7ZjqhPmXhrIvQcnV2doeemv6UfIysAeNoVg9TMfdLqZH3ILYI
+ lx36Oi/v+Knp+/mwhsFfHcR0X2W3Su0Gy4ZrG3tU5GaOHtwhzXv3jqwB8ZgFiaezamFVWAMDO30
+ uOQAsjfvFL8QHpdFLOoPE8FQXG99UvxecMbhameOrCnB8tbh0WtjA3IZuYl6TXC1PoY/b9eRwAU
+ LhYDH3emXdD20EwX4p3SzS4VKWQsAwxDPYHMQxLlnDpJNlbSyEO8BvjvDKrJnZFlcxJ+biGkRT6
+ Ynf45VMAqHypXfMa0uY+8JM2Fpycr+QzfQ+JM0+nhPoMBHit3FFxuHT6FB8rGsYnusWZVsoBAVO
+ HQzrlkoqlcW1qSyQkOMmsE+c9sk2Gy+d//Xzo+Ago40RD3wsARKMrG6oIyxDDsCRDOAtodLsU44
+ Md3QtXKcnzLGELoXN7cos4y3jcl0QAYOFrOZ4ySLGooWqgOYBs4XVp+Qw9i2XZX1GyZtIJvQdhR
+ iIl23S4ntgYSNB67xcWTs2ouDmn97FRrlX6MlzUSt8jo8XQlq0AgMm3J8Ln1OfmnBBg9U2DliGG
+ YUmGEON7pE1lVNtWAGD2IwlPK1fF6jhziGMxE8eR54QhnWtUatY5hpS5ixfCPmexsDHRLedOeDK
+ 6YOARsdLxabHqVJIiozbvqQzvsZnvJ33r7dkgfuXQBWHC6NMSIrvqZ+grOTOsX+evPLlZRMlCXs
+ vGGsZn1osgOt7Q2rMaWIYYhmsyhAj+6tue2ybNZ59ruVfl5k614igL+90NvoNz9LT5jkJgKiVWS
+ 58CAOVSGYAv9FU0eezXaItsHHkkCQBI360RnDKBzODx7iKNGcPa1FCZk3JArZzT53FC5yB4DxGN
+ 8IY04d3+m+23AcDMqZmma6wWliGGYcUMCWL/G+HJJsNbhkPtPBxrGwv5pcufAgDcrzlPXayX430
+ wD2GMTx/CQlkqqUzokGO8MpZGUwtagqbASTAScuZ8ATOVlGa7wT6Jnnb/QacTaO4CFR0JFsXiiX
+ joGrte3lUHgJ2/29ly46BliGG4LoY0YvLoZMgyjr9zXCKyTrEozuemsEfRjtEZfUq+4MvWjFxoj
+ dympTNqamq+iKjP8LJlln6VGQGxkj4hpS2lbJYeXC++xZuQpOxjO2hCi3f0lSxVtAOWIYahLQxp
+ BKXmijJjQed1R9nbptbNAhbbgRBoQYomxAobW0M5jvuWrpGQRj7MyFlsYg6RSDCS863ZnZf3Emu
+ FVcNPDMv9/FNKsfsPHAoxf/RFmbpWLLBtVa5JNvY90FcHWpueYRliGNrKkC/+90Ud8EucLjR7dt
+ Q3qALLaCz7vsT9nO6V7vVXcqBi6jYUqHB7wyvCDWccz6kGpFU1ZqNCENWaKAWOMjZzn8RsbOTDg
+ UOhz690vZJWYBliGNr6i1/6r0zM9LQqVXGZI3yqeQjfZ4ae2SCWyaY3QbhldOB7kqHHEuGaSiNe
+ +a3UXlh2zeiaKYOPS7nW19uA8dekPB3TXGf0xVEAgXKv1oCok+VOCcs2DbY2oWc5WIYYhrb4kOm
+ stFgWL0tUlf9QNB0ygcwoXw5XzQY3D9YBIK7M4MQZwM87HM1dSmzP1KgrFgv7joUFNjlwqSF5ef
+ V1YUHurFj32PYx75gdL+uUhbyuf6UZeq9WFznJv6KqL33fjYRliGFoiSGc0FLUxQW8VsolmsAa4
+ UVCSRnng1PlqBPFNO/gKnVs76RP6WRuo35q6HHJsqkKsPZOto7v2e1dY+QF8RkDfaqIdZCVmsOo
+ +swIjbX/GwnLEMPQEkPatZoomdE47QsAoHX6aDRcm+B4X/K6YbT+oYxg/ZttopzKxsmhgF8zrzR
+ 00bDjhp/l8iL508s3lbcDliGGwf4ghsGIFXWONaxHAvgrDMVUhmGLEfuA4xoiT70rgQQdrteBr6
+ XbktcoF94G/GY6hrO5U9pipAEAz1mttmfFipXAMsQw3BD5fbVwuJbUGl868UJMXdNEa0eIdocLU
+ JwH8souSfI8eV7DYcr2lPqDgUNlPtzKSqawIMXQ+3rn418PLEMMgxE+hNirCxkDQK82SrgqBqY5
+ K1eZUanK+94ahrpaUfGCyCCDm0VMpDBImb4SaHKgD/FWFVVmMGFl6Tl3Qgpok0eXXwS5HbAMMQx
+ GMSSIsZekXNp7v8zKymzo0T26WpHO42MjNxsmGHWx6a6/rz/0uWDBi+coFJRdnDHLiI4rTqxQCm
+ oHLEMMgxFR1mJgMOQ1YHO7S14b1zQhQ5iHsLXoiM6g9SbTICDPdIRbiLw5kyqpNK72djNgGWIYj
+ GVIPB5e+cBrauvSdh5tGWKbEHMJFrn6HxDf0bNOfE+Na7cEWaFPT+m+oNMjip9J/pE90Xrz9Gph
+ GWIYjGUIW/zZyMysOcrGuTjXVAwXjbgST0WjKbZ70nXkC77VFy9r/jEXZteC077W0NXCMsQwGJu
+ HNGJsp+QlfZqXsHWHKi4n1xS0YZvToWc+lPLr+U+k2BSqgV31MnNjvgfLEMNgjGWsFFytiM3WcZ
+ WBOUWgoj6kqv/Ii00RXBm7GljViBFXwaD/d2IZYhiMjbKWgreUhlb3OAFz4CHZz6aGWHd4hTnHF
+ cWWjeBAeJKpKbAMMQy3HEO45Mfecfl3qNSyuB5iRtt/XP43hK5w3nK+4rcBNa7FZQIsQwyDMdFF
+ qxh9UZb+6HtIlr1wF8SHFM6JPsVGueDiMzej8W21sAwxDPYHMQy3nFNfCmltrKuWRDrhv0HlUMW
+ V8Rrn1JsGyxDDYLS1XA927ZZWooL+MzC29sycuHmNCq3AMsQwfGt8CFHISwEqskbDWwPlkeVgGW
+ JxczH20lidTXe3AixDLCwsLCwsviX4P68/8cprSSqmAAAAAElFTkSuQmCC
+ROLE:Developer
+SOUND;ENCODING=b:UklGRiQgAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAgAA
+ B/f39/f39/f39/f39/f4B/gH+Af4CAgIGBgYGBgYGBgYGBgYGBgYGBgIGAgICBgYGBgYGBgYCAg
+ YGAgICAgICAgIGBgICAgYCAgICBgICAgICAgICAgIGAgICAgYCAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAf4CAgICAgICAgICAgICAgIB/f4CAgICAgICAgIB/f4B/f39/f39/f3+AgICAf4CAf3
+ 9/gIB/f4CAgH9/gICAgH+AgICAgICAgICAf4CAgIB/gICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgIB/gICAf39/f39/f3+Af4CAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgH9/f39/f39/f39/f39/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgYCAgICB
+ gICAgYGBgICAgICAgICAgIGAgICBgICBgYGBgYGBgYGAgYGBgYCBgYGBgYGBgYGBgYGBgYGBgYG
+ BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgICAgYGBgIGBgYGAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYGAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgH+AgICAgICAgICAgICAgICAgICAf4CAgIB/gH+Af4B/f39/f39/f
+ 39/f39/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgYG
+ BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgY
+ GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGAgIGBgYCAgICBgYCAgYGBgYCAgICBg
+ YCAgICAgICAgICAgICAgIGAgICAgICAgICAgICAgYGBgYGAgICAgIGBgYCAgICBgYGAgICAgIGA
+ gICAgICAgIGBgYCBgYGAgYGBgYGBgoGBgYGCgoGAgICBgYGBgYCBgYKCgYGBgYGBgYGAgYGBgH9
+ /gIB/f3+AgIB/gH9/gICAf4CBgYB/f35+gIB/f4CBgX9/gYCAgYKCgYCAgYOBgICAgoGAf4GCgn
+ 9/gIB/gICBgH5/f4GAfX+BgYB+fn9/fn+Afn6Af39+f35+fn9+fn6AgH9/f4CAgYCAgYKCgH9/g
+ IKBf3+AgYJ/f4GCg4F/gIGCgoOBfoCDgYF/fH+BgYGAfn6Af4CCgn58gIN/fX+AgICBgYGAgYCB
+ goOCgICBgYB/fXyBhIGAgH5+f4GBgX9/f35+gIKCgXt9goN/f318f4GAf4B/fHt9fn5+fXx+fn5
+ /gH9+fHx/gHx9f4CAfn18gIB9e4GBfn5+foCBfHyAgoGAfX6CgYCBgH+DgHt9gYN/fX2BhYB7en
+ 6AgH98foKCgH18foCAfoCAf4CAf31+fn+AgH1+goJ/fX5/gIKAgIGAfH6CgX17foCBf3+BgoB/g
+ YOBgIB/f4KDgoJ+e3+ChIOBfn1/gX+BfX2BgH59foCCfn1/gX99fICAgoB/f39/gISCf36AgoSB
+ foCBgYGAf4CCf4CCgX5/gYKDgYB/gH+BhIOBgYB/gIOEhIF9foGCg4OBgICBgoOCgICChIOBg4O
+ CgoSDgoKAgYKBgoODhISBf4CCg4SEgoGCg4SEgH6BhIWEgX+EhYKAgoKFhIB+gIKChYKAgYOCgo
+ B+gYWFhIKAg4WEg4ODhIWFhYaGhIWGhYWGh4eHhYOCgoSFg4B/gIB+fX1+fn57e3t7fHt6fH5/f
+ 4GDhYiMkJGQj5GVmZycmZiXlZGIfXRwcnZ4d3Nva2hoamppaWxvc3R0dXl+iJCPjZShqrCzvNHi
+ rFYxOmKSrJ6UmJOAbV1ZboSMjI6JfnNjW2JweYGFg4B/e3Z0c3Btc3p9fHp4fIGBf3+Dh4qLjJG
+ YnZuWkY6MhnpwbnN6fHlya2hlZmdmaW9ycXFzd3t/g42cpquzvMzd/sRADiBZkL6xpLa1jFpANV
+ iHoJ2ioJWGbEQ2VHaOm5aHhIFuYmZzgoVxZFxfdJCSgnt/kKewr7bDsopcSV6JopyRjpOQfmNXZ
+ HmGhYB8fXtvXExKXnyKgnJsc3t8fZC0ysfK0+vHTxEeXY6yr6rAxJRSNTdbh5+fp7KjglQ6PVx3
+ ho6Ph3dmW15oa2x0hIuNkK3R18jJ0Ys8LUlxkqais8WtdlBFT2yDjpywro9mS0VVaXaBiIdyX2J
+ rcG5udYKKjaXT4dfV2Y82JkBpiaKkvdO2eE9GTGZ4hZu6u51yTUNOX2l9kZWKdl5PXG93dHeAip
+ KZut7j2tacRjJIYHCOo77XvoRdVlFYZniVuL+kfl9ST1Rcb4mTjoNxW1pqdXd5fH6CiKHN4d7k2
+ 4E4N05Yaoqo1eO0emVbTU5ZdKDCuZV9b1tMSVlyiYp8dHl7cmpqdYCCgIqixNTR2OqnRTlPXFt3
+ ksbnw4lzcFdKS2KMtLWZiIZwV0lPZXZ1b3uNinZqbXJ3dX2XxOPg4/zDUDRCTUhmiMXx1p+If1g
+ +O1J7pK+yspN3W0pGVGdugYmCen54b21rb3V9i6vZ7vD92WpAS0gzSny04tq3qp5qPDhHX3uVrs
+ KukXVhT0hRYnF/gnuCh31ubG9zdHeMs+Dr8P7KalFVOylJeq7QzMPIqmtIRUZOY4KovqicknhZS
+ 1BWXWh6gIeLiH91b21vc4GcyuXt/umTZGhGIjJfh6y+yt7LkGlfTD1GYYOipKKwnXhkXldVUFhs
+ dYOMjYWBem9ucoCWvNTm9+2weHdZMSpQboujvtnUrY6BZEdDUmV4jZypppeFd2dcWVFWWWh8hYS
+ Ii393c3Z7iJ3A1ePp3aGDfVYyOVZkfJe5zsavpJNsU1BTVmR9kpmcn5uGdGxoWU5VVGZ0fIKLh4
+ F/d3Z6g4ufuc3e3depjoViPz9VWm2FqLu8tLGifmphWlJZbH+Lj5+elod+c19WU0xUZW11gISEg
+ nx6e3h6g4qWp7zM3NrSpZGIYkRFVVVlfZ6vsbW3qIl7b2BUWGVwe4ybn52TkIRuX2BVRVFjZmp7
+ hIF9fX12cnZ6eX6LlZyousfP0c++jot6X0ZRXF9sgaCnqa6yn4eAdGZZYGlvd4mcnqCWnY91aWN
+ cSUtYYmJxf399f351cXFzcHF4foOIlZ+hp7CytLm8uLKNg4RtYF5oanN8j5qZnqGaioV9dGpqb3
+ J1f42Rk5iZjIKAcmFcW1RXYGhwdXt+e3Z2c25tcXJxd3yChY2Tl5udn6Gho6ioq6uonoaHbWNzZ
+ GdxgoGPlZqdj5CJgHFzcWxwdH1/hYqOioSFfnFpaWBdX2Jqb3N5fHh4eHNxcXFxc3d7foOJjI6Q
+ lZOSlZOTl5eYnJucnpyZkYiEfnd1dnZ5fYKHio6QkIyHg394dHRzc3Z5fHt2enJrcG5obG9sbW9
+ ucHJzdnh3eXp5eXt9foCDhoeJjI6OkJGSk5aYmZudn6Gjn5qLh4J3cnJ0dHR5g4GEi42Ih4aBfH
+ h4dXR1eHl4dXhuaW5rZ2ltaWttbW5vcnR1dHZ2dXV2d3d7fn+BhIeIiYuOjpCTlJWZnJueoKarr
+ ZuJhntvaW1ucnh+hIWJi4qFg4F7d3V1c3R3fH1/gHlvbXJnXmdpYmdtamtxcW9vbm9tamxtbG5z
+ dXZ5fHx7fX9+fH6AgoWHioyNj5CRlZeYmp2TiYyGenp9eXh9fX19foB/fH1/e3p8e3l5e3x7e3x
+ 6d3FxcnBwcG9vb29vb29wb25tbWxsbW5vb3Fyc3N1dXV1dnd4ent9fn+BgoGAgoWEhoqJiYyLi4
+ yPioaLiYSEh4WDhIOCgoKAgX58fXx5eXp4eHl4dXZ1cnN0dHR1cnJycXBxcnJycW9ubGxsbW1ub
+ m5ub29vcXNzdHZ2dXh6enp+fn5/gYCBhIKFiYeGiomJi4qKjIuLjIyLiomKh4aKioeIiIWDgYB9
+ fHx7e3l4eHh2dnd3d3l7enp6enp5ent7e3x7enl6e3t7fHx7e3p8fX5/gIKDg4GCg4KCg4OEhoe
+ Hh4iJiomLjIqLi4uLi46Pjo2OjouJiomHh4aDgH9/fXp5eXh3d3h4eHh4d3V2d3Z0dnd0dXd3d3
+ p7fH59fX19fH5+fn+Af4CAf3+Af359f4KDgoSIiIeJjI6PkJOUk5SYmZiYmZqZmJqamJiZmJaWl
+ ZSTkZCQj46Ni4qHh4iFgoKBfn9+fn+AgYCAgYOCgoSEg4SFhIODg4OCgICBf35/gICAgYKCgYGD
+ hIOBgYOCgYGAgIGBgIGCgoKBgoSEg4KCgH9/f318fHt6e3t7enl5eHh5fH18foCAf4GDg4OFhoa
+ GhoeJh4aJioiIiYiIhoWGh4aGh4eHiImHh4mIiIeHh4iIiIqJioqJh4mKh4eJiYiHh4eHhoWGhY
+ OEhYWEhYWDg4SFhYWEg4SDgYKCgH+AgICBgoOCgYGBgYKEhoaGhoiIh4eJiYmKioqKiomKioiJi
+ oqJiYmJiIiIiYiHh4eHh4eGhYWFhISDgoKCgoKCgYGAf31+fXx9fn58e3x8fHx9fX19fX19fX19
+ fHx9fn5/gICBgYGCgoKCg4OCgoODgoKDg4OCgoKCgoGBgYGBgYGCgYCBgIGCgoKDg4KCg4OEhIS
+ FhYWFhYWFhYWFhoWGhoWEhISDg4ODg4ODg4ODg4SDgoKDg4OCg4SDgoKDgoGBgoGAgIB/f35+fn
+ 9+fn9/f35+fn5/gICAgIGBgYKCg4ODg4SEg4OEhIODhIODhIODg4ODg4OCgoKBgoKCg4OCgoODg
+ 4ODg4OEhISEhIWEhIWFhISEhISEhIWEhISEhIODhISEg4ODg4ODg4ODg4ODgoKCgoKCg4KCgoKC
+ goKCgoKCgoOCgoKCgoKBgYKCgoKCgoGBgYGAgICAgYGBgYCAf39/f39/f3+Af39/f39/f39/gIC
+ AgIB/f39/f39/gICAgIB/f39/gIB/f39/gH9/f39/f39/fn5+f39/f39/fn5+fn5/f39/f39/fn
+ 5+fn9/f39/f35+fn5+fn9/f35/f39+fn5+fn9/fn5+fn5+fn5+f39+fn5+fn19fX5+fn5+fn5+f
+ X5+fn5+fn5+fn19fn5+fn5+fX19fX59fn5+fn19fX19fX1+fX19fX19fX19fX19fX19fX19fX19
+ fX19fX19fX19fX19fX19fX19fX19fX19fXx9fX18fH19fXx8fXx9fXx8fHx8fHx8fH19fX19fXx
+ 9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX
+ 19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
+ X19fX19fX19fX19fX19fX19fX19fX19fX1+fX5+fn19fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+
+ fn5+fn5+fn5+fn5+f39/fn5/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f3
+ 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f
+ 39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/
+ f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f39/f39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3
+ 9/f39/f39/f39/f39/f39/f35+fn5+f35/f39/f39/f39/f39/f39/f39/f39/f39/f39/f35+f
+ 39+fn5/fn5+f39/fn9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/
+ f39/f39/f39/f39/f39+fn9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f4CAf39/f39/f39/gIB/f39/f39/f4B/f4CAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgIB/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgIGBgYGAgYGBgICAgIGAgICBgYGAgYGBgYGBgYGBgYGBgY
+ GBgYGBgYGBgYGBgYGBgYGAgYCAgICAgICAgICAgICAgICAgICAgICAgICAgH+AgICAf39/gH9/f
+ 3+Af39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4CAgH9/gIB/f39/f4B/f3+AgICAf4CA
+ gICAgICAgICAgICAgH9/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgIB/f39/f39/f39/f4CAf39/f39/f39/f39/f39/f39+fn
+ 9/fn5+fn5+fn5+fn5+fX19fX19fX19fX19fX19fX19fn5+fn5+fn9/f39/f4CAgICAgICBgYGBg
+ YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICAgICAgICAgICAgICAgYGAgICA
+ gICAgICAgICAgICBgYGAgICAgICAgICAgICAgICAgICAgICAgIB/gICAgH9/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAgICAf39/f39/gH+AgI
+ CAgICAgIB/f39/f39/f4B/f39/f39/f39/f39/f39/f39/f39/f39/f39/fn9/f39/f39/f39+f
+ 39/f39/f39/f39/f39/f39/f39/f4B/f4CAgICAf39/f39/gICAf39/gICAgICAgICAgICAgICA
+ gICAgIGAgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICBgYGBgYGBgYCBgYGBgYCAgIG
+ BgICAgICAgICAgICAgICAgICAgICAgICAgICBgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAf3+AgIB/f4CAf39/f39/f3+AgICAgH+AgICAf3+AgICAgICAgH9/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3
+ 9/f39/f39/gH9/gICAgICAgICAgIB/gICAgH+AgIB/f39/f39/gICAgICAgH9/f3+AgH9/f4B/f
+ 39/f39/f39/f39/f3+AgICAgICAf3+AgIB/f4CAgIB/f4CAgICAgICAgICAf39/f3+AgIB/f4CA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAf39/f39
+ /f4CAgH9/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f39/f39/gIB/f39/f39/f39/gH9/f3
+ 9/f4CAgICAgIB/gICAgICAgICAgICAgICAgICAgICAgICAgH+AgICAf4CAgICAgICAgIB/gICAf
+ 4CAgICAgH+Af4CAgH9/f39/f39/f39/gICAgICAgICAgICAf39/f39/f4CAgH9/gICAf3+AgICA
+ gICAgICAgICAgICAgICAgICAgIB/f39/gIB/f4CAgIB/f4CAgH9/gICAf39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3
+ 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAf39/gH9/f3+AgH9/f39/f39/f39/f39/f
+ 39/f39/f39/f39/f39/f39/f39/gICAf3+AgH9/f3+AgH9/f4CAgH9/gICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICBgYGBgIGBgYCAgYGAgICAgYCAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAf39/f39/f4CAgIB/f4CAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAf39/f3+Af39/f39/f39/f4B/f39/f4CAgICAgICAgICAgA==
+TEL;TYPE=HOME:0351 223344
+TEL;TYPE=WORK:0351 443322
+TEL;TYPE=CELL:0173 223344
+UID:P9HXg34Oav
+URL:http://www.mustermann.de/~heiko/
+X-KADDRESSBOOK-X-Anniversary:1982-07-07
+X-KADDRESSBOOK-X-AssistantsName:Mr. Smithers
+X-KADDRESSBOOK-X-Department:Edutainment
+X-KADDRESSBOOK-X-IMAddress:122334455
+X-KADDRESSBOOK-X-ManagersName:Mr. Burnes
+X-KADDRESSBOOK-X-Office:223 2nd stage
+X-KADDRESSBOOK-X-Profession:Clown
+X-KADDRESSBOOK-X-SpousesName:femal clown
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard6.vcf.ref b/autotests/data/v4_0.vcard6.vcf.ref
new file mode 100644 (file)
index 0000000..80622a6
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN:VCARD
+VERSION:4.0
+CLASS:PUBLIC
+EMAIL;TYPE=PREF:email1@abc.de
+EMAIL:email2@abc.de
+FN:Test User
+N:User;Test;;;
+UID:uJTkVqH5Qt
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard7.vcf.ref b/autotests/data/v4_0.vcard7.vcf.ref
new file mode 100644 (file)
index 0000000..64ff743
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN:VCARD
+VERSION:4.0
+FN:
+LABEL;TYPE=work:401 Park Drive  3 West\nBoston\, MA 02215\nUSA\nHerecomesye
+ tanotherline
+UID:pas-id-3E136F9B00000000
+END:VCARD
+
diff --git a/autotests/data/v4_0.vcard8.vcf.ref b/autotests/data/v4_0.vcard8.vcf.ref
new file mode 100644 (file)
index 0000000..55bf303
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN:VCARD
+VERSION:4.0
+FN:test
+N:test;;;;
+NOTE:line one\nline two\nline three\n
+UID:8OD165Pope
+END:VCARD
+
diff --git a/autotests/data/vcard1.vcf b/autotests/data/vcard1.vcf
new file mode 100644 (file)
index 0000000..da4613e
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:vCard
+VERSION:3.0
+UID:gDVbA6LY3t
+FN:Frank Dawson
+ORG:Lotus Development Corporation
+ADR;TYPE=WORK,POSTAL,PARCEL:;;6544 Battleford Drive
+ ;Raleigh;NC;27613-3502;U.S.A.
+TEL;TYPE=VOICE,MSG,WORK:+1-919-676-9515
+TEL;TYPE=FAX,WORK:+1-919-676-9564
+EMAIL;TYPE=INTERNET,PREF:Frank_Dawson@Lotus.com
+EMAIL;TYPE=INTERNET:fdawson@earthlink.net
+URL:http://home.earthlink.net/~fdawson
+END:vCard
+
diff --git a/autotests/data/vcard1.vcf.2_1ref b/autotests/data/vcard1.vcf.2_1ref
new file mode 100644 (file)
index 0000000..08ede3e
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+ADR;PARCEL;POSTAL;WORK:;;6544 Battleford Drive;Raleigh;NC;27613-3502;U.S.A.\r
+EMAIL;INTERNET;PREF:Frank_Dawson@Lotus.com\r
+EMAIL;INTERNET:fdawson@earthlink.net\r
+FN:Frank Dawson\r
+N:;;;;\r
+ORG:Lotus Development Corporation\r
+TEL;MSG;VOICE;WORK:+1-919-676-9515\r
+TEL;FAX;WORK:+1-919-676-9564\r
+UID:gDVbA6LY3t\r
+URL:http://home.earthlink.net/~fdawson\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard1.vcf.ref b/autotests/data/vcard1.vcf.ref
new file mode 100644 (file)
index 0000000..038cb5f
--- /dev/null
@@ -0,0 +1,15 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=parcel,postal,work:;;6544 Battleford Drive;Raleigh;NC;27613-3502;U\r
+ .S.A.\r
+EMAIL;TYPE=INTERNET,PREF:Frank_Dawson@Lotus.com\r
+EMAIL;TYPE=INTERNET:fdawson@earthlink.net\r
+FN:Frank Dawson\r
+N:;;;;\r
+ORG:Lotus Development Corporation\r
+TEL;TYPE=MSG,VOICE,WORK:+1-919-676-9515\r
+TEL;TYPE=FAX,WORK:+1-919-676-9564\r
+UID:gDVbA6LY3t\r
+URL:http://home.earthlink.net/~fdawson\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard10.vcf b/autotests/data/vcard10.vcf
new file mode 100644 (file)
index 0000000..821aea6
--- /dev/null
@@ -0,0 +1,56 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+UID:bfa5fdc5-fbfc-4ce2-8196-29ca0deb3bd3\r
+ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=44=44=44=44=44=44=44=44=44=44=44=44=44=44=44=2D=53=74=61=C3=9F=65=20=\r
+=32=0A=30=30=30=30=30=20=44=44=44=44=44=44=44=44=44=44;;;;\r
+ORG:Bike Shop\r
+Note:This one has a funny character at the end in the contact list\r
+END:VCARD\r
+BEGIN:VCARD\r
+VERSION:2.1\r
+UID:f8361311-b25c-4237-a94e-3015d8fe19de\r
+N:;Broken;;;\r
+FN:Field after Address broken\r
+ADR;HOME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=41=41=41=41=41=41=41=2D=41=C3=A4=41=41=41=41=41=20=\r
+=31=31;=55=72=74;;=30=30=30=30=30;\r
+URL:http://some-webseite.com\r
+NOTE:This one's URL field is truncated (see contact's edit form). Remove the linebreak in the VCF's address field and the URL will will be correct.\r
+END:VCARD\r
+BEGIN:VCARD\r
+VERSION:2.1\r
+UID:462269fd-607c-4284-998b-6771143b63a7\r
+N:;Broken;;;\r
+FN:Field after Address not broken\r
+ADR;HOME;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=41=41=41=41=41=41=41=2D=41=C3=A4=41=41=41=41=41=20=\r
+=31=31;=55=72=74;;=30=30=30=30=30;\r
+NOTE:this is identical to "Website broken", but with Note and URL swapped. The Note field does not get truncated as the URL field does.\r
+URL:http://some-webseite.com\r
+END:VCARD\r
+BEGIN:VCARD\r
+VERSION:2.1\r
+UID:b4fc4930-043f-446a-9a76-cf0b548368d8\r
+N:Name;Surname;;Dr. med.;\r
+FN:Dr. med. Surname Name\r
+TITLE;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=46=61=63=68=61=72=7A=74=20=66=C3=BC=72=20=41=6C=6C=67=65=6D=65=69=6E=\r
+=6D=65=64=69=7A=69=6E\r
+NOTE;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=53=70=72=65=63=68=7A=65=69=74=65=6E=3A=0A=4D=6F=20=20=20=38=E2=80=93=\r
+=31=32=2C=20=31=35=E2=80=93=31=38=0A=44=69=20=20=20=20=20=38=E2=80=93=\r
+=31=32=0A=4D=69=20=20=20=20=38=E2=80=93=31=32=2C=20=31=34=E2=80=93=31=\r
+=35=0A=44=6F=20=31=34=E2=80=93=31=39=0A=46=72=20=20=20=20=20=38=E2=80=\r
+=93=31=32\r
+END:VCARD\r
+BEGIN:VCARD\r
+VERSION:2.1\r
+UID:941b057d-e959-4644-b41f-e122b26c3807\r
+N:Doctor;Second;;;\r
+FN:Second Doctor\r
+ADR;WORK;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;;=44=44=44=44=44=44=44=44=44=44=44=73=74=72=61=C3=9F=65=20=32=36=0A=30=\r
+=30=30=30=30=20=44=44=44=44=44=44=44=44=44=44=44=44=44=44=44=44=44;;;;\r
+ORG:\r
+TITLE:Dentist\r
+NOTE;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=53=70=72=65=63=68=7A=65=69=74=65=6E=3A=0A=4D=6F=20=20=20=38=E2=80=93=\r
+=31=33=0A=44=69=20=20=20=31=30=E2=80=93=31=32=2C=20=31=33=E2=80=93=31=\r
+=38=0A=4D=69=20=20=31=30=E2=80=93=31=32=2C=20=31=33=E2=80=93=31=38=0A=\r
+=44=6F=20=20=31=33=E2=80=93=31=38=0A=46=72=20=20=20=20=20=20=38=E2=80=\r
+=93=31=32\r
+END:VCARD\r
diff --git a/autotests/data/vcard10.vcf.ref b/autotests/data/vcard10.vcf.ref
new file mode 100644 (file)
index 0000000..7a21a4b
--- /dev/null
@@ -0,0 +1,52 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=work:;;DDDDDDDDDDDDDDD-Staße 2\n00000 DDDDDDDDDD;;;;\r
+N:;;;;\r
+NOTE:This one has a funny character at the end in the contact list\r
+ORG:Bike Shop\r
+UID:bfa5fdc5-fbfc-4ce2-8196-29ca0deb3bd3\r
+END:VCARD\r
+\r
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=home:;;AAAAAAA-AäAAAAA 11;Urt;;00000;\r
+FN:Field after Address broken\r
+N:;Broken;;;\r
+NOTE:This one's URL field is truncated (see contact's edit form). Remove th\r
+ e linebreak in the VCF's address field and the URL will will be correct.\r
+UID:f8361311-b25c-4237-a94e-3015d8fe19de\r
+URL:http://some-webseite.com\r
+END:VCARD\r
+\r
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=home:;;AAAAAAA-AäAAAAA 11;Urt;;00000;\r
+FN:Field after Address not broken\r
+N:;Broken;;;\r
+NOTE:this is identical to "Website broken"\, but with Note and URL swapped.\r
+  The Note field does not get truncated as the URL field does.\r
+UID:462269fd-607c-4284-998b-6771143b63a7\r
+URL:http://some-webseite.com\r
+END:VCARD\r
+\r
+BEGIN:VCARD\r
+VERSION:3.0\r
+FN:Dr. med. Surname Name\r
+N:Name;Surname;;Dr. med.;\r
+NOTE:Sprechzeiten:\nMo   8–12\, 15–18\nDi     8–12\nMi    8–12\, 14\r
+ –15\nDo 14–19\nFr     8–12\r
+TITLE:Facharzt für Allgemeinmedizin\r
+UID:b4fc4930-043f-446a-9a76-cf0b548368d8\r
+END:VCARD\r
+\r
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=work:;;DDDDDDDDDDDstraße 26\n00000 DDDDDDDDDDDDDDDDD;;;;\r
+FN:Second Doctor\r
+N:Doctor;Second;;;\r
+NOTE:Sprechzeiten:\nMo   8–13\nDi   10–12\, 13–18\nMi  10–12\, 13\r
+ –18\nDo  13–18\nFr      8–12\r
+TITLE:Dentist\r
+UID:941b057d-e959-4644-b41f-e122b26c3807\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard11.vcf b/autotests/data/vcard11.vcf
new file mode 100644 (file)
index 0000000..904e083
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN:VCARD
+CLASS:PUBLIC
+EMAIL;TYPE=HOME;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com
+FN:Test User
+N:User;Test;;;
+UID:uJTkVqH5Qt
+VERSION:3.0
+END:VCARD
+
diff --git a/autotests/data/vcard11.vcf.ref b/autotests/data/vcard11.vcf.ref
new file mode 100644 (file)
index 0000000..2b8709a
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+CLASS:PUBLIC\r
+EMAIL;TYPE=HOME;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com\r
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com\r
+FN:Test User\r
+N:User;Test;;;\r
+UID:uJTkVqH5Qt\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard12.vcf b/autotests/data/vcard12.vcf
new file mode 100644 (file)
index 0000000..ecb2d6c
--- /dev/null
@@ -0,0 +1,15 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+UID:pas-id-54B2522600000001\r
+FN:Good Looking\r
+N:Looking;Good;;;\r
+PHOTO;TYPE=WORK;ENCODING=b:iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAA\r
+ CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wELCiUrjAUGmgAAABl0RVh0Q29tbWVudABDcm\r
+ VhdGVkIHdpdGggR0lNUFeBDhcAAAAZSURBVAjXY/zPAAVMDAwMDP//MzAwMMLFAEMrBAHo3Yr/\r
+ AAAAAElFTkSuQmCC\r
+PHOTO;TYPE=HOME;ENCODING=b:iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAA\r
+ CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wELCiQG0MFrrgAAABl0RVh0Q29tbWVudABDcm\r
+ VhdGVkIHdpdGggR0lNUFeBDhcAAAAUSURBVAjXY/j//z8DAwOEhAG4GACzagv1unIx3wAAAABJ\r
+ RU5ErkJggg==\r
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:goodlooking@example.com\r
+END:VCARD\r
diff --git a/autotests/data/vcard12.vcf.2_1ref b/autotests/data/vcard12.vcf.2_1ref
new file mode 100644 (file)
index 0000000..6560135
Binary files /dev/null and b/autotests/data/vcard12.vcf.2_1ref differ
diff --git a/autotests/data/vcard12.vcf.ref b/autotests/data/vcard12.vcf.ref
new file mode 100644 (file)
index 0000000..980372e
--- /dev/null
@@ -0,0 +1,16 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:goodlooking@example.com\r
+FN:Good Looking\r
+N:Looking;Good;;;\r
+PHOTO;ENCODING=b;TYPE=WORK:iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAA\r
+ CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wELCiUrjAUGmgAAABl0RVh0Q29tbWVudABDcmV\r
+ hdGVkIHdpdGggR0lNUFeBDhcAAAAZSURBVAjXY/zPAAVMDAwMDP//MzAwMMLFAEMrBAHo3Yr/AA\r
+ AAAElFTkSuQmCC\r
+PHOTO;ENCODING=b;TYPE=HOME:iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAA\r
+ CXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wELCiQG0MFrrgAAABl0RVh0Q29tbWVudABDcmV\r
+ hdGVkIHdpdGggR0lNUFeBDhcAAAAUSURBVAjXY/j//z8DAwOEhAG4GACzagv1unIx3wAAAABJRU\r
+ 5ErkJggg==\r
+UID:pas-id-54B2522600000001\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard13.vcf b/autotests/data/vcard13.vcf
new file mode 100644 (file)
index 0000000..5e31757
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD
+CLASS:PUBLIC
+EMAIL;TYPE=HOME;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com
+FN:Test User
+N:User;Test;;;
+UID:uJTkVqH5Qt
+URL:test1.url
+URL:test2.url
+VERSION:3.0
+END:VCARD
+
diff --git a/autotests/data/vcard13.vcf.ref b/autotests/data/vcard13.vcf.ref
new file mode 100644 (file)
index 0000000..ce0cba0
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+CLASS:PUBLIC\r
+EMAIL;TYPE=HOME;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com\r
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com\r
+FN:Test User\r
+N:User;Test;;;\r
+UID:uJTkVqH5Qt\r
+URL:test1.url\r
+URL:test2.url\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard14.vcf b/autotests/data/vcard14.vcf
new file mode 100644 (file)
index 0000000..8fe5db3
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+CLASS:PUBLIC\r
+EMAIL;TYPE=HOME,PREF;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com\r
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com\r
+FN:Test User\r
+N:User;Test;;;\r
+UID:uJTkVqH5Qt\r
+SOURCE:http://www.kde.org\r
+SOURCE:http://www.kde1.org\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard14.vcf.2_1ref b/autotests/data/vcard14.vcf.2_1ref
new file mode 100644 (file)
index 0000000..367e523
--- /dev/null
@@ -0,0 +1,11 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+EMAIL;HOME;PREF;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com\r
+EMAIL;WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com\r
+FN:Test User\r
+N:User;Test;;;\r
+SOURCE:http://www.kde.org\r
+SOURCE:http://www.kde1.org\r
+UID:uJTkVqH5Qt\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard14.vcf.ref b/autotests/data/vcard14.vcf.ref
new file mode 100644 (file)
index 0000000..2488a73
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+CLASS:PUBLIC\r
+EMAIL;TYPE=HOME,PREF;X-EVOLUTION-UI-SLOT=2:thejoe@abc.com\r
+EMAIL;TYPE=WORK;X-EVOLUTION-UI-SLOT=1:johndoe@abc.com\r
+FN:Test User\r
+N:User;Test;;;\r
+SOURCE:http://www.kde.org\r
+SOURCE:http://www.kde1.org\r
+UID:uJTkVqH5Qt\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard2.vcf b/autotests/data/vcard2.vcf
new file mode 100644 (file)
index 0000000..13cd72e
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:vCard
+VERSION:3.0
+UID:1UgvYejJMs
+FN:Tim Howes
+ORG:Netscape Communications Corp.
+ADR;TYPE=WORK:;;501 E. Middlefield Rd.;Mountain View;
+ CA; 94043;U.S.A.
+TEL;TYPE=VOICE,MSG,WORK:+1-415-937-3419
+TEL;TYPE=FAX,WORK:+1-415-528-4164
+EMAIL;TYPE=INTERNET:howes@netscape.com
+END:vCard
+
diff --git a/autotests/data/vcard2.vcf.2_1ref b/autotests/data/vcard2.vcf.2_1ref
new file mode 100644 (file)
index 0000000..0af201c
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+ADR;WORK:;;501 E. Middlefield Rd.;Mountain View;CA; 94043;U.S.A.\r
+EMAIL;INTERNET:howes@netscape.com\r
+FN:Tim Howes\r
+N:;;;;\r
+ORG:Netscape Communications Corp.\r
+TEL;MSG;VOICE;WORK:+1-415-937-3419\r
+TEL;FAX;WORK:+1-415-528-4164\r
+UID:1UgvYejJMs\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard2.vcf.ref b/autotests/data/vcard2.vcf.ref
new file mode 100644 (file)
index 0000000..0cf6003
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=work:;;501 E. Middlefield Rd.;Mountain View;CA; 94043;U.S.A.\r
+EMAIL;TYPE=INTERNET:howes@netscape.com\r
+FN:Tim Howes\r
+N:;;;;\r
+ORG:Netscape Communications Corp.\r
+TEL;TYPE=MSG,VOICE,WORK:+1-415-937-3419\r
+TEL;TYPE=FAX,WORK:+1-415-528-4164\r
+UID:1UgvYejJMs\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard3.vcf b/autotests/data/vcard3.vcf
new file mode 100644 (file)
index 0000000..579e2da
--- /dev/null
@@ -0,0 +1,12 @@
+begin:vcard
+uid:ebvq9Wo9rO
+n:geiser;ian
+x-mozilla-html:FALSE
+org:Source eXtreme
+version:2.1
+email;internet:geiseri@yahoo.com
+title:VP of Engineering
+x-mozilla-cpt:;0
+fn:ian geiser
+end:vcard
+
diff --git a/autotests/data/vcard3.vcf.2_1ref b/autotests/data/vcard3.vcf.2_1ref
new file mode 100644 (file)
index 0000000..0a81e62
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+EMAIL;INTERNET:geiseri@yahoo.com\r
+FN:ian geiser\r
+N:geiser;ian;;;\r
+ORG:Source eXtreme\r
+TITLE:VP of Engineering\r
+UID:ebvq9Wo9rO\r
+X-mozilla-cpt:;0\r
+X-mozilla-html:FALSE\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard3.vcf.ref b/autotests/data/vcard3.vcf.ref
new file mode 100644 (file)
index 0000000..0eeeec2
--- /dev/null
@@ -0,0 +1,12 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+EMAIL;TYPE=internet:geiseri@yahoo.com\r
+FN:ian geiser\r
+N:geiser;ian;;;\r
+ORG:Source eXtreme\r
+TITLE:VP of Engineering\r
+UID:ebvq9Wo9rO\r
+X-mozilla-cpt:;0\r
+X-mozilla-html:FALSE\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard4.vcf b/autotests/data/vcard4.vcf
new file mode 100644 (file)
index 0000000..7152ceb
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:VCARD
+X-EVOLUTION-FILE-AS:Ximian, Inc.
+FN
+N:
+LABEL;QUOTED-PRINTABLE;WORK:401 Park Drive  3 West=0ABoston, MA 02215=0AUSA
+TEL;WORK;VOICE:(617) 236-0442
+TEL;WORK;FAX:(617) 236-8630
+EMAIL;INTERNET:hello@ximian.com
+URL:www.ximian.com/
+ORG:Ximian, Inc.
+NOTE:Welcome to the Ximian Addressbook.
+UID:pas-id-3E136F9B00000000
+END:VCARD
+
diff --git a/autotests/data/vcard4.vcf.ref b/autotests/data/vcard4.vcf.ref
new file mode 100644 (file)
index 0000000..28e0b58
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+EMAIL;TYPE=INTERNET:hello@ximian.com\r
+LABEL;TYPE=work:401 Park Drive  3 West\nBoston\, MA 02215\nUSA\r
+N:;;;;\r
+NOTE:Welcome to the Ximian Addressbook.\r
+ORG:Ximian\, Inc.\r
+TEL;TYPE=VOICE,WORK:(617) 236-0442\r
+TEL;TYPE=FAX,WORK:(617) 236-8630\r
+UID:pas-id-3E136F9B00000000\r
+URL:www.ximian.com/\r
+X-EVOLUTION-FILE-AS:Ximian\, Inc.\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard5.vcf b/autotests/data/vcard5.vcf
new file mode 100644 (file)
index 0000000..957a1e5
--- /dev/null
@@ -0,0 +1,313 @@
+BEGIN:VCARD
+ADR;TYPE=home;TYPE=pref:;;Buehlauer Strasse 45;Dresden;Sachsen;01327;German
+ y
+ADR;TYPE=work:;;Prager Strasse 55;Dresden;Sachsen;01066;Germany
+BDAY:1955-07-19T00:00:00Z
+CATEGORIES:Friend
+CLASS:CONFIDENTIAL
+EMAIL;TYPE=PREF:heiko.mustermann@freenet.org
+EMAIL:heiko8@tst.inf.tu-dresden.de
+FN:Mustermann, Heiko
+GEO:19.283333;166.616669
+LOGO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVU
+ AAAOW0lEQVR4nO1df2hb1xX+OlyQwQEJXJAgBQlaiEwDsdqO2l1H47RjMe2gbjOoszG2dozG7aC
+ YMIqTjS7NYKSmbE1StsQpWxMXmsSF1WhlTeSwJlZY06gsSVXWUAkakKAGPajBDxrQ/jjnez8k27
+ EjJdyU+/0jPz29X9L57jnnO+deAxYWFhYWFhYWFu3Hbe082XT2ZF3+WgAAdHZ0AgBqTg0A4H7DT
+ 7oAgNSdKQDAg99/sK33caPB53RqFQBA5PYIAMBdkOf6yc9+ct3P852W786irWjJMs/8+0wdACpV
+ sZTS50V5vSLb1UoVQMCCvnFDx0c65f3BHw4CABJ3RL19mwYfM4Y1e/+0V5h/VbbPXzwPACiVSgA
+ A15XnikVjAIB44Dli3TIKjL+2e0XPYxliGFZshSdzZ+r8OyKGjdl8AQCQfe84AKBaFUaUrojlRL
+ vEUpx5h0eGjo9EZH+kQ7Yz9/d71+vdkAYApO+W16d+/NQNZ8ye8T11ACiV5TkcR5jufCX3XyrLc
+ 9XmxUe6bvi5XH3OaNRnSKRL9vWsywAAstNTyz6HZYhhsD+IYbjmMPDxfz6uA0BMqQcAR96eAgBM
+ vSevtTkJa6tzQnVSNn2XOLSZ03ljHHQ7MLxlSIa2kg5pOrTVHHHu/hANRDrke4t2y3cy+KgEMPs
+ P7F/0O7EMMQzXtNxLn16qA8DUu1nvvcm3DgEAFlxxbo4jFpFYK4woXix8qxhxLYxuH6sDQG5Gvq
+ NSueLtcxwdNTTASemoUTi3+HdkGWIYlrTkj2clzC1ckGRv3xv7vH2VK7QAlUDuktA0f/bG+YpL5
+ 4WpDDXv7TdPbjm472AdACbe9L+r4mdMHuW+42uTAIByuWwZciugY6kdER3zzn8iMkFt3pc93Kvy
+ d0QzvHYxI5h8Qq/BiK1YLur78nJw4mBIznj2V88ueg+HjxyrA0BEdE7EumLePp773u/e25b7jyb
+ kfAObh7z3SleELS40SVZ/u+2F0ToA7H99PHRtyxDD0GQZZ9RKKxUZ+/YdmAAAFD4peJ9hbF2dq7
+ aHGSpn12p+dJI7PQsAcDS3YcxfmZPXnrt65F40P+pd3wsgwF69x/xHedlWNkfX+PlUep0ck0omA
+ ACxO+R10w82tfRch8leAJOas82ek+cho/sfkGtnT8xYhpiMJh9CiytVZayjYIgO37KCmWgrOPy3
+ w3UAKKpsf/zd496+wkVhpOu5Lje0XS6X9X5lbJ49KxZIJqSSEu+Xrsj9V1Xw5OcBIPJBDoCfPw0
+ 9IVn0sanpOgA8NXR9JYBILO7/rd8n/RV9SM1tPg6wDDEOTQxhFEKLYjbuBljBX9tjzyox/Q+xQO
+ crOf7I20cAAIVz+cCnVNJu8Alo3Nb43nEjoSfa8dIOAMDs6Zy+3wcAyM3kvCtUyZ6qXLd0WZj6z
+ E+fAeAX4FZbYo5Fmt9zXfeanwEsQ4xDE0Nqbi203ZhztANVZUb2PdF+imqZLgLX0OvSd7lLbPO+
+ OFan1B8wGotEE3pCYfpWtX4A2L1rZ+gcHN+Pa8EtEpX3mR9tGlieKSf/dVJ9Ysl7j/fhMYTfuDa
+ ANMIyxDA0Z+r6Qy7MC1Mi3lge1PijTYetBNNTkjUfOjQJAMi+LzE6S7nJtX50krknE74hZQZbij
+ o19U7eKcfQAKuaP7lam+hRdZXl19kZX7Xe+PBG2acM7YzE9Bpi1UfeFN9G7Y6+77EfLR59UdGtV
+ X2G8D7IEPrf7PtZq2XdCmhiCBsUYnGxLJfxP5rzkI0Pb6wDwMypmWXHVupOk0eFEfQZIy+MAQB6
+ 79OGhnSvd0yk4c5o4c7Xamlrwj7Nqck9uVf5eblGVnONqIY1W38+4h3DyJH1i6j6jFi3+B1GaPm
+ 8RGFsa9qrqu7zI6KfHXtHmM8KKq8ZfNZg/rMcLEMMQ5NlH/zLwVDz28SbomVRUxI0RDZJZrqici
+ biYmHFz8Q6cmox6bR8bsfOVwD44ylzH47dgK9lTf5dqpOzZ0V1Hnh0AIDfXMdmO7Zx0h8MaO2aT
+ JtQf7D16Se9a9CS0+uEofQVmV7ZjqhPmXhrIvQcnV2doeemv6UfIysAeNoVg9TMfdLqZH3ILYIl
+ x36Oi/v+Knp+/mwhsFfHcR0X2W3Su0Gy4ZrG3tU5GaOHtwhzXv3jqwB8ZgFiaezamFVWAMDO30u
+ OQAsjfvFL8QHpdFLOoPE8FQXG99UvxecMbhameOrCnB8tbh0WtjA3IZuYl6TXC1PoY/b9eRwAUL
+ hYDH3emXdD20EwX4p3SzS4VKWQsAwxDPYHMQxLlnDpJNlbSyEO8BvjvDKrJnZFlcxJ+biGkRT6Y
+ nf45VMAqHypXfMa0uY+8JM2Fpycr+QzfQ+JM0+nhPoMBHit3FFxuHT6FB8rGsYnusWZVsoBAVOH
+ QzrlkoqlcW1qSyQkOMmsE+c9sk2Gy+d//Xzo+Ago40RD3wsARKMrG6oIyxDDsCRDOAtodLsU44M
+ d3QtXKcnzLGELoXN7cos4y3jcl0QAYOFrOZ4ySLGooWqgOYBs4XVp+Qw9i2XZX1GyZtIJvQdhRi
+ Il23S4ntgYSNB67xcWTs2ouDmn97FRrlX6MlzUSt8jo8XQlq0AgMm3J8Ln1OfmnBBg9U2DliGGY
+ UmGEON7pE1lVNtWAGD2IwlPK1fF6jhziGMxE8eR54QhnWtUatY5hpS5ixfCPmexsDHRLedOeDK6
+ YOARsdLxabHqVJIiozbvqQzvsZnvJ33r7dkgfuXQBWHC6NMSIrvqZ+grOTOsX+evPLlZRMlCXsv
+ GGsZn1osgOt7Q2rMaWIYYhmsyhAj+6tue2ybNZ59ruVfl5k614igL+90NvoNz9LT5jkJgKiVWS5
+ 8CAOVSGYAv9FU0eezXaItsHHkkCQBI360RnDKBzODx7iKNGcPa1FCZk3JArZzT53FC5yB4DxGN8
+ IY04d3+m+23AcDMqZmma6wWliGGYcUMCWL/G+HJJsNbhkPtPBxrGwv5pcufAgDcrzlPXayX430w
+ D2GMTx/CQlkqqUzokGO8MpZGUwtagqbASTAScuZ8ATOVlGa7wT6Jnnb/QacTaO4CFR0JFsXiiXj
+ oGrte3lUHgJ2/29ly46BliGG4LoY0YvLoZMgyjr9zXCKyTrEozuemsEfRjtEZfUq+4MvWjFxojd
+ ympTNqamq+iKjP8LJlln6VGQGxkj4hpS2lbJYeXC++xZuQpOxjO2hCi3f0lSxVtAOWIYahLQxpB
+ KXmijJjQed1R9nbptbNAhbbgRBoQYomxAobW0M5jvuWrpGQRj7MyFlsYg6RSDCS863ZnZf3EmuF
+ VcNPDMv9/FNKsfsPHAoxf/RFmbpWLLBtVa5JNvY90FcHWpueYRliGNrKkC/+90Ud8EucLjR7dtQ
+ 3qALLaCz7vsT9nO6V7vVXcqBi6jYUqHB7wyvCDWccz6kGpFU1ZqNCENWaKAWOMjZzn8RsbOTDgU
+ Ohz690vZJWYBliGNr6i1/6r0zM9LQqVXGZI3yqeQjfZ4ae2SCWyaY3QbhldOB7kqHHEuGaSiNe+
+ a3UXlh2zeiaKYOPS7nW19uA8dekPB3TXGf0xVEAgXKv1oCok+VOCcs2DbY2oWc5WIYYhrb4kOms
+ tFgWL0tUlf9QNB0ygcwoXw5XzQY3D9YBIK7M4MQZwM87HM1dSmzP1KgrFgv7joUFNjlwqSF5efV
+ 1YUHurFj32PYx75gdL+uUhbyuf6UZeq9WFznJv6KqL33fjYRliGFoiSGc0FLUxQW8VsolmsAa4U
+ VCSRnng1PlqBPFNO/gKnVs76RP6WRuo35q6HHJsqkKsPZOto7v2e1dY+QF8RkDfaqIdZCVmsOo+
+ swIjbX/GwnLEMPQEkPatZoomdE47QsAoHX6aDRcm+B4X/K6YbT+oYxg/ZttopzKxsmhgF8zrzR0
+ 0bDjhp/l8iL508s3lbcDliGGwf4ghsGIFXWONaxHAvgrDMVUhmGLEfuA4xoiT70rgQQdrteBr6X
+ bktcoF94G/GY6hrO5U9pipAEAz1mttmfFipXAMsQw3BD5fbVwuJbUGl868UJMXdNEa0eIdocLUJ
+ wH8souSfI8eV7DYcr2lPqDgUNlPtzKSqawIMXQ+3rn418PLEMMgxE+hNirCxkDQK82SrgqBqY5K
+ 1eZUanK+94ahrpaUfGCyCCDm0VMpDBImb4SaHKgD/FWFVVmMGFl6Tl3Qgpok0eXXwS5HbAMMQxG
+ MSSIsZekXNp7v8zKymzo0T26WpHO42MjNxsmGHWx6a6/rz/0uWDBi+coFJRdnDHLiI4rTqxQCmo
+ HLEMMgxFR1mJgMOQ1YHO7S14b1zQhQ5iHsLXoiM6g9SbTICDPdIRbiLw5kyqpNK72djNgGWIYjG
+ VIPB5e+cBrauvSdh5tGWKbEHMJFrn6HxDf0bNOfE+Na7cEWaFPT+m+oNMjip9J/pE90Xrz9GphG
+ WIYjGUIW/zZyMysOcrGuTjXVAwXjbgST0WjKbZ70nXkC77VFy9r/jEXZteC077W0NXCMsQwGJuH
+ NGJsp+QlfZqXsHWHKi4n1xS0YZvToWc+lPLr+U+k2BSqgV31MnNjvgfLEMNgjGWsFFytiM3WcZW
+ BOUWgoj6kqv/Ii00RXBm7GljViBFXwaD/d2IZYhiMjbKWgreUhlb3OAFz4CHZz6aGWHd4hTnHFc
+ WWjeBAeJKpKbAMMQy3HEO45Mfecfl3qNSyuB5iRtt/XP43hK5w3nK+4rcBNa7FZQIsQwyDMdFFq
+ xh9UZb+6HtIlr1wF8SHFM6JPsVGueDiMzej8W21sAwxDPYHMQy3nFNfCmltrKuWRDrhv0HlUMWV
+ 8Rrn1JsGyxDDYLS1XA927ZZWooL+MzC29sycuHmNCq3AMsQwfGt8CFHISwEqskbDWwPlkeVgGWJ
+ xczH20lidTXe3AixDLCwsLCwsviX4P68/8cprSSqmAAAAAElFTkSuQmCC
+N:Mustermann;Heiko;;;
+NICKNAME:clown
+NOTE:I'm a friendly note text,\nplease copy me to your address book...
+ORG:KDE
+PHOTO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pV
+ UAAAOW0lEQVR4nO1df2hb1xX+OlyQwQEJXJAgBQlaiEwDsdqO2l1H47RjMe2gbjOoszG2dozG7a
+ CYMIqTjS7NYKSmbE1StsQpWxMXmsSF1WhlTeSwJlZY06gsSVXWUAkakKAGPajBDxrQ/jjnez8k2
+ 7EjJdyU+/0jPz29X9L57jnnO+deAxYWFhYWFhYWFu3Hbe082XT2ZF3+WgAAdHZ0AgBqTg0A4H7D
+ T7oAgNSdKQDAg99/sK33caPB53RqFQBA5PYIAMBdkOf6yc9+ct3P852W786irWjJMs/8+0wdACp
+ VsZTS50V5vSLb1UoVQMCCvnFDx0c65f3BHw4CABJ3RL19mwYfM4Y1e/+0V5h/VbbPXzwPACiVSg
+ AA15XnikVjAIB44Dli3TIKjL+2e0XPYxliGFZshSdzZ+r8OyKGjdl8AQCQfe84AKBaFUaUrojlR
+ LvEUpx5h0eGjo9EZH+kQ7Yz9/d71+vdkAYApO+W16d+/NQNZ8ye8T11ACiV5TkcR5jufCX3XyrL
+ c9XmxUe6bvi5XH3OaNRnSKRL9vWsywAAstNTyz6HZYhhsD+IYbjmMPDxfz6uA0BMqQcAR96eAgB
+ MvSevtTkJa6tzQnVSNn2XOLSZ03ljHHQ7MLxlSIa2kg5pOrTVHHHu/hANRDrke4t2y3cy+KgEMP
+ sP7F/0O7EMMQzXtNxLn16qA8DUu1nvvcm3DgEAFlxxbo4jFpFYK4woXix8qxhxLYxuH6sDQG5Gv
+ qNSueLtcxwdNTTASemoUTi3+HdkGWIYlrTkj2clzC1ckGRv3xv7vH2VK7QAlUDuktA0f/bG+YpL
+ 54WpDDXv7TdPbjm472AdACbe9L+r4mdMHuW+42uTAIByuWwZciugY6kdER3zzn8iMkFt3pc93Kv
+ yd0QzvHYxI5h8Qq/BiK1YLur78nJw4mBIznj2V88ueg+HjxyrA0BEdE7EumLePp773u/e25b7jy
+ bkfAObh7z3SleELS40SVZ/u+2F0ToA7H99PHRtyxDD0GQZZ9RKKxUZ+/YdmAAAFD4peJ9hbF2dq
+ 7aHGSpn12p+dJI7PQsAcDS3YcxfmZPXnrt65F40P+pd3wsgwF69x/xHedlWNkfX+PlUep0ck0om
+ AACxO+R10w82tfRch8leAJOas82ek+cho/sfkGtnT8xYhpiMJh9CiytVZayjYIgO37KCmWgrOPy
+ 3w3UAKKpsf/zd496+wkVhpOu5Lje0XS6X9X5lbJ49KxZIJqSSEu+Xrsj9V1Xw5OcBIPJBDoCfPw
+ 09IVn0sanpOgA8NXR9JYBILO7/rd8n/RV9SM1tPg6wDDEOTQxhFEKLYjbuBljBX9tjzyox/Q+xQ
+ OcrOf7I20cAAIVz+cCnVNJu8Alo3Nb43nEjoSfa8dIOAMDs6Zy+3wcAyM3kvCtUyZ6qXLd0WZj6
+ zE+fAeAX4FZbYo5Fmt9zXfeanwEsQ4xDE0Nqbi203ZhztANVZUb2PdF+imqZLgLX0OvSd7lLbPO
+ +OFan1B8wGotEE3pCYfpWtX4A2L1rZ+gcHN+Pa8EtEpX3mR9tGlieKSf/dVJ9Ysl7j/fhMYTfuD
+ aANMIyxDA0Z+r6Qy7MC1Mi3lge1PijTYetBNNTkjUfOjQJAMi+LzE6S7nJtX50krknE74hZQZbi
+ jo19U7eKcfQAKuaP7lam+hRdZXl19kZX7Xe+PBG2acM7YzE9Bpi1UfeFN9G7Y6+77EfLR59UdGt
+ VX2G8D7IEPrf7PtZq2XdCmhiCBsUYnGxLJfxP5rzkI0Pb6wDwMypmWXHVupOk0eFEfQZIy+MAQB
+ 679OGhnSvd0yk4c5o4c7Xamlrwj7Nqck9uVf5eblGVnONqIY1W38+4h3DyJH1i6j6jFi3+B1GaP
+ m8RGFsa9qrqu7zI6KfHXtHmM8KKq8ZfNZg/rMcLEMMQ5NlH/zLwVDz28SbomVRUxI0RDZJZrqic
+ ibiYmHFz8Q6cmox6bR8bsfOVwD44ylzH47dgK9lTf5dqpOzZ0V1Hnh0AIDfXMdmO7Zx0h8MaO2a
+ TJtQf7D16Se9a9CS0+uEofQVmV7ZjqhPmXhrIvQcnV2doeemv6UfIysAeNoVg9TMfdLqZH3ILYI
+ lx36Oi/v+Knp+/mwhsFfHcR0X2W3Su0Gy4ZrG3tU5GaOHtwhzXv3jqwB8ZgFiaezamFVWAMDO30
+ uOQAsjfvFL8QHpdFLOoPE8FQXG99UvxecMbhameOrCnB8tbh0WtjA3IZuYl6TXC1PoY/b9eRwAU
+ LhYDH3emXdD20EwX4p3SzS4VKWQsAwxDPYHMQxLlnDpJNlbSyEO8BvjvDKrJnZFlcxJ+biGkRT6
+ Ynf45VMAqHypXfMa0uY+8JM2Fpycr+QzfQ+JM0+nhPoMBHit3FFxuHT6FB8rGsYnusWZVsoBAVO
+ HQzrlkoqlcW1qSyQkOMmsE+c9sk2Gy+d//Xzo+Ago40RD3wsARKMrG6oIyxDDsCRDOAtodLsU44
+ Md3QtXKcnzLGELoXN7cos4y3jcl0QAYOFrOZ4ySLGooWqgOYBs4XVp+Qw9i2XZX1GyZtIJvQdhR
+ iIl23S4ntgYSNB67xcWTs2ouDmn97FRrlX6MlzUSt8jo8XQlq0AgMm3J8Ln1OfmnBBg9U2DliGG
+ YUmGEON7pE1lVNtWAGD2IwlPK1fF6jhziGMxE8eR54QhnWtUatY5hpS5ixfCPmexsDHRLedOeDK
+ 6YOARsdLxabHqVJIiozbvqQzvsZnvJ33r7dkgfuXQBWHC6NMSIrvqZ+grOTOsX+evPLlZRMlCXs
+ vGGsZn1osgOt7Q2rMaWIYYhmsyhAj+6tue2ybNZ59ruVfl5k614igL+90NvoNz9LT5jkJgKiVWS
+ 58CAOVSGYAv9FU0eezXaItsHHkkCQBI360RnDKBzODx7iKNGcPa1FCZk3JArZzT53FC5yB4DxGN
+ 8IY04d3+m+23AcDMqZmma6wWliGGYcUMCWL/G+HJJsNbhkPtPBxrGwv5pcufAgDcrzlPXayX430
+ wD2GMTx/CQlkqqUzokGO8MpZGUwtagqbASTAScuZ8ATOVlGa7wT6Jnnb/QacTaO4CFR0JFsXiiX
+ joGrte3lUHgJ2/29ly46BliGG4LoY0YvLoZMgyjr9zXCKyTrEozuemsEfRjtEZfUq+4MvWjFxoj
+ dympTNqamq+iKjP8LJlln6VGQGxkj4hpS2lbJYeXC++xZuQpOxjO2hCi3f0lSxVtAOWIYahLQxp
+ BKXmijJjQed1R9nbptbNAhbbgRBoQYomxAobW0M5jvuWrpGQRj7MyFlsYg6RSDCS863ZnZf3Emu
+ FVcNPDMv9/FNKsfsPHAoxf/RFmbpWLLBtVa5JNvY90FcHWpueYRliGNrKkC/+90Ud8EucLjR7dt
+ Q3qALLaCz7vsT9nO6V7vVXcqBi6jYUqHB7wyvCDWccz6kGpFU1ZqNCENWaKAWOMjZzn8RsbOTDg
+ UOhz690vZJWYBliGNr6i1/6r0zM9LQqVXGZI3yqeQjfZ4ae2SCWyaY3QbhldOB7kqHHEuGaSiNe
+ +a3UXlh2zeiaKYOPS7nW19uA8dekPB3TXGf0xVEAgXKv1oCok+VOCcs2DbY2oWc5WIYYhrb4kOm
+ stFgWL0tUlf9QNB0ygcwoXw5XzQY3D9YBIK7M4MQZwM87HM1dSmzP1KgrFgv7joUFNjlwqSF5ef
+ V1YUHurFj32PYx75gdL+uUhbyuf6UZeq9WFznJv6KqL33fjYRliGFoiSGc0FLUxQW8VsolmsAa4
+ UVCSRnng1PlqBPFNO/gKnVs76RP6WRuo35q6HHJsqkKsPZOto7v2e1dY+QF8RkDfaqIdZCVmsOo
+ +swIjbX/GwnLEMPQEkPatZoomdE47QsAoHX6aDRcm+B4X/K6YbT+oYxg/ZttopzKxsmhgF8zrzR
+ 00bDjhp/l8iL508s3lbcDliGGwf4ghsGIFXWONaxHAvgrDMVUhmGLEfuA4xoiT70rgQQdrteBr6
+ XbktcoF94G/GY6hrO5U9pipAEAz1mttmfFipXAMsQw3BD5fbVwuJbUGl868UJMXdNEa0eIdocLU
+ JwH8souSfI8eV7DYcr2lPqDgUNlPtzKSqawIMXQ+3rn418PLEMMgxE+hNirCxkDQK82SrgqBqY5
+ K1eZUanK+94ahrpaUfGCyCCDm0VMpDBImb4SaHKgD/FWFVVmMGFl6Tl3Qgpok0eXXwS5HbAMMQx
+ GMSSIsZekXNp7v8zKymzo0T26WpHO42MjNxsmGHWx6a6/rz/0uWDBi+coFJRdnDHLiI4rTqxQCm
+ oHLEMMgxFR1mJgMOQ1YHO7S14b1zQhQ5iHsLXoiM6g9SbTICDPdIRbiLw5kyqpNK72djNgGWIYj
+ GVIPB5e+cBrauvSdh5tGWKbEHMJFrn6HxDf0bNOfE+Na7cEWaFPT+m+oNMjip9J/pE90Xrz9Gph
+ GWIYjGUIW/zZyMysOcrGuTjXVAwXjbgST0WjKbZ70nXkC77VFy9r/jEXZteC077W0NXCMsQwGJu
+ HNGJsp+QlfZqXsHWHKi4n1xS0YZvToWc+lPLr+U+k2BSqgV31MnNjvgfLEMNgjGWsFFytiM3WcZ
+ WBOUWgoj6kqv/Ii00RXBm7GljViBFXwaD/d2IZYhiMjbKWgreUhlb3OAFz4CHZz6aGWHd4hTnHF
+ cWWjeBAeJKpKbAMMQy3HEO45Mfecfl3qNSyuB5iRtt/XP43hK5w3nK+4rcBNa7FZQIsQwyDMdFF
+ qxh9UZb+6HtIlr1wF8SHFM6JPsVGueDiMzej8W21sAwxDPYHMQy3nFNfCmltrKuWRDrhv0HlUMW
+ V8Rrn1JsGyxDDYLS1XA927ZZWooL+MzC29sycuHmNCq3AMsQwfGt8CFHISwEqskbDWwPlkeVgGW
+ JxczH20lidTXe3AixDLCwsLCwsviX4P68/8cprSSqmAAAAAElFTkSuQmCC
+ROLE:Developer
+SOUND;ENCODING=b:UklGRiQgAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAgAA
+ B/f39/f39/f39/f39/f4B/gH+Af4CAgIGBgYGBgYGBgYGBgYGBgYGBgIGAgICBgYGBgYGBgYCAg
+ YGAgICAgICAgIGBgICAgYCAgICBgICAgICAgICAgIGAgICAgYCAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAf4CAgICAgICAgICAgICAgIB/f4CAgICAgICAgIB/f4B/f39/f39/f3+AgICAf4CAf3
+ 9/gIB/f4CAgH9/gICAgH+AgICAgICAgICAf4CAgIB/gICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgIB/gICAf39/f39/f3+Af4CAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgH9/f39/f39/f39/f39/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgYCAgICB
+ gICAgYGBgICAgICAgICAgIGAgICBgICBgYGBgYGBgYGAgYGBgYCBgYGBgYGBgYGBgYGBgYGBgYG
+ BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgICAgYGBgIGBgYGAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYGAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgH+AgICAgICAgICAgICAgICAgICAf4CAgIB/gH+Af4B/f39/f39/f
+ 39/f39/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgYG
+ BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgY
+ GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGAgIGBgYCAgICBgYCAgYGBgYCAgICBg
+ YCAgICAgICAgICAgICAgIGAgICAgICAgICAgICAgYGBgYGAgICAgIGBgYCAgICBgYGAgICAgIGA
+ gICAgICAgIGBgYCBgYGAgYGBgYGBgoGBgYGCgoGAgICBgYGBgYCBgYKCgYGBgYGBgYGAgYGBgH9
+ /gIB/f3+AgIB/gH9/gICAf4CBgYB/f35+gIB/f4CBgX9/gYCAgYKCgYCAgYOBgICAgoGAf4GCgn
+ 9/gIB/gICBgH5/f4GAfX+BgYB+fn9/fn+Afn6Af39+f35+fn9+fn6AgH9/f4CAgYCAgYKCgH9/g
+ IKBf3+AgYJ/f4GCg4F/gIGCgoOBfoCDgYF/fH+BgYGAfn6Af4CCgn58gIN/fX+AgICBgYGAgYCB
+ goOCgICBgYB/fXyBhIGAgH5+f4GBgX9/f35+gIKCgXt9goN/f318f4GAf4B/fHt9fn5+fXx+fn5
+ /gH9+fHx/gHx9f4CAfn18gIB9e4GBfn5+foCBfHyAgoGAfX6CgYCBgH+DgHt9gYN/fX2BhYB7en
+ 6AgH98foKCgH18foCAfoCAf4CAf31+fn+AgH1+goJ/fX5/gIKAgIGAfH6CgX17foCBf3+BgoB/g
+ YOBgIB/f4KDgoJ+e3+ChIOBfn1/gX+BfX2BgH59foCCfn1/gX99fICAgoB/f39/gISCf36AgoSB
+ foCBgYGAf4CCf4CCgX5/gYKDgYB/gH+BhIOBgYB/gIOEhIF9foGCg4OBgICBgoOCgICChIOBg4O
+ CgoSDgoKAgYKBgoODhISBf4CCg4SEgoGCg4SEgH6BhIWEgX+EhYKAgoKFhIB+gIKChYKAgYOCgo
+ B+gYWFhIKAg4WEg4ODhIWFhYaGhIWGhYWGh4eHhYOCgoSFg4B/gIB+fX1+fn57e3t7fHt6fH5/f
+ 4GDhYiMkJGQj5GVmZycmZiXlZGIfXRwcnZ4d3Nva2hoamppaWxvc3R0dXl+iJCPjZShqrCzvNHi
+ rFYxOmKSrJ6UmJOAbV1ZboSMjI6JfnNjW2JweYGFg4B/e3Z0c3Btc3p9fHp4fIGBf3+Dh4qLjJG
+ YnZuWkY6MhnpwbnN6fHlya2hlZmdmaW9ycXFzd3t/g42cpquzvMzd/sRADiBZkL6xpLa1jFpANV
+ iHoJ2ioJWGbEQ2VHaOm5aHhIFuYmZzgoVxZFxfdJCSgnt/kKewr7bDsopcSV6JopyRjpOQfmNXZ
+ HmGhYB8fXtvXExKXnyKgnJsc3t8fZC0ysfK0+vHTxEeXY6yr6rAxJRSNTdbh5+fp7KjglQ6PVx3
+ ho6Ph3dmW15oa2x0hIuNkK3R18jJ0Ys8LUlxkqais8WtdlBFT2yDjpywro9mS0VVaXaBiIdyX2J
+ rcG5udYKKjaXT4dfV2Y82JkBpiaKkvdO2eE9GTGZ4hZu6u51yTUNOX2l9kZWKdl5PXG93dHeAip
+ KZut7j2tacRjJIYHCOo77XvoRdVlFYZniVuL+kfl9ST1Rcb4mTjoNxW1pqdXd5fH6CiKHN4d7k2
+ 4E4N05Yaoqo1eO0emVbTU5ZdKDCuZV9b1tMSVlyiYp8dHl7cmpqdYCCgIqixNTR2OqnRTlPXFt3
+ ksbnw4lzcFdKS2KMtLWZiIZwV0lPZXZ1b3uNinZqbXJ3dX2XxOPg4/zDUDRCTUhmiMXx1p+If1g
+ +O1J7pK+yspN3W0pGVGdugYmCen54b21rb3V9i6vZ7vD92WpAS0gzSny04tq3qp5qPDhHX3uVrs
+ KukXVhT0hRYnF/gnuCh31ubG9zdHeMs+Dr8P7KalFVOylJeq7QzMPIqmtIRUZOY4KovqicknhZS
+ 1BWXWh6gIeLiH91b21vc4GcyuXt/umTZGhGIjJfh6y+yt7LkGlfTD1GYYOipKKwnXhkXldVUFhs
+ dYOMjYWBem9ucoCWvNTm9+2weHdZMSpQboujvtnUrY6BZEdDUmV4jZypppeFd2dcWVFWWWh8hYS
+ Ii393c3Z7iJ3A1ePp3aGDfVYyOVZkfJe5zsavpJNsU1BTVmR9kpmcn5uGdGxoWU5VVGZ0fIKLh4
+ F/d3Z6g4ufuc3e3depjoViPz9VWm2FqLu8tLGifmphWlJZbH+Lj5+elod+c19WU0xUZW11gISEg
+ nx6e3h6g4qWp7zM3NrSpZGIYkRFVVVlfZ6vsbW3qIl7b2BUWGVwe4ybn52TkIRuX2BVRVFjZmp7
+ hIF9fX12cnZ6eX6LlZyousfP0c++jot6X0ZRXF9sgaCnqa6yn4eAdGZZYGlvd4mcnqCWnY91aWN
+ cSUtYYmJxf399f351cXFzcHF4foOIlZ+hp7CytLm8uLKNg4RtYF5oanN8j5qZnqGaioV9dGpqb3
+ J1f42Rk5iZjIKAcmFcW1RXYGhwdXt+e3Z2c25tcXJxd3yChY2Tl5udn6Gho6ioq6uonoaHbWNzZ
+ GdxgoGPlZqdj5CJgHFzcWxwdH1/hYqOioSFfnFpaWBdX2Jqb3N5fHh4eHNxcXFxc3d7foOJjI6Q
+ lZOSlZOTl5eYnJucnpyZkYiEfnd1dnZ5fYKHio6QkIyHg394dHRzc3Z5fHt2enJrcG5obG9sbW9
+ ucHJzdnh3eXp5eXt9foCDhoeJjI6OkJGSk5aYmZudn6Gjn5qLh4J3cnJ0dHR5g4GEi42Ih4aBfH
+ h4dXR1eHl4dXhuaW5rZ2ltaWttbW5vcnR1dHZ2dXV2d3d7fn+BhIeIiYuOjpCTlJWZnJueoKarr
+ ZuJhntvaW1ucnh+hIWJi4qFg4F7d3V1c3R3fH1/gHlvbXJnXmdpYmdtamtxcW9vbm9tamxtbG5z
+ dXZ5fHx7fX9+fH6AgoWHioyNj5CRlZeYmp2TiYyGenp9eXh9fX19foB/fH1/e3p8e3l5e3x7e3x
+ 6d3FxcnBwcG9vb29vb29wb25tbWxsbW5vb3Fyc3N1dXV1dnd4ent9fn+BgoGAgoWEhoqJiYyLi4
+ yPioaLiYSEh4WDhIOCgoKAgX58fXx5eXp4eHl4dXZ1cnN0dHR1cnJycXBxcnJycW9ubGxsbW1ub
+ m5ub29vcXNzdHZ2dXh6enp+fn5/gYCBhIKFiYeGiomJi4qKjIuLjIyLiomKh4aKioeIiIWDgYB9
+ fHx7e3l4eHh2dnd3d3l7enp6enp5ent7e3x7enl6e3t7fHx7e3p8fX5/gIKDg4GCg4KCg4OEhoe
+ Hh4iJiomLjIqLi4uLi46Pjo2OjouJiomHh4aDgH9/fXp5eXh3d3h4eHh4d3V2d3Z0dnd0dXd3d3
+ p7fH59fX19fH5+fn+Af4CAf3+Af359f4KDgoSIiIeJjI6PkJOUk5SYmZiYmZqZmJqamJiZmJaWl
+ ZSTkZCQj46Ni4qHh4iFgoKBfn9+fn+AgYCAgYOCgoSEg4SFhIODg4OCgICBf35/gICAgYKCgYGD
+ hIOBgYOCgYGAgIGBgIGCgoKBgoSEg4KCgH9/f318fHt6e3t7enl5eHh5fH18foCAf4GDg4OFhoa
+ GhoeJh4aJioiIiYiIhoWGh4aGh4eHiImHh4mIiIeHh4iIiIqJioqJh4mKh4eJiYiHh4eHhoWGhY
+ OEhYWEhYWDg4SFhYWEg4SDgYKCgH+AgICBgoOCgYGBgYKEhoaGhoiIh4eJiYmKioqKiomKioiJi
+ oqJiYmJiIiIiYiHh4eHh4eGhYWFhISDgoKCgoKCgYGAf31+fXx9fn58e3x8fHx9fX19fX19fX19
+ fHx9fn5/gICBgYGCgoKCg4OCgoODgoKDg4OCgoKCgoGBgYGBgYGCgYCBgIGCgoKDg4KCg4OEhIS
+ FhYWFhYWFhYWFhoWGhoWEhISDg4ODg4ODg4ODg4SDgoKDg4OCg4SDgoKDgoGBgoGAgIB/f35+fn
+ 9+fn9/f35+fn5/gICAgIGBgYKCg4ODg4SEg4OEhIODhIODhIODg4ODg4OCgoKBgoKCg4OCgoODg
+ 4ODg4OEhISEhIWEhIWFhISEhISEhIWEhISEhIODhISEg4ODg4ODg4ODg4ODgoKCgoKCg4KCgoKC
+ goKCgoKCgoOCgoKCgoKBgYKCgoKCgoGBgYGAgICAgYGBgYCAf39/f39/f3+Af39/f39/f39/gIC
+ AgIB/f39/f39/gICAgIB/f39/gIB/f39/gH9/f39/f39/fn5+f39/f39/fn5+fn5/f39/f39/fn
+ 5+fn9/f39/f35+fn5+fn9/f35/f39+fn5+fn9/fn5+fn5+fn5+f39+fn5+fn19fX5+fn5+fn5+f
+ X5+fn5+fn5+fn19fn5+fn5+fX19fX59fn5+fn19fX19fX1+fX19fX19fX19fX19fX19fX19fX19
+ fX19fX19fX19fX19fX19fX19fX19fX19fXx9fX18fH19fXx8fXx9fXx8fHx8fHx8fH19fX19fXx
+ 9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX
+ 19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
+ X19fX19fX19fX19fX19fX19fX19fX19fX1+fX5+fn19fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+
+ fn5+fn5+fn5+fn5+f39/fn5/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f3
+ 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f
+ 39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/
+ f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f39/f39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3
+ 9/f39/f39/f39/f39/f39/f35+fn5+f35/f39/f39/f39/f39/f39/f39/f39/f39/f39/f35+f
+ 39+fn5/fn5+f39/fn9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/
+ f39/f39/f39/f39/f39+fn9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f4CAf39/f39/f39/gIB/f39/f39/f4B/f4CAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgIB/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgIGBgYGAgYGBgICAgIGAgICBgYGAgYGBgYGBgYGBgYGBgY
+ GBgYGBgYGBgYGBgYGBgYGAgYCAgICAgICAgICAgICAgICAgICAgICAgICAgH+AgICAf39/gH9/f
+ 3+Af39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4CAgH9/gIB/f39/f4B/f3+AgICAf4CA
+ gICAgICAgICAgICAgH9/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgIB/f39/f39/f39/f4CAf39/f39/f39/f39/f39/f39+fn
+ 9/fn5+fn5+fn5+fn5+fX19fX19fX19fX19fX19fX19fn5+fn5+fn9/f39/f4CAgICAgICBgYGBg
+ YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICAgICAgICAgICAgICAgYGAgICA
+ gICAgICAgICAgICBgYGAgICAgICAgICAgICAgICAgICAgICAgIB/gICAgH9/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAgICAf39/f39/gH+AgI
+ CAgICAgIB/f39/f39/f4B/f39/f39/f39/f39/f39/f39/f39/f39/f39/fn9/f39/f39/f39+f
+ 39/f39/f39/f39/f39/f39/f39/f4B/f4CAgICAf39/f39/gICAf39/gICAgICAgICAgICAgICA
+ gICAgIGAgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICBgYGBgYGBgYCBgYGBgYCAgIG
+ BgICAgICAgICAgICAgICAgICAgICAgICAgICBgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAf3+AgIB/f4CAf39/f39/f3+AgICAgH+AgICAf3+AgICAgICAgH9/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3
+ 9/f39/f39/gH9/gICAgICAgICAgIB/gICAgH+AgIB/f39/f39/gICAgICAgH9/f3+AgH9/f4B/f
+ 39/f39/f39/f39/f3+AgICAgICAf3+AgIB/f4CAgIB/f4CAgICAgICAgICAf39/f3+AgIB/f4CA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAf39/f39
+ /f4CAgH9/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f39/f39/gIB/f39/f39/f39/gH9/f3
+ 9/f4CAgICAgIB/gICAgICAgICAgICAgICAgICAgICAgICAgH+AgICAf4CAgICAgICAgIB/gICAf
+ 4CAgICAgH+Af4CAgH9/f39/f39/f39/gICAgICAgICAgICAf39/f39/f4CAgH9/gICAf3+AgICA
+ gICAgICAgICAgICAgICAgICAgIB/f39/gIB/f4CAgIB/f4CAgH9/gICAf39/f39/f39/f39/f39
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3
+ 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAf39/gH9/f3+AgH9/f39/f39/f39/f39/f
+ 39/f39/f39/f39/f39/f39/f39/gICAf3+AgH9/f3+AgH9/f4CAgH9/gICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAgICAgICBgYGBgIGBgYCAgYGAgICAgYCAgICAgICAgICAgICAgICAg
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
+ CAgICAgICAgICAgICAgICAf39/f39/f4CAgIB/f4CAgICAgICAgICAgICAgICAgICAgICAgICAg
+ ICAf39/f3+Af39/f39/f39/f4B/f39/f4CAgICAgICAgICAgA==
+TEL;TYPE=HOME:0351 223344
+TEL;TYPE=WORK:0351 443322
+TEL;TYPE=CELL:0173 223344
+UID:P9HXg34Oav
+URL:http://www.mustermann.de/~heiko/
+VERSION:3.0
+X-KADDRESSBOOK-X-Anniversary:1982-07-07
+X-KADDRESSBOOK-X-AssistantsName:Mr. Smithers
+X-KADDRESSBOOK-X-Department:Edutainment
+X-KADDRESSBOOK-X-IMAddress:122334455
+X-KADDRESSBOOK-X-ManagersName:Mr. Burnes
+X-KADDRESSBOOK-X-Office:223 2nd stage
+X-KADDRESSBOOK-X-Profession:Clown
+X-KADDRESSBOOK-X-SpousesName:femal clown
+END:VCARD
+
diff --git a/autotests/data/vcard5.vcf.ref b/autotests/data/vcard5.vcf.ref
new file mode 100644 (file)
index 0000000..531271f
--- /dev/null
@@ -0,0 +1,312 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+ADR;TYPE=home,pref:;;Buehlauer Strasse 45;Dresden;Sachsen;01327;Germany\r
+ADR;TYPE=work:;;Prager Strasse 55;Dresden;Sachsen;01066;Germany\r
+BDAY:1955-07-19T00:00:00Z\r
+CATEGORIES:Friend\r
+CLASS:CONFIDENTIAL\r
+EMAIL;TYPE=PREF:heiko.mustermann@freenet.org\r
+EMAIL:heiko8@tst.inf.tu-dresden.de\r
+FN:Mustermann\, Heiko\r
+GEO:19.283333;166.616669\r
+LOGO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVU\r
+ AAAOW0lEQVR4nO1df2hb1xX+OlyQwQEJXJAgBQlaiEwDsdqO2l1H47RjMe2gbjOoszG2dozG7aC\r
+ YMIqTjS7NYKSmbE1StsQpWxMXmsSF1WhlTeSwJlZY06gsSVXWUAkakKAGPajBDxrQ/jjnez8k27\r
+ EjJdyU+/0jPz29X9L57jnnO+deAxYWFhYWFhYWFu3Hbe082XT2ZF3+WgAAdHZ0AgBqTg0A4H7DT\r
+ 7oAgNSdKQDAg99/sK33caPB53RqFQBA5PYIAMBdkOf6yc9+ct3P852W786irWjJMs/8+0wdACpV\r
+ sZTS50V5vSLb1UoVQMCCvnFDx0c65f3BHw4CABJ3RL19mwYfM4Y1e/+0V5h/VbbPXzwPACiVSgA\r
+ A15XnikVjAIB44Dli3TIKjL+2e0XPYxliGFZshSdzZ+r8OyKGjdl8AQCQfe84AKBaFUaUrojlRL\r
+ vEUpx5h0eGjo9EZH+kQ7Yz9/d71+vdkAYApO+W16d+/NQNZ8ye8T11ACiV5TkcR5jufCX3XyrLc\r
+ 9XmxUe6bvi5XH3OaNRnSKRL9vWsywAAstNTyz6HZYhhsD+IYbjmMPDxfz6uA0BMqQcAR96eAgBM\r
+ vSevtTkJa6tzQnVSNn2XOLSZ03ljHHQ7MLxlSIa2kg5pOrTVHHHu/hANRDrke4t2y3cy+KgEMPs\r
+ P7F/0O7EMMQzXtNxLn16qA8DUu1nvvcm3DgEAFlxxbo4jFpFYK4woXix8qxhxLYxuH6sDQG5Gvq\r
+ NSueLtcxwdNTTASemoUTi3+HdkGWIYlrTkj2clzC1ckGRv3xv7vH2VK7QAlUDuktA0f/bG+YpL5\r
+ 4WpDDXv7TdPbjm472AdACbe9L+r4mdMHuW+42uTAIByuWwZciugY6kdER3zzn8iMkFt3pc93Kvy\r
+ d0QzvHYxI5h8Qq/BiK1YLur78nJw4mBIznj2V88ueg+HjxyrA0BEdE7EumLePp773u/e25b7jyb\r
+ kfAObh7z3SleELS40SVZ/u+2F0ToA7H99PHRtyxDD0GQZZ9RKKxUZ+/YdmAAAFD4peJ9hbF2dq7\r
+ aHGSpn12p+dJI7PQsAcDS3YcxfmZPXnrt65F40P+pd3wsgwF69x/xHedlWNkfX+PlUep0ck0omA\r
+ ACxO+R10w82tfRch8leAJOas82ek+cho/sfkGtnT8xYhpiMJh9CiytVZayjYIgO37KCmWgrOPy3\r
+ w3UAKKpsf/zd496+wkVhpOu5Lje0XS6X9X5lbJ49KxZIJqSSEu+Xrsj9V1Xw5OcBIPJBDoCfPw0\r
+ 9IVn0sanpOgA8NXR9JYBILO7/rd8n/RV9SM1tPg6wDDEOTQxhFEKLYjbuBljBX9tjzyox/Q+xQO\r
+ crOf7I20cAAIVz+cCnVNJu8Alo3Nb43nEjoSfa8dIOAMDs6Zy+3wcAyM3kvCtUyZ6qXLd0WZj6z\r
+ E+fAeAX4FZbYo5Fmt9zXfeanwEsQ4xDE0Nqbi203ZhztANVZUb2PdF+imqZLgLX0OvSd7lLbPO+\r
+ OFan1B8wGotEE3pCYfpWtX4A2L1rZ+gcHN+Pa8EtEpX3mR9tGlieKSf/dVJ9Ysl7j/fhMYTfuDa\r
+ ANMIyxDA0Z+r6Qy7MC1Mi3lge1PijTYetBNNTkjUfOjQJAMi+LzE6S7nJtX50krknE74hZQZbij\r
+ o19U7eKcfQAKuaP7lam+hRdZXl19kZX7Xe+PBG2acM7YzE9Bpi1UfeFN9G7Y6+77EfLR59UdGtV\r
+ X2G8D7IEPrf7PtZq2XdCmhiCBsUYnGxLJfxP5rzkI0Pb6wDwMypmWXHVupOk0eFEfQZIy+MAQB6\r
+ 79OGhnSvd0yk4c5o4c7Xamlrwj7Nqck9uVf5eblGVnONqIY1W38+4h3DyJH1i6j6jFi3+B1GaPm\r
+ 8RGFsa9qrqu7zI6KfHXtHmM8KKq8ZfNZg/rMcLEMMQ5NlH/zLwVDz28SbomVRUxI0RDZJZrqici\r
+ biYmHFz8Q6cmox6bR8bsfOVwD44ylzH47dgK9lTf5dqpOzZ0V1Hnh0AIDfXMdmO7Zx0h8MaO2aT\r
+ JtQf7D16Se9a9CS0+uEofQVmV7ZjqhPmXhrIvQcnV2doeemv6UfIysAeNoVg9TMfdLqZH3ILYIl\r
+ x36Oi/v+Knp+/mwhsFfHcR0X2W3Su0Gy4ZrG3tU5GaOHtwhzXv3jqwB8ZgFiaezamFVWAMDO30u\r
+ OQAsjfvFL8QHpdFLOoPE8FQXG99UvxecMbhameOrCnB8tbh0WtjA3IZuYl6TXC1PoY/b9eRwAUL\r
+ hYDH3emXdD20EwX4p3SzS4VKWQsAwxDPYHMQxLlnDpJNlbSyEO8BvjvDKrJnZFlcxJ+biGkRT6Y\r
+ nf45VMAqHypXfMa0uY+8JM2Fpycr+QzfQ+JM0+nhPoMBHit3FFxuHT6FB8rGsYnusWZVsoBAVOH\r
+ QzrlkoqlcW1qSyQkOMmsE+c9sk2Gy+d//Xzo+Ago40RD3wsARKMrG6oIyxDDsCRDOAtodLsU44M\r
+ d3QtXKcnzLGELoXN7cos4y3jcl0QAYOFrOZ4ySLGooWqgOYBs4XVp+Qw9i2XZX1GyZtIJvQdhRi\r
+ Il23S4ntgYSNB67xcWTs2ouDmn97FRrlX6MlzUSt8jo8XQlq0AgMm3J8Ln1OfmnBBg9U2DliGGY\r
+ UmGEON7pE1lVNtWAGD2IwlPK1fF6jhziGMxE8eR54QhnWtUatY5hpS5ixfCPmexsDHRLedOeDK6\r
+ YOARsdLxabHqVJIiozbvqQzvsZnvJ33r7dkgfuXQBWHC6NMSIrvqZ+grOTOsX+evPLlZRMlCXsv\r
+ GGsZn1osgOt7Q2rMaWIYYhmsyhAj+6tue2ybNZ59ruVfl5k614igL+90NvoNz9LT5jkJgKiVWS5\r
+ 8CAOVSGYAv9FU0eezXaItsHHkkCQBI360RnDKBzODx7iKNGcPa1FCZk3JArZzT53FC5yB4DxGN8\r
+ IY04d3+m+23AcDMqZmma6wWliGGYcUMCWL/G+HJJsNbhkPtPBxrGwv5pcufAgDcrzlPXayX430w\r
+ D2GMTx/CQlkqqUzokGO8MpZGUwtagqbASTAScuZ8ATOVlGa7wT6Jnnb/QacTaO4CFR0JFsXiiXj\r
+ oGrte3lUHgJ2/29ly46BliGG4LoY0YvLoZMgyjr9zXCKyTrEozuemsEfRjtEZfUq+4MvWjFxojd\r
+ ympTNqamq+iKjP8LJlln6VGQGxkj4hpS2lbJYeXC++xZuQpOxjO2hCi3f0lSxVtAOWIYahLQxpB\r
+ KXmijJjQed1R9nbptbNAhbbgRBoQYomxAobW0M5jvuWrpGQRj7MyFlsYg6RSDCS863ZnZf3EmuF\r
+ VcNPDMv9/FNKsfsPHAoxf/RFmbpWLLBtVa5JNvY90FcHWpueYRliGNrKkC/+90Ud8EucLjR7dtQ\r
+ 3qALLaCz7vsT9nO6V7vVXcqBi6jYUqHB7wyvCDWccz6kGpFU1ZqNCENWaKAWOMjZzn8RsbOTDgU\r
+ Ohz690vZJWYBliGNr6i1/6r0zM9LQqVXGZI3yqeQjfZ4ae2SCWyaY3QbhldOB7kqHHEuGaSiNe+\r
+ a3UXlh2zeiaKYOPS7nW19uA8dekPB3TXGf0xVEAgXKv1oCok+VOCcs2DbY2oWc5WIYYhrb4kOms\r
+ tFgWL0tUlf9QNB0ygcwoXw5XzQY3D9YBIK7M4MQZwM87HM1dSmzP1KgrFgv7joUFNjlwqSF5efV\r
+ 1YUHurFj32PYx75gdL+uUhbyuf6UZeq9WFznJv6KqL33fjYRliGFoiSGc0FLUxQW8VsolmsAa4U\r
+ VCSRnng1PlqBPFNO/gKnVs76RP6WRuo35q6HHJsqkKsPZOto7v2e1dY+QF8RkDfaqIdZCVmsOo+\r
+ swIjbX/GwnLEMPQEkPatZoomdE47QsAoHX6aDRcm+B4X/K6YbT+oYxg/ZttopzKxsmhgF8zrzR0\r
+ 0bDjhp/l8iL508s3lbcDliGGwf4ghsGIFXWONaxHAvgrDMVUhmGLEfuA4xoiT70rgQQdrteBr6X\r
+ bktcoF94G/GY6hrO5U9pipAEAz1mttmfFipXAMsQw3BD5fbVwuJbUGl868UJMXdNEa0eIdocLUJ\r
+ wH8souSfI8eV7DYcr2lPqDgUNlPtzKSqawIMXQ+3rn418PLEMMgxE+hNirCxkDQK82SrgqBqY5K\r
+ 1eZUanK+94ahrpaUfGCyCCDm0VMpDBImb4SaHKgD/FWFVVmMGFl6Tl3Qgpok0eXXwS5HbAMMQxG\r
+ MSSIsZekXNp7v8zKymzo0T26WpHO42MjNxsmGHWx6a6/rz/0uWDBi+coFJRdnDHLiI4rTqxQCmo\r
+ HLEMMgxFR1mJgMOQ1YHO7S14b1zQhQ5iHsLXoiM6g9SbTICDPdIRbiLw5kyqpNK72djNgGWIYjG\r
+ VIPB5e+cBrauvSdh5tGWKbEHMJFrn6HxDf0bNOfE+Na7cEWaFPT+m+oNMjip9J/pE90Xrz9GphG\r
+ WIYjGUIW/zZyMysOcrGuTjXVAwXjbgST0WjKbZ70nXkC77VFy9r/jEXZteC077W0NXCMsQwGJuH\r
+ NGJsp+QlfZqXsHWHKi4n1xS0YZvToWc+lPLr+U+k2BSqgV31MnNjvgfLEMNgjGWsFFytiM3WcZW\r
+ BOUWgoj6kqv/Ii00RXBm7GljViBFXwaD/d2IZYhiMjbKWgreUhlb3OAFz4CHZz6aGWHd4hTnHFc\r
+ WWjeBAeJKpKbAMMQy3HEO45Mfecfl3qNSyuB5iRtt/XP43hK5w3nK+4rcBNa7FZQIsQwyDMdFFq\r
+ xh9UZb+6HtIlr1wF8SHFM6JPsVGueDiMzej8W21sAwxDPYHMQy3nFNfCmltrKuWRDrhv0HlUMWV\r
+ 8Rrn1JsGyxDDYLS1XA927ZZWooL+MzC29sycuHmNCq3AMsQwfGt8CFHISwEqskbDWwPlkeVgGWJ\r
+ xczH20lidTXe3AixDLCwsLCwsviX4P68/8cprSSqmAAAAAElFTkSuQmCC\r
+N:Mustermann;Heiko;;;\r
+NICKNAME:clown\r
+NOTE:I'm a friendly note text\,\nplease copy me to your address book...\r
+ORG:KDE\r
+PHOTO;ENCODING=b;TYPE=image/png:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pV\r
+ UAAAOW0lEQVR4nO1df2hb1xX+OlyQwQEJXJAgBQlaiEwDsdqO2l1H47RjMe2gbjOoszG2dozG7a\r
+ CYMIqTjS7NYKSmbE1StsQpWxMXmsSF1WhlTeSwJlZY06gsSVXWUAkakKAGPajBDxrQ/jjnez8k2\r
+ 7EjJdyU+/0jPz29X9L57jnnO+deAxYWFhYWFhYWFu3Hbe082XT2ZF3+WgAAdHZ0AgBqTg0A4H7D\r
+ T7oAgNSdKQDAg99/sK33caPB53RqFQBA5PYIAMBdkOf6yc9+ct3P852W786irWjJMs/8+0wdACp\r
+ VsZTS50V5vSLb1UoVQMCCvnFDx0c65f3BHw4CABJ3RL19mwYfM4Y1e/+0V5h/VbbPXzwPACiVSg\r
+ AA15XnikVjAIB44Dli3TIKjL+2e0XPYxliGFZshSdzZ+r8OyKGjdl8AQCQfe84AKBaFUaUrojlR\r
+ LvEUpx5h0eGjo9EZH+kQ7Yz9/d71+vdkAYApO+W16d+/NQNZ8ye8T11ACiV5TkcR5jufCX3XyrL\r
+ c9XmxUe6bvi5XH3OaNRnSKRL9vWsywAAstNTyz6HZYhhsD+IYbjmMPDxfz6uA0BMqQcAR96eAgB\r
+ MvSevtTkJa6tzQnVSNn2XOLSZ03ljHHQ7MLxlSIa2kg5pOrTVHHHu/hANRDrke4t2y3cy+KgEMP\r
+ sP7F/0O7EMMQzXtNxLn16qA8DUu1nvvcm3DgEAFlxxbo4jFpFYK4woXix8qxhxLYxuH6sDQG5Gv\r
+ qNSueLtcxwdNTTASemoUTi3+HdkGWIYlrTkj2clzC1ckGRv3xv7vH2VK7QAlUDuktA0f/bG+YpL\r
+ 54WpDDXv7TdPbjm472AdACbe9L+r4mdMHuW+42uTAIByuWwZciugY6kdER3zzn8iMkFt3pc93Kv\r
+ yd0QzvHYxI5h8Qq/BiK1YLur78nJw4mBIznj2V88ueg+HjxyrA0BEdE7EumLePp773u/e25b7jy\r
+ bkfAObh7z3SleELS40SVZ/u+2F0ToA7H99PHRtyxDD0GQZZ9RKKxUZ+/YdmAAAFD4peJ9hbF2dq\r
+ 7aHGSpn12p+dJI7PQsAcDS3YcxfmZPXnrt65F40P+pd3wsgwF69x/xHedlWNkfX+PlUep0ck0om\r
+ AACxO+R10w82tfRch8leAJOas82ek+cho/sfkGtnT8xYhpiMJh9CiytVZayjYIgO37KCmWgrOPy\r
+ 3w3UAKKpsf/zd496+wkVhpOu5Lje0XS6X9X5lbJ49KxZIJqSSEu+Xrsj9V1Xw5OcBIPJBDoCfPw\r
+ 09IVn0sanpOgA8NXR9JYBILO7/rd8n/RV9SM1tPg6wDDEOTQxhFEKLYjbuBljBX9tjzyox/Q+xQ\r
+ OcrOf7I20cAAIVz+cCnVNJu8Alo3Nb43nEjoSfa8dIOAMDs6Zy+3wcAyM3kvCtUyZ6qXLd0WZj6\r
+ zE+fAeAX4FZbYo5Fmt9zXfeanwEsQ4xDE0Nqbi203ZhztANVZUb2PdF+imqZLgLX0OvSd7lLbPO\r
+ +OFan1B8wGotEE3pCYfpWtX4A2L1rZ+gcHN+Pa8EtEpX3mR9tGlieKSf/dVJ9Ysl7j/fhMYTfuD\r
+ aANMIyxDA0Z+r6Qy7MC1Mi3lge1PijTYetBNNTkjUfOjQJAMi+LzE6S7nJtX50krknE74hZQZbi\r
+ jo19U7eKcfQAKuaP7lam+hRdZXl19kZX7Xe+PBG2acM7YzE9Bpi1UfeFN9G7Y6+77EfLR59UdGt\r
+ VX2G8D7IEPrf7PtZq2XdCmhiCBsUYnGxLJfxP5rzkI0Pb6wDwMypmWXHVupOk0eFEfQZIy+MAQB\r
+ 679OGhnSvd0yk4c5o4c7Xamlrwj7Nqck9uVf5eblGVnONqIY1W38+4h3DyJH1i6j6jFi3+B1GaP\r
+ m8RGFsa9qrqu7zI6KfHXtHmM8KKq8ZfNZg/rMcLEMMQ5NlH/zLwVDz28SbomVRUxI0RDZJZrqic\r
+ ibiYmHFz8Q6cmox6bR8bsfOVwD44ylzH47dgK9lTf5dqpOzZ0V1Hnh0AIDfXMdmO7Zx0h8MaO2a\r
+ TJtQf7D16Se9a9CS0+uEofQVmV7ZjqhPmXhrIvQcnV2doeemv6UfIysAeNoVg9TMfdLqZH3ILYI\r
+ lx36Oi/v+Knp+/mwhsFfHcR0X2W3Su0Gy4ZrG3tU5GaOHtwhzXv3jqwB8ZgFiaezamFVWAMDO30\r
+ uOQAsjfvFL8QHpdFLOoPE8FQXG99UvxecMbhameOrCnB8tbh0WtjA3IZuYl6TXC1PoY/b9eRwAU\r
+ LhYDH3emXdD20EwX4p3SzS4VKWQsAwxDPYHMQxLlnDpJNlbSyEO8BvjvDKrJnZFlcxJ+biGkRT6\r
+ Ynf45VMAqHypXfMa0uY+8JM2Fpycr+QzfQ+JM0+nhPoMBHit3FFxuHT6FB8rGsYnusWZVsoBAVO\r
+ HQzrlkoqlcW1qSyQkOMmsE+c9sk2Gy+d//Xzo+Ago40RD3wsARKMrG6oIyxDDsCRDOAtodLsU44\r
+ Md3QtXKcnzLGELoXN7cos4y3jcl0QAYOFrOZ4ySLGooWqgOYBs4XVp+Qw9i2XZX1GyZtIJvQdhR\r
+ iIl23S4ntgYSNB67xcWTs2ouDmn97FRrlX6MlzUSt8jo8XQlq0AgMm3J8Ln1OfmnBBg9U2DliGG\r
+ YUmGEON7pE1lVNtWAGD2IwlPK1fF6jhziGMxE8eR54QhnWtUatY5hpS5ixfCPmexsDHRLedOeDK\r
+ 6YOARsdLxabHqVJIiozbvqQzvsZnvJ33r7dkgfuXQBWHC6NMSIrvqZ+grOTOsX+evPLlZRMlCXs\r
+ vGGsZn1osgOt7Q2rMaWIYYhmsyhAj+6tue2ybNZ59ruVfl5k614igL+90NvoNz9LT5jkJgKiVWS\r
+ 58CAOVSGYAv9FU0eezXaItsHHkkCQBI360RnDKBzODx7iKNGcPa1FCZk3JArZzT53FC5yB4DxGN\r
+ 8IY04d3+m+23AcDMqZmma6wWliGGYcUMCWL/G+HJJsNbhkPtPBxrGwv5pcufAgDcrzlPXayX430\r
+ wD2GMTx/CQlkqqUzokGO8MpZGUwtagqbASTAScuZ8ATOVlGa7wT6Jnnb/QacTaO4CFR0JFsXiiX\r
+ joGrte3lUHgJ2/29ly46BliGG4LoY0YvLoZMgyjr9zXCKyTrEozuemsEfRjtEZfUq+4MvWjFxoj\r
+ dympTNqamq+iKjP8LJlln6VGQGxkj4hpS2lbJYeXC++xZuQpOxjO2hCi3f0lSxVtAOWIYahLQxp\r
+ BKXmijJjQed1R9nbptbNAhbbgRBoQYomxAobW0M5jvuWrpGQRj7MyFlsYg6RSDCS863ZnZf3Emu\r
+ FVcNPDMv9/FNKsfsPHAoxf/RFmbpWLLBtVa5JNvY90FcHWpueYRliGNrKkC/+90Ud8EucLjR7dt\r
+ Q3qALLaCz7vsT9nO6V7vVXcqBi6jYUqHB7wyvCDWccz6kGpFU1ZqNCENWaKAWOMjZzn8RsbOTDg\r
+ UOhz690vZJWYBliGNr6i1/6r0zM9LQqVXGZI3yqeQjfZ4ae2SCWyaY3QbhldOB7kqHHEuGaSiNe\r
+ +a3UXlh2zeiaKYOPS7nW19uA8dekPB3TXGf0xVEAgXKv1oCok+VOCcs2DbY2oWc5WIYYhrb4kOm\r
+ stFgWL0tUlf9QNB0ygcwoXw5XzQY3D9YBIK7M4MQZwM87HM1dSmzP1KgrFgv7joUFNjlwqSF5ef\r
+ V1YUHurFj32PYx75gdL+uUhbyuf6UZeq9WFznJv6KqL33fjYRliGFoiSGc0FLUxQW8VsolmsAa4\r
+ UVCSRnng1PlqBPFNO/gKnVs76RP6WRuo35q6HHJsqkKsPZOto7v2e1dY+QF8RkDfaqIdZCVmsOo\r
+ +swIjbX/GwnLEMPQEkPatZoomdE47QsAoHX6aDRcm+B4X/K6YbT+oYxg/ZttopzKxsmhgF8zrzR\r
+ 00bDjhp/l8iL508s3lbcDliGGwf4ghsGIFXWONaxHAvgrDMVUhmGLEfuA4xoiT70rgQQdrteBr6\r
+ XbktcoF94G/GY6hrO5U9pipAEAz1mttmfFipXAMsQw3BD5fbVwuJbUGl868UJMXdNEa0eIdocLU\r
+ JwH8souSfI8eV7DYcr2lPqDgUNlPtzKSqawIMXQ+3rn418PLEMMgxE+hNirCxkDQK82SrgqBqY5\r
+ K1eZUanK+94ahrpaUfGCyCCDm0VMpDBImb4SaHKgD/FWFVVmMGFl6Tl3Qgpok0eXXwS5HbAMMQx\r
+ GMSSIsZekXNp7v8zKymzo0T26WpHO42MjNxsmGHWx6a6/rz/0uWDBi+coFJRdnDHLiI4rTqxQCm\r
+ oHLEMMgxFR1mJgMOQ1YHO7S14b1zQhQ5iHsLXoiM6g9SbTICDPdIRbiLw5kyqpNK72djNgGWIYj\r
+ GVIPB5e+cBrauvSdh5tGWKbEHMJFrn6HxDf0bNOfE+Na7cEWaFPT+m+oNMjip9J/pE90Xrz9Gph\r
+ GWIYjGUIW/zZyMysOcrGuTjXVAwXjbgST0WjKbZ70nXkC77VFy9r/jEXZteC077W0NXCMsQwGJu\r
+ HNGJsp+QlfZqXsHWHKi4n1xS0YZvToWc+lPLr+U+k2BSqgV31MnNjvgfLEMNgjGWsFFytiM3WcZ\r
+ WBOUWgoj6kqv/Ii00RXBm7GljViBFXwaD/d2IZYhiMjbKWgreUhlb3OAFz4CHZz6aGWHd4hTnHF\r
+ cWWjeBAeJKpKbAMMQy3HEO45Mfecfl3qNSyuB5iRtt/XP43hK5w3nK+4rcBNa7FZQIsQwyDMdFF\r
+ qxh9UZb+6HtIlr1wF8SHFM6JPsVGueDiMzej8W21sAwxDPYHMQy3nFNfCmltrKuWRDrhv0HlUMW\r
+ V8Rrn1JsGyxDDYLS1XA927ZZWooL+MzC29sycuHmNCq3AMsQwfGt8CFHISwEqskbDWwPlkeVgGW\r
+ JxczH20lidTXe3AixDLCwsLCwsviX4P68/8cprSSqmAAAAAElFTkSuQmCC\r
+ROLE:Developer\r
+SOUND;ENCODING=b:UklGRiQgAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAgAA\r
+ B/f39/f39/f39/f39/f4B/gH+Af4CAgIGBgYGBgYGBgYGBgYGBgYGBgIGAgICBgYGBgYGBgYCAg\r
+ YGAgICAgICAgIGBgICAgYCAgICBgICAgICAgICAgIGAgICAgYCAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAf4CAgICAgICAgICAgICAgIB/f4CAgICAgICAgIB/f4B/f39/f39/f3+AgICAf4CAf3\r
+ 9/gIB/f4CAgH9/gICAgH+AgICAgICAgICAf4CAgIB/gICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgIB/gICAf39/f39/f3+Af4CAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgH9/f39/f39/f39/f39/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgIGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgYCAgICB\r
+ gICAgYGBgICAgICAgICAgIGAgICBgICBgYGBgYGBgYGAgYGBgYCBgYGBgYGBgYGBgYGBgYGBgYG\r
+ BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgICAgYGBgIGBgYGAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYGAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgH+AgICAgICAgICAgICAgICAgICAf4CAgIB/gH+Af4B/f39/f39/f\r
+ 39/f39/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgYG\r
+ BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgY\r
+ GBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGAgIGBgYCAgICBgYCAgYGBgYCAgICBg\r
+ YCAgICAgICAgICAgICAgIGAgICAgICAgICAgICAgYGBgYGAgICAgIGBgYCAgICBgYGAgICAgIGA\r
+ gICAgICAgIGBgYCBgYGAgYGBgYGBgoGBgYGCgoGAgICBgYGBgYCBgYKCgYGBgYGBgYGAgYGBgH9\r
+ /gIB/f3+AgIB/gH9/gICAf4CBgYB/f35+gIB/f4CBgX9/gYCAgYKCgYCAgYOBgICAgoGAf4GCgn\r
+ 9/gIB/gICBgH5/f4GAfX+BgYB+fn9/fn+Afn6Af39+f35+fn9+fn6AgH9/f4CAgYCAgYKCgH9/g\r
+ IKBf3+AgYJ/f4GCg4F/gIGCgoOBfoCDgYF/fH+BgYGAfn6Af4CCgn58gIN/fX+AgICBgYGAgYCB\r
+ goOCgICBgYB/fXyBhIGAgH5+f4GBgX9/f35+gIKCgXt9goN/f318f4GAf4B/fHt9fn5+fXx+fn5\r
+ /gH9+fHx/gHx9f4CAfn18gIB9e4GBfn5+foCBfHyAgoGAfX6CgYCBgH+DgHt9gYN/fX2BhYB7en\r
+ 6AgH98foKCgH18foCAfoCAf4CAf31+fn+AgH1+goJ/fX5/gIKAgIGAfH6CgX17foCBf3+BgoB/g\r
+ YOBgIB/f4KDgoJ+e3+ChIOBfn1/gX+BfX2BgH59foCCfn1/gX99fICAgoB/f39/gISCf36AgoSB\r
+ foCBgYGAf4CCf4CCgX5/gYKDgYB/gH+BhIOBgYB/gIOEhIF9foGCg4OBgICBgoOCgICChIOBg4O\r
+ CgoSDgoKAgYKBgoODhISBf4CCg4SEgoGCg4SEgH6BhIWEgX+EhYKAgoKFhIB+gIKChYKAgYOCgo\r
+ B+gYWFhIKAg4WEg4ODhIWFhYaGhIWGhYWGh4eHhYOCgoSFg4B/gIB+fX1+fn57e3t7fHt6fH5/f\r
+ 4GDhYiMkJGQj5GVmZycmZiXlZGIfXRwcnZ4d3Nva2hoamppaWxvc3R0dXl+iJCPjZShqrCzvNHi\r
+ rFYxOmKSrJ6UmJOAbV1ZboSMjI6JfnNjW2JweYGFg4B/e3Z0c3Btc3p9fHp4fIGBf3+Dh4qLjJG\r
+ YnZuWkY6MhnpwbnN6fHlya2hlZmdmaW9ycXFzd3t/g42cpquzvMzd/sRADiBZkL6xpLa1jFpANV\r
+ iHoJ2ioJWGbEQ2VHaOm5aHhIFuYmZzgoVxZFxfdJCSgnt/kKewr7bDsopcSV6JopyRjpOQfmNXZ\r
+ HmGhYB8fXtvXExKXnyKgnJsc3t8fZC0ysfK0+vHTxEeXY6yr6rAxJRSNTdbh5+fp7KjglQ6PVx3\r
+ ho6Ph3dmW15oa2x0hIuNkK3R18jJ0Ys8LUlxkqais8WtdlBFT2yDjpywro9mS0VVaXaBiIdyX2J\r
+ rcG5udYKKjaXT4dfV2Y82JkBpiaKkvdO2eE9GTGZ4hZu6u51yTUNOX2l9kZWKdl5PXG93dHeAip\r
+ KZut7j2tacRjJIYHCOo77XvoRdVlFYZniVuL+kfl9ST1Rcb4mTjoNxW1pqdXd5fH6CiKHN4d7k2\r
+ 4E4N05Yaoqo1eO0emVbTU5ZdKDCuZV9b1tMSVlyiYp8dHl7cmpqdYCCgIqixNTR2OqnRTlPXFt3\r
+ ksbnw4lzcFdKS2KMtLWZiIZwV0lPZXZ1b3uNinZqbXJ3dX2XxOPg4/zDUDRCTUhmiMXx1p+If1g\r
+ +O1J7pK+yspN3W0pGVGdugYmCen54b21rb3V9i6vZ7vD92WpAS0gzSny04tq3qp5qPDhHX3uVrs\r
+ KukXVhT0hRYnF/gnuCh31ubG9zdHeMs+Dr8P7KalFVOylJeq7QzMPIqmtIRUZOY4KovqicknhZS\r
+ 1BWXWh6gIeLiH91b21vc4GcyuXt/umTZGhGIjJfh6y+yt7LkGlfTD1GYYOipKKwnXhkXldVUFhs\r
+ dYOMjYWBem9ucoCWvNTm9+2weHdZMSpQboujvtnUrY6BZEdDUmV4jZypppeFd2dcWVFWWWh8hYS\r
+ Ii393c3Z7iJ3A1ePp3aGDfVYyOVZkfJe5zsavpJNsU1BTVmR9kpmcn5uGdGxoWU5VVGZ0fIKLh4\r
+ F/d3Z6g4ufuc3e3depjoViPz9VWm2FqLu8tLGifmphWlJZbH+Lj5+elod+c19WU0xUZW11gISEg\r
+ nx6e3h6g4qWp7zM3NrSpZGIYkRFVVVlfZ6vsbW3qIl7b2BUWGVwe4ybn52TkIRuX2BVRVFjZmp7\r
+ hIF9fX12cnZ6eX6LlZyousfP0c++jot6X0ZRXF9sgaCnqa6yn4eAdGZZYGlvd4mcnqCWnY91aWN\r
+ cSUtYYmJxf399f351cXFzcHF4foOIlZ+hp7CytLm8uLKNg4RtYF5oanN8j5qZnqGaioV9dGpqb3\r
+ J1f42Rk5iZjIKAcmFcW1RXYGhwdXt+e3Z2c25tcXJxd3yChY2Tl5udn6Gho6ioq6uonoaHbWNzZ\r
+ GdxgoGPlZqdj5CJgHFzcWxwdH1/hYqOioSFfnFpaWBdX2Jqb3N5fHh4eHNxcXFxc3d7foOJjI6Q\r
+ lZOSlZOTl5eYnJucnpyZkYiEfnd1dnZ5fYKHio6QkIyHg394dHRzc3Z5fHt2enJrcG5obG9sbW9\r
+ ucHJzdnh3eXp5eXt9foCDhoeJjI6OkJGSk5aYmZudn6Gjn5qLh4J3cnJ0dHR5g4GEi42Ih4aBfH\r
+ h4dXR1eHl4dXhuaW5rZ2ltaWttbW5vcnR1dHZ2dXV2d3d7fn+BhIeIiYuOjpCTlJWZnJueoKarr\r
+ ZuJhntvaW1ucnh+hIWJi4qFg4F7d3V1c3R3fH1/gHlvbXJnXmdpYmdtamtxcW9vbm9tamxtbG5z\r
+ dXZ5fHx7fX9+fH6AgoWHioyNj5CRlZeYmp2TiYyGenp9eXh9fX19foB/fH1/e3p8e3l5e3x7e3x\r
+ 6d3FxcnBwcG9vb29vb29wb25tbWxsbW5vb3Fyc3N1dXV1dnd4ent9fn+BgoGAgoWEhoqJiYyLi4\r
+ yPioaLiYSEh4WDhIOCgoKAgX58fXx5eXp4eHl4dXZ1cnN0dHR1cnJycXBxcnJycW9ubGxsbW1ub\r
+ m5ub29vcXNzdHZ2dXh6enp+fn5/gYCBhIKFiYeGiomJi4qKjIuLjIyLiomKh4aKioeIiIWDgYB9\r
+ fHx7e3l4eHh2dnd3d3l7enp6enp5ent7e3x7enl6e3t7fHx7e3p8fX5/gIKDg4GCg4KCg4OEhoe\r
+ Hh4iJiomLjIqLi4uLi46Pjo2OjouJiomHh4aDgH9/fXp5eXh3d3h4eHh4d3V2d3Z0dnd0dXd3d3\r
+ p7fH59fX19fH5+fn+Af4CAf3+Af359f4KDgoSIiIeJjI6PkJOUk5SYmZiYmZqZmJqamJiZmJaWl\r
+ ZSTkZCQj46Ni4qHh4iFgoKBfn9+fn+AgYCAgYOCgoSEg4SFhIODg4OCgICBf35/gICAgYKCgYGD\r
+ hIOBgYOCgYGAgIGBgIGCgoKBgoSEg4KCgH9/f318fHt6e3t7enl5eHh5fH18foCAf4GDg4OFhoa\r
+ GhoeJh4aJioiIiYiIhoWGh4aGh4eHiImHh4mIiIeHh4iIiIqJioqJh4mKh4eJiYiHh4eHhoWGhY\r
+ OEhYWEhYWDg4SFhYWEg4SDgYKCgH+AgICBgoOCgYGBgYKEhoaGhoiIh4eJiYmKioqKiomKioiJi\r
+ oqJiYmJiIiIiYiHh4eHh4eGhYWFhISDgoKCgoKCgYGAf31+fXx9fn58e3x8fHx9fX19fX19fX19\r
+ fHx9fn5/gICBgYGCgoKCg4OCgoODgoKDg4OCgoKCgoGBgYGBgYGCgYCBgIGCgoKDg4KCg4OEhIS\r
+ FhYWFhYWFhYWFhoWGhoWEhISDg4ODg4ODg4ODg4SDgoKDg4OCg4SDgoKDgoGBgoGAgIB/f35+fn\r
+ 9+fn9/f35+fn5/gICAgIGBgYKCg4ODg4SEg4OEhIODhIODhIODg4ODg4OCgoKBgoKCg4OCgoODg\r
+ 4ODg4OEhISEhIWEhIWFhISEhISEhIWEhISEhIODhISEg4ODg4ODg4ODg4ODgoKCgoKCg4KCgoKC\r
+ goKCgoKCgoOCgoKCgoKBgYKCgoKCgoGBgYGAgICAgYGBgYCAf39/f39/f3+Af39/f39/f39/gIC\r
+ AgIB/f39/f39/gICAgIB/f39/gIB/f39/gH9/f39/f39/fn5+f39/f39/fn5+fn5/f39/f39/fn\r
+ 5+fn9/f39/f35+fn5+fn9/f35/f39+fn5+fn9/fn5+fn5+fn5+f39+fn5+fn19fX5+fn5+fn5+f\r
+ X5+fn5+fn5+fn19fn5+fn5+fX19fX59fn5+fn19fX19fX1+fX19fX19fX19fX19fX19fX19fX19\r
+ fX19fX19fX19fX19fX19fX19fX19fX19fXx9fX18fH19fXx8fXx9fXx8fHx8fHx8fH19fX19fXx\r
+ 9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX\r
+ 19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f\r
+ X19fX19fX19fX19fX19fX19fX19fX19fX1+fX5+fn19fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+\r
+ fn5+fn5+fn5+fn5+f39/fn5/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39\r
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f3\r
+ 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f\r
+ 39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/\r
+ f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f39/f39/f39/f39/f39/f39\r
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3\r
+ 9/f39/f39/f39/f39/f39/f35+fn5+f35/f39/f39/f39/f39/f39/f39/f39/f39/f39/f35+f\r
+ 39+fn5/fn5+f39/fn9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/\r
+ f39/f39/f39/f39/f39+fn9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39\r
+ /f39/f39/f39/f39/f39/f39/f39/f4CAf39/f39/f39/gIB/f39/f39/f4B/f4CAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgIB/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgIGBgYGAgYGBgICAgIGAgICBgYGAgYGBgYGBgYGBgYGBgY\r
+ GBgYGBgYGBgYGBgYGBgYGAgYCAgICAgICAgICAgICAgICAgICAgICAgICAgH+AgICAf39/gH9/f\r
+ 3+Af39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f4CAgH9/gIB/f39/f4B/f3+AgICAf4CA\r
+ gICAgICAgICAgICAgH9/f4CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgIB/f39/f39/f39/f4CAf39/f39/f39/f39/f39/f39+fn\r
+ 9/fn5+fn5+fn5+fn5+fX19fX19fX19fX19fX19fX19fn5+fn5+fn9/f39/f4CAgICAgICBgYGBg\r
+ YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICAgICAgICAgICAgICAgYGAgICA\r
+ gICAgICAgICAgICBgYGAgICAgICAgICAgICAgICAgICAgICAgIB/gICAgH9/f39/f39/f39/f39\r
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAgICAf39/f39/gH+AgI\r
+ CAgICAgIB/f39/f39/f4B/f39/f39/f39/f39/f39/f39/f39/f39/f39/fn9/f39/f39/f39+f\r
+ 39/f39/f39/f39/f39/f39/f39/f4B/f4CAgICAf39/f39/gICAf39/gICAgICAgICAgICAgICA\r
+ gICAgIGAgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYCAgICBgYGBgYGBgYCBgYGBgYCAgIG\r
+ BgICAgICAgICAgICAgICAgICAgICAgICAgICBgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAf3+AgIB/f4CAf39/f39/f3+AgICAgH+AgICAf3+AgICAgICAgH9/f39/f39\r
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3\r
+ 9/f39/f39/gH9/gICAgICAgICAgIB/gICAgH+AgIB/f39/f39/gICAgICAgH9/f3+AgH9/f4B/f\r
+ 39/f39/f39/f39/f3+AgICAgICAf3+AgIB/f4CAgIB/f4CAgICAgICAgICAf39/f3+AgIB/f4CA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAf39/f39\r
+ /f4CAgH9/f39/f39/f39/f39/f39/f39/f39/f39/f4B/f39/f39/gIB/f39/f39/f39/gH9/f3\r
+ 9/f4CAgICAgIB/gICAgICAgICAgICAgICAgICAgICAgICAgH+AgICAf4CAgICAgICAgIB/gICAf\r
+ 4CAgICAgH+Af4CAgH9/f39/f39/f39/gICAgICAgICAgICAf39/f39/f4CAgH9/gICAf3+AgICA\r
+ gICAgICAgICAgICAgICAgICAgIB/f39/gIB/f4CAgIB/f4CAgH9/gICAf39/f39/f39/f39/f39\r
+ /f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3\r
+ 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/gICAf39/gH9/f3+AgH9/f39/f39/f39/f39/f\r
+ 39/f39/f39/f39/f39/f39/f39/gICAf3+AgH9/f3+AgH9/f4CAgH9/gICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAgICAgICBgYGBgIGBgYCAgYGAgICAgYCAgICAgICAgICAgICAgICAg\r
+ ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA\r
+ gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC\r
+ AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI\r
+ CAgICAgICAgICAgICAgICAf39/f39/f4CAgIB/f4CAgICAgICAgICAgICAgICAgICAgICAgICAg\r
+ ICAf39/f3+Af39/f39/f39/f4B/f39/f4CAgICAgICAgICAgA==\r
+TEL;TYPE=HOME:0351 223344\r
+TEL;TYPE=WORK:0351 443322\r
+TEL;TYPE=CELL:0173 223344\r
+UID:P9HXg34Oav\r
+URL:http://www.mustermann.de/~heiko/\r
+X-KADDRESSBOOK-X-Anniversary:1982-07-07\r
+X-KADDRESSBOOK-X-AssistantsName:Mr. Smithers\r
+X-KADDRESSBOOK-X-Department:Edutainment\r
+X-KADDRESSBOOK-X-IMAddress:122334455\r
+X-KADDRESSBOOK-X-ManagersName:Mr. Burnes\r
+X-KADDRESSBOOK-X-Office:223 2nd stage\r
+X-KADDRESSBOOK-X-Profession:Clown\r
+X-KADDRESSBOOK-X-SpousesName:femal clown\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard6.vcf b/autotests/data/vcard6.vcf
new file mode 100644 (file)
index 0000000..92339c7
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN:VCARD
+CLASS:PUBLIC
+EMAIL;TYPE=PREF:email1@abc.de
+EMAIL:email2@abc.de
+FN:Test User
+N:User;Test;;;
+UID:uJTkVqH5Qt
+VERSION:3.0
+END:VCARD
+
diff --git a/autotests/data/vcard6.vcf.2_1ref b/autotests/data/vcard6.vcf.2_1ref
new file mode 100644 (file)
index 0000000..d5a5a9b
--- /dev/null
@@ -0,0 +1,9 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+EMAIL;PREF:email1@abc.de\r
+EMAIL:email2@abc.de\r
+FN:Test User\r
+N:User;Test;;;\r
+UID:uJTkVqH5Qt\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard6.vcf.ref b/autotests/data/vcard6.vcf.ref
new file mode 100644 (file)
index 0000000..712afe7
--- /dev/null
@@ -0,0 +1,10 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+CLASS:PUBLIC\r
+EMAIL;TYPE=PREF:email1@abc.de\r
+EMAIL:email2@abc.de\r
+FN:Test User\r
+N:User;Test;;;\r
+UID:uJTkVqH5Qt\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard7.vcf b/autotests/data/vcard7.vcf
new file mode 100644 (file)
index 0000000..8b5be8e
--- /dev/null
@@ -0,0 +1,7 @@
+BEGIN:VCARD
+N:
+LABEL;QUOTED-PRINTABLE;WORK:401 Park Drive  3 West=0ABoston, MA 02215=0AUSA=0A=
+Herecomesyetanotherline
+UID:pas-id-3E136F9B00000000
+END:VCARD
+
diff --git a/autotests/data/vcard7.vcf.2_1ref b/autotests/data/vcard7.vcf.2_1ref
new file mode 100644 (file)
index 0000000..5d4e702
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN:VCARD\r
+VERSION:2.1\r
+LABEL;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE;WORK:401 Park Drive  3 West\n\r
+ Boston\, MA 02215\nUSA\nHerecomesyetanotherline\r
+N:;;;;\r
+UID:pas-id-3E136F9B00000000\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard7.vcf.ref b/autotests/data/vcard7.vcf.ref
new file mode 100644 (file)
index 0000000..b099b98
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+LABEL;TYPE=work:401 Park Drive  3 West\nBoston\, MA 02215\nUSA\nHerecomesye\r
+ tanotherline\r
+N:;;;;\r
+UID:pas-id-3E136F9B00000000\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard8.vcf b/autotests/data/vcard8.vcf
new file mode 100644 (file)
index 0000000..eff34b8
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN:VCARD\r
+FN:test\r
+N:test;;;;\r
+NOTE:line one\Nline two\Nline three\N\r
+UID:8OD165Pope\r
+VERSION:3.0\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard8.vcf.ref b/autotests/data/vcard8.vcf.ref
new file mode 100644 (file)
index 0000000..54f7dbc
--- /dev/null
@@ -0,0 +1,8 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+FN:test\r
+N:test;;;;\r
+NOTE:line one\nline two\nline three\n\r
+UID:8OD165Pope\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard9.vcf b/autotests/data/vcard9.vcf
new file mode 100644 (file)
index 0000000..c38153b
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:VCARD\r
+FN:βγδεαβγδ1βγδεαβγδ1βγδεαβγδ1βγδεαβγδ1βγ\r
+ δεαβγδ1βγδεαβγδ1βγδεαβγδ1βγδεαβγδ1βγδεα\r
+ βγδ1βγδεαβγδ1βγδεαβγδ1\r
+N:123ㄓㄕㄖbcdeαβγδαβγδαβγδαβγδαβγδαβγδαβㄤ\r
+ ㄓㄕㄖ;;;;\r
+NOTE:Длинный комментарий на русском языке\,\r
+  Ñ\87Ñ\82обÑ\8b Ð² Ñ\80езÑ\83лÑ\8cÑ\82аÑ\82е Ð¿Ð¾Ð»Ñ\83Ñ\87илаÑ\81Ñ\8c Ñ\81Ñ\82Ñ\80ока Ð±Ð¾Ð\r
+ »ÐµÐµ 70 Ñ\81имволов.\r
+PRODID:-//ownCloud//NONSGML Contacts 0.3//EN\r
+UID:20140316T155019.877e370c11@martin\r
+VERSION:3.0\r
+END:VCARD\r
+\r
diff --git a/autotests/data/vcard9.vcf.ref b/autotests/data/vcard9.vcf.ref
new file mode 100644 (file)
index 0000000..cbb9ed8
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN:VCARD\r
+VERSION:3.0\r
+FN:βγδεαβγδ1βγδεαβγδ1βγδεαβγδ1βγδεαβγδ1βγ\r
+ δεαβγδ1βγδεαβγδ1βγδεαβγδ1βγδεαβγδ1βγδεα\r
+ βγδ1βγδεαβγδ1βγδεαβγδ1\r
+N:123ㄓㄕㄖbcdeαβγδαβγδαβγδαβγδαβγδαβγδαβㄤ\r
+ ㄓㄕㄖ;;;;\r
+NOTE:Длинный комментарий на русском языке\,\r
+  чтобы в результате получилась строка бо\r
+ лее 70 символов.\r
+PRODID:-//ownCloud//NONSGML Contacts 0.3//EN\r
+UID:20140316T155019.877e370c11@martin\r
+END:VCARD\r
+\r
diff --git a/autotests/emailtest.cpp b/autotests/emailtest.cpp
new file mode 100644 (file)
index 0000000..f8e5374
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+#include "emailtest.h"
+#include "email.h"
+#include <qtest.h>
+#include "vcardtool.h"
+
+EmailTest::EmailTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+EmailTest::~EmailTest()
+{
+
+}
+
+void EmailTest::shouldHaveDefaultValue()
+{
+    KContacts::Email email;
+    QVERIFY(!email.isValid());
+    QVERIFY(email.mail().isEmpty());
+    QVERIFY(email.parameters().isEmpty());
+}
+
+void EmailTest::shouldAssignValue()
+{
+    const QString mail(QStringLiteral("foo@kde.org"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Email email(mail);
+    email.setParameters(params);
+    QVERIFY(email.isValid());
+    QVERIFY(!email.mail().isEmpty());
+    QCOMPARE(email.mail(), mail);
+    QVERIFY(!email.parameters().isEmpty());
+    QCOMPARE(email.parameters(), params);
+}
+
+void EmailTest::shouldAssignExternal()
+{
+    KContacts::Email email;
+    const QString mail(QStringLiteral("foo@kde.org"));
+    email.setEmail(mail);
+    QVERIFY(email.isValid());
+    QVERIFY(!email.mail().isEmpty());
+    QCOMPARE(email.mail(), mail);
+}
+
+void EmailTest::shouldSerialized()
+{
+    KContacts::Email email;
+    KContacts::Email result;
+    const QString mail(QStringLiteral("foo@kde.org"));
+    email.setEmail(mail);
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    email.setParameters(params);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << email;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> result;
+
+    QVERIFY(email == result);
+}
+
+void EmailTest::shouldEqualEmail()
+{
+    KContacts::Email email;
+    KContacts::Email result;
+    const QString mail(QStringLiteral("foo@kde.org"));
+    email.setEmail(mail);
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    email.setParameters(params);
+
+    result = email;
+    QVERIFY(email == result);
+}
+
+void EmailTest::shouldParseEmailVCard()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "EMAIL;TYPE=HOME,PREF;X-EVOLUTION-UI-SLOT=2:foo@foo.com\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(!lst.at(0).emailList().isEmpty());
+    QCOMPARE(lst.at(0).emailList().count(), 1);
+    KContacts::Email email = lst.at(0).emailList().at(0);
+    QCOMPARE(email.mail(), QStringLiteral("foo@foo.com"));
+    QCOMPARE(email.parameters().count(), 2);
+}
+
+void EmailTest::shouldParseEmailVCardWithMultiEmails()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "EMAIL;TYPE=HOME,PREF;X-EVOLUTION-UI-SLOT=2:foo@foo.com\n"
+                         "EMAIL;TYPE=HOME,PREF;X-EVOLUTION-UI-SLOT=2:bla@bla.com\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(!lst.at(0).emailList().isEmpty());
+    QCOMPARE(lst.at(0).emailList().count(), 2);
+    KContacts::Email email = lst.at(0).emailList().at(0);
+    QCOMPARE(email.mail(), QStringLiteral("bla@bla.com"));
+    email = lst.at(0).emailList().at(1);
+    QCOMPARE(email.mail(), QStringLiteral("foo@foo.com"));
+
+    QCOMPARE(email.parameters().count(), 2);
+}
+
+void EmailTest::shouldParseEmailVCardWithoutEmail()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(lst.at(0).emailList().isEmpty());
+}
+
+void EmailTest::shouldExportVcard()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org") << QStringLiteral("bla@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "EMAIL:bla@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+QTEST_MAIN(EmailTest)
diff --git a/autotests/emailtest.h b/autotests/emailtest.h
new file mode 100644 (file)
index 0000000..3974fa1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef EMAILTEST_H
+#define EMAILTEST_H
+
+#include <QObject>
+
+class EmailTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit EmailTest(QObject *parent = Q_NULLPTR);
+    ~EmailTest();
+
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldAssignExternal();
+    void shouldSerialized();
+    void shouldEqualEmail();
+    void shouldParseEmailVCard();
+    void shouldParseEmailVCardWithoutEmail();
+    void shouldParseEmailVCardWithMultiEmails();
+    void shouldExportVcard();
+};
+
+#endif // EMAILTEST_H
diff --git a/autotests/gendertest.cpp b/autotests/gendertest.cpp
new file mode 100644 (file)
index 0000000..b553d5d
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "gendertest.h"
+#include "gender.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+GenderTest::GenderTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+GenderTest::~GenderTest()
+{
+
+}
+
+void GenderTest::shouldHaveDefaultValue()
+{
+    KContacts::Gender gender;
+    QVERIFY(!gender.isValid());
+    QVERIFY(gender.gender().isEmpty());
+    QVERIFY(gender.comment().isEmpty());
+}
+
+void GenderTest::shouldAssignValue()
+{
+    const QString genderStr(QStringLiteral("F"));
+    KContacts::Gender gender(genderStr);
+    const QString commentStr(QStringLiteral("foo"));
+    gender.setComment(commentStr);
+    QVERIFY(gender.isValid());
+    QVERIFY(!gender.gender().isEmpty());
+    QCOMPARE(gender.gender(), genderStr);
+    QVERIFY(!gender.comment().isEmpty());
+    QCOMPARE(gender.comment(), commentStr);
+}
+
+void GenderTest::shouldAssignExternal()
+{
+    KContacts::Gender gender;
+    const QString genderStr(QStringLiteral("H"));
+    gender.setGender(genderStr);
+    QVERIFY(gender.isValid());
+    QVERIFY(!gender.gender().isEmpty());
+    QCOMPARE(gender.gender(), genderStr);
+}
+
+void GenderTest::shouldSerialized()
+{
+    KContacts::Gender gender;
+    KContacts::Gender result;
+    const QString genderStr(QStringLiteral("H"));
+    gender.setGender(genderStr);
+    gender.setComment(QStringLiteral("foo"));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << gender;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> result;
+
+    QVERIFY(gender == result);
+}
+
+void GenderTest::shouldEqualGender()
+{
+    KContacts::Gender gender;
+    KContacts::Gender result;
+    const QString genderStr(QStringLiteral("H"));
+    gender.setGender(genderStr);
+    gender.setComment(QStringLiteral("foo"));
+
+    result = gender;
+    QVERIFY(gender == result);
+}
+
+void GenderTest::shouldParseGender_data()
+{
+    QTest::addColumn<QByteArray>("vcarddata");
+    QTest::addColumn<QString>("genre");
+    QTest::addColumn<QString>("comment");
+    QTest::addColumn<bool>("hasGender");
+
+    QByteArray str("BEGIN:VCARD\n"
+                   "VERSION:3.0\n"
+                   "N:LastName;FirstName;;;\n"
+                   "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                   "REV:2015-03-14T09:24:45+00:00\n"
+                   "FN:FirstName LastName\n"
+                   "END:VCARD\n");
+    QTest::newRow("nogender") << str << QString() << QString() << false;
+
+    str = QByteArray("BEGIN:VCARD\n"
+                     "VERSION:3.0\n"
+                     "N:LastName;FirstName;;;\n"
+                     "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                     "REV:2015-03-14T09:24:45+00:00\n"
+                     "FN:FirstName LastName\n"
+                     "GENDER:H\n"
+                     "END:VCARD\n");
+    QTest::newRow("hasgenderbutnocomment") << str << QStringLiteral("H") << QString() << true;
+
+    str = QByteArray("BEGIN:VCARD\n"
+                     "VERSION:3.0\n"
+                     "N:LastName;FirstName;;;\n"
+                     "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                     "REV:2015-03-14T09:24:45+00:00\n"
+                     "FN:FirstName LastName\n"
+                     "GENDER:;foo\n"
+                     "END:VCARD\n");
+    QTest::newRow("hasgenderbutnotypebutcomment") << str << QString() << QStringLiteral("foo") << true;
+
+    str = QByteArray("BEGIN:VCARD\n"
+                     "VERSION:3.0\n"
+                     "N:LastName;FirstName;;;\n"
+                     "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                     "REV:2015-03-14T09:24:45+00:00\n"
+                     "FN:FirstName LastName\n"
+                     "GENDER:H;foo\n"
+                     "END:VCARD\n");
+    QTest::newRow("hasgendertypeandcomment") << str << QStringLiteral("H") << QStringLiteral("foo") << true;
+}
+
+void GenderTest::shouldParseGender()
+{
+    QFETCH(QByteArray, vcarddata);
+    QFETCH(QString, genre);
+    QFETCH(QString, comment);
+    QFETCH(bool, hasGender);
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).gender().isValid(), hasGender);
+    QCOMPARE(lst.at(0).gender().comment(), comment);
+    QCOMPARE(lst.at(0).gender().gender(), genre);
+}
+
+QByteArray GenderTest::createCard(const QByteArray &gender)
+{
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n");
+    if (!gender.isEmpty()) {
+        expected += gender + "\r\n";
+    }
+    expected += QByteArray(
+                    "N:;;;;\r\n"
+                    "UID:testuid\r\n"
+                    "END:VCARD\r\n\r\n");
+    return expected;
+}
+
+void GenderTest::shouldExportEmptyGender()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected = createCard(QByteArray());
+    QCOMPARE(ba, expected);
+}
+
+void GenderTest::shouldExportOnlyGenderWithoutCommentGender()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Gender gender;
+    gender.setGender(QStringLiteral("H"));
+    addr.setGender(gender);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected = createCard(QByteArray("GENDER:H"));
+    QCOMPARE(ba, expected);
+}
+
+void GenderTest::shouldExportOnlyGenderWithCommentGender()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Gender gender;
+    gender.setGender(QStringLiteral("H"));
+    gender.setComment(QStringLiteral("comment"));
+    addr.setGender(gender);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected = createCard(QByteArray("GENDER:H;comment"));
+    QCOMPARE(ba, expected);
+}
+
+void GenderTest::shouldExportOnlyGenderWithoutTypeCommentGender()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Gender gender;
+    gender.setComment(QStringLiteral("comment"));
+    addr.setGender(gender);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected = createCard(QByteArray("GENDER:;comment"));
+    QCOMPARE(ba, expected);
+}
+
+void GenderTest::shouldNotExportInVcard3()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Gender gender;
+    gender.setComment(QStringLiteral("comment"));
+    addr.setGender(gender);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:3.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+QTEST_MAIN(GenderTest)
diff --git a/autotests/gendertest.h b/autotests/gendertest.h
new file mode 100644 (file)
index 0000000..2afed5b
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef GENDERTEST_H
+#define GENDERTEST_H
+
+#include <QObject>
+
+class GenderTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit GenderTest(QObject *parent = Q_NULLPTR);
+    ~GenderTest();
+
+private:
+    QByteArray createCard(const QByteArray &gender);
+
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldAssignExternal();
+    void shouldSerialized();
+    void shouldEqualGender();
+    void shouldParseGender();
+    void shouldParseGender_data();
+    void shouldExportOnlyGenderWithoutCommentGender();
+    void shouldExportEmptyGender();
+    void shouldExportOnlyGenderWithCommentGender();
+    void shouldExportOnlyGenderWithoutTypeCommentGender();
+    void shouldNotExportInVcard3();
+};
+
+#endif // GENDERTEST_H
diff --git a/autotests/geotest.cpp b/autotests/geotest.cpp
new file mode 100644 (file)
index 0000000..c79d868
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "geotest.h"
+#include "kcontacts/geo.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+QTEST_MAIN(GeoTest)
+
+void GeoTest::constructor()
+{
+    KContacts::Geo geo(1.2f, 3.4f);
+
+    QVERIFY((float)geo.latitude() == (float)1.2);
+    QVERIFY((float)geo.longitude() == (float)3.4);
+}
+
+void GeoTest::isValid()
+{
+    KContacts::Geo geo;
+
+    QVERIFY(!geo.isValid());
+
+    geo.setLatitude(23);
+
+    QVERIFY(!geo.isValid());
+
+    geo.setLongitude(45);
+
+    QVERIFY(geo.isValid());
+
+    geo.clear();
+
+    QVERIFY(!geo.isValid());
+    QVERIFY(geo == KContacts::Geo());
+}
+
+void GeoTest::setData()
+{
+    KContacts::Geo geo;
+
+    geo.setLatitude(22.5f);
+    geo.setLongitude(45.1f);
+
+    QVERIFY((float)geo.latitude() == (float)22.5);
+    QVERIFY((float)geo.longitude() == (float)45.1);
+}
+
+void GeoTest::equals()
+{
+    KContacts::Geo geo1(22.5f, 33.7f);
+    KContacts::Geo geo2(22.5f, 33.7f);
+
+    QVERIFY(geo1 == geo2);
+}
+
+void GeoTest::differs()
+{
+    KContacts::Geo geo1(22.5f, 33.7f);
+    KContacts::Geo geo2(22.5f, 33.6f);
+
+    QVERIFY(geo1 != geo2);
+}
+
+void GeoTest::serialization()
+{
+    KContacts::Geo geo1(22.5f, 33.7f);
+    QByteArray data;
+
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << geo1;
+
+    KContacts::Geo geo2;
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> geo2;
+
+    QVERIFY(geo1 == geo2);
+}
+
+void GeoTest::shouldParseGeoVCard3()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "GEO:22.5;180.0\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(lst.at(0).geo().isValid());
+    KContacts::Geo geo = lst.at(0).geo();
+    QCOMPARE(geo.latitude(), 22.5);
+    QCOMPARE(geo.longitude(), 180.0);
+}
+
+void GeoTest::shouldParseGeoVCard4()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:4.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "GEO:geo:22.5,180.0\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(lst.at(0).geo().isValid());
+    KContacts::Geo geo = lst.at(0).geo();
+    QCOMPARE(geo.latitude(), 22.5);
+    QCOMPARE(geo.longitude(), 180.0);
+}
+
+void GeoTest::shouldGenerateVCard3()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+
+    KContacts::Geo geo;
+    geo.setLongitude(180.0);
+    geo.setLatitude(22.5);
+    addr.setGeo(geo);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:3.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "GEO:22.500000;180.000000\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+
+}
+
+void GeoTest::shouldGenerateVCard4()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+
+    KContacts::Geo geo;
+    geo.setLongitude(180.0);
+    geo.setLatitude(22.5);
+    addr.setGeo(geo);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "GEO:geo:22.500000,180.000000\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+void GeoTest::shouldGenerateWithoutGeo()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
diff --git a/autotests/geotest.h b/autotests/geotest.h
new file mode 100644 (file)
index 0000000..25a4e47
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef GEOTEST_H
+#define GEOTEST_H
+
+#include <QtCore/QObject>
+
+class GeoTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void constructor();
+    void isValid();
+    void setData();
+    void equals();
+    void differs();
+    void serialization();
+    void shouldParseGeoVCard3();
+    void shouldParseGeoVCard4();
+    void shouldGenerateVCard3();
+    void shouldGenerateVCard4();
+    void shouldGenerateWithoutGeo();
+};
+
+#endif
diff --git a/autotests/impptest.cpp b/autotests/impptest.cpp
new file mode 100644 (file)
index 0000000..72f6716
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+    This file is part of libkabc.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "impptest.h"
+#include "impp.h"
+#include <qtest.h>
+#include "vcardtool.h"
+Q_DECLARE_METATYPE(KContacts::Impp::ImppType)
+
+ImppTest::ImppTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+ImppTest::~ImppTest()
+{
+
+}
+
+void ImppTest::shouldHaveDefaultValue()
+{
+    KContacts::Impp impp;
+    QVERIFY(!impp.isValid());
+    QVERIFY(impp.address().isEmpty());
+    QCOMPARE(impp.type(), KContacts::Impp::Unknown);
+    QVERIFY(impp.parameters().isEmpty());
+}
+
+void ImppTest::shouldAssignValue()
+{
+    const QString address(QStringLiteral("address"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Impp impp;
+    impp.setParameters(params);
+    impp.setType(KContacts::Impp::Icq);
+    impp.setAddress(address);
+    QVERIFY(impp.isValid());
+    QVERIFY(!impp.address().isEmpty());
+    QCOMPARE(impp.address(), address);
+    QCOMPARE(impp.type(), KContacts::Impp::Icq);
+    QVERIFY(!impp.parameters().isEmpty());
+    QCOMPARE(impp.parameters(), params);
+}
+
+void ImppTest::shouldSerialized()
+{
+    const QString address(QStringLiteral("address"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Impp impp;
+    impp.setParameters(params);
+    impp.setType(KContacts::Impp::Icq);
+    impp.setAddress(address);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << impp;
+
+    KContacts::Impp result;
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> result;
+
+    QVERIFY(impp == result);
+}
+
+void ImppTest::shouldEqualImpp()
+{
+    const QString address(QStringLiteral("address"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Impp impp;
+    impp.setParameters(params);
+    impp.setType(KContacts::Impp::Icq);
+    impp.setAddress(address);
+
+    KContacts::Impp result(impp);
+    QVERIFY(impp == result);
+}
+
+void ImppTest::shouldParseWithoutImpp()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "LANG:fr"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).imppList().count(), 0);
+}
+
+void ImppTest::shouldParseImpp()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "IMPP;X-SERVICE-TYPE=skype:skype:xxxxxxxx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).imppList().count(), 1);
+    KContacts::Impp impp = lst.at(0).imppList().at(0);
+    QCOMPARE(impp.address(), QStringLiteral("skype:xxxxxxxx"));
+    QCOMPARE(impp.type(), KContacts::Impp::Skype);
+}
+
+QByteArray createCard(KContacts::Impp::ImppType type)
+{
+    QByteArray expected("BEGIN:VCARD\n"
+                        "VERSION:3.0\n"
+                        "EMAIL:foo@kde.org\n");
+    if (type != KContacts::Impp::Unknown) {
+        const QByteArray baType = KContacts::Impp::typeToString(type).toLatin1();
+        expected += "IMPP;X-SERVICE-TYPE=" + baType + ":" + baType + ":address\n";
+    }
+    expected += QByteArray(
+                    "N:;;;;\n"
+                    "UID:testuid\n"
+                    "END:VCARD\n\n");
+    return expected;
+}
+
+void ImppTest::shouldParseServiceType_data()
+{
+    QTest::addColumn<KContacts::Impp::ImppType>("type");
+    QTest::addColumn<bool>("hasImpp");
+    QTest::newRow("withoutimpp") << KContacts::Impp::Unknown << false;
+    QTest::newRow("skype") << KContacts::Impp::Skype << true;
+    for (int i = KContacts::Impp::Unknown + 1; i < KContacts::Impp::EndList; ++i) {
+        KContacts::Impp::ImppType type = static_cast<KContacts::Impp::ImppType>(i);
+        QTest::newRow(KContacts::Impp::typeToString(type).toLatin1().constData()) << type << true;
+    }
+}
+
+void ImppTest::shouldParseServiceType()
+{
+    QFETCH(KContacts::Impp::ImppType, type);
+    QFETCH(bool, hasImpp);
+
+    QByteArray vcarddata = createCard(type);
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+
+    QCOMPARE(!lst.at(0).imppList().isEmpty(), hasImpp);
+    if (hasImpp) {
+        KContacts::Impp impp = lst.at(0).imppList().at(0);
+        QCOMPARE(impp.address(), QStringLiteral("%1:address").arg(KContacts::Impp::typeToString(type)));
+        QCOMPARE(impp.type(), type);
+    }
+}
+
+QByteArray expectedVcard(KContacts::Impp::ImppType type)
+{
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n");
+    if (type != KContacts::Impp::Unknown) {
+        const QByteArray baType = KContacts::Impp::typeToString(type).toLatin1();
+        expected += "IMPP;X-SERVICE-TYPE=" + baType + ":address\r\n";
+    }
+
+    expected += ("N:;;;;\r\n"
+                 "UID:testuid\r\n"
+                 "END:VCARD\r\n\r\n");
+    return expected;
+}
+
+void ImppTest::shouldExportEmptyType()
+{
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QCOMPARE(ba, expected);
+}
+
+void ImppTest::shouldExportType_data()
+{
+    QTest::addColumn<KContacts::Impp::ImppType>("type");
+    for (int i = KContacts::Impp::Unknown + 1; i < KContacts::Impp::EndList; ++i) {
+        KContacts::Impp::ImppType type = static_cast<KContacts::Impp::ImppType>(i);
+        QTest::newRow(KContacts::Impp::typeToString(type).toLatin1().constData()) << type;
+    }
+}
+
+void ImppTest::shouldExportType()
+{
+    QFETCH(KContacts::Impp::ImppType, type);
+
+    QByteArray expected = expectedVcard(type);
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Impp impp;
+    impp.setAddress(QStringLiteral("address"));
+    impp.setType(type);
+    addr.insertImpp(impp);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QCOMPARE(ba, expected);
+}
+
+void ImppTest::shouldExportWithParameters()
+{
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "IMPP;FOO1=bla1,blo1;FOO2=bla2,blo2;X-SERVICE-TYPE=skype:address\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Impp impp;
+    impp.setAddress(QStringLiteral("address"));
+    impp.setType(KContacts::Impp::Skype);
+    impp.setParameters(params);
+    addr.insertImpp(impp);
+    lst << addr;
+
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QCOMPARE(ba, expected);
+}
+
+void ImppTest::shouldShouldNoyExportTwiceServiceType()
+{
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "IMPP;FOO1=bla1,blo1;FOO2=bla2,blo2;X-SERVICE-TYPE=skype:address\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    params.insert(QStringLiteral("X-SERVICE-TYPE"), QStringList() << QStringLiteral("aim"));
+    KContacts::Impp impp;
+    impp.setAddress(QStringLiteral("address"));
+    impp.setType(KContacts::Impp::Skype);
+    impp.setParameters(params);
+    addr.insertImpp(impp);
+    lst << addr;
+
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QCOMPARE(ba, expected);
+}
+
+QTEST_MAIN(ImppTest)
diff --git a/autotests/impptest.h b/autotests/impptest.h
new file mode 100644 (file)
index 0000000..388fa73
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    This file is part of libkabc.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef IMPPTEST_H
+#define IMPPTEST_H
+
+#include <QObject>
+
+class ImppTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit ImppTest(QObject *parent = Q_NULLPTR);
+    ~ImppTest();
+
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldSerialized();
+    void shouldEqualImpp();
+    void shouldParseWithoutImpp();
+    void shouldParseImpp();
+    void shouldParseServiceType_data();
+    void shouldParseServiceType();
+    void shouldExportEmptyType();
+
+    void shouldExportType_data();
+    void shouldExportType();
+
+    void shouldExportWithParameters();
+    void shouldShouldNoyExportTwiceServiceType();
+};
+
+#endif // IMPPTEST_H
diff --git a/autotests/keytest.cpp b/autotests/keytest.cpp
new file mode 100644 (file)
index 0000000..6fbeb2e
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "keytest.h"
+#include "kcontacts/key.h"
+#include <qtest.h>
+
+QTEST_MAIN(KeyTest)
+
+void KeyTest::storeTest()
+{
+    KContacts::Key key;
+
+    key.setId(QStringLiteral("My Id"));
+    key.setType(KContacts::Key::Custom);
+    key.setCustomTypeString(QStringLiteral("GnuPG"));
+    key.setTextData(QStringLiteral("That's my super secret key"));
+
+    QVERIFY(key.id() == QStringLiteral("My Id"));
+    QVERIFY(key.type() == KContacts::Key::Custom);
+    QVERIFY(key.customTypeString() == QStringLiteral("GnuPG"));
+    QVERIFY(key.textData() == QStringLiteral("That's my super secret key"));
+    QVERIFY(key.isBinary() == false);
+}
+
+void KeyTest::equalsTest()
+{
+    KContacts::Key key1, key2;
+
+    key1.setId(QStringLiteral("My Id"));
+    key1.setType(KContacts::Key::Custom);
+    key1.setCustomTypeString(QStringLiteral("GnuPG"));
+    key1.setTextData(QStringLiteral("That's my super secret key"));
+
+    key2.setId(QStringLiteral("My Id"));
+    key2.setType(KContacts::Key::Custom);
+    key2.setCustomTypeString(QStringLiteral("GnuPG"));
+    key2.setTextData(QStringLiteral("That's my super secret key"));
+
+    QVERIFY(key1 == key2);
+}
+
+void KeyTest::differsTest()
+{
+    KContacts::Key key1(QStringLiteral("TextKey"), KContacts::Key::PGP);
+    KContacts::Key key2(QStringLiteral("TextKey"), KContacts::Key::Custom);
+
+    QVERIFY(key1 != key2);
+}
+
+void KeyTest::assignmentTest()
+{
+    KContacts::Key key1, key2;
+
+    key1.setId(QStringLiteral("My Id"));
+    key1.setType(KContacts::Key::Custom);
+    key1.setCustomTypeString(QStringLiteral("GnuPG"));
+    key1.setTextData(QStringLiteral("That's my super secret key"));
+
+    key2 = key1;
+
+    QVERIFY(key1 == key2);
+}
+
+void KeyTest::serializeTest()
+{
+    KContacts::Key key1, key2;
+
+    key1.setId(QStringLiteral("My Id"));
+    key1.setType(KContacts::Key::Custom);
+    key1.setCustomTypeString(QStringLiteral("GnuPG"));
+    key1.setTextData(QStringLiteral("That's my super secret key"));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << key1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> key2;
+
+    QVERIFY(key1 == key2);
+}
diff --git a/autotests/keytest.h b/autotests/keytest.h
new file mode 100644 (file)
index 0000000..f097c75
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KEY_TEST_H
+#define KEY_TEST_H
+
+#include <QtCore/QObject>
+
+class KeyTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void storeTest();
+    void equalsTest();
+    void differsTest();
+    void assignmentTest();
+    void serializeTest();
+};
+
+#endif
diff --git a/autotests/langtest.cpp b/autotests/langtest.cpp
new file mode 100644 (file)
index 0000000..95e57e6
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "langtest.h"
+#include "lang.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+LangTest::LangTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+LangTest::~LangTest()
+{
+
+}
+
+void LangTest::shouldHaveDefaultValue()
+{
+    KContacts::Lang language;
+    QVERIFY(!language.isValid());
+    QVERIFY(language.language().isEmpty());
+    QVERIFY(language.parameters().isEmpty());
+}
+
+void LangTest::shouldAssignValue()
+{
+    const QString lang(QStringLiteral("fr"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Lang language(lang);
+    language.setParameters(params);
+    QVERIFY(language.isValid());
+    QVERIFY(!language.language().isEmpty());
+    QCOMPARE(language.language(), lang);
+    QVERIFY(!language.parameters().isEmpty());
+    QCOMPARE(language.parameters(), params);
+
+}
+
+void LangTest::shouldAssignExternal()
+{
+    KContacts::Lang language;
+    const QString lang(QStringLiteral("fr"));
+    language.setLanguage(lang);
+    QVERIFY(language.isValid());
+    QCOMPARE(language.language(), lang);
+}
+
+void LangTest::shouldSerialized()
+{
+    KContacts::Lang language;
+    KContacts::Lang result;
+    const QString lang(QStringLiteral("fr"));
+    language.setLanguage(lang);
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    language.setParameters(params);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << language;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> result;
+
+    QVERIFY(language == result);
+
+}
+
+void LangTest::shouldEqualLanguage()
+{
+    KContacts::Lang language;
+    KContacts::Lang result;
+    const QString lang(QStringLiteral("fr"));
+    language.setLanguage(lang);
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    language.setParameters(params);
+
+    result = language;
+    QVERIFY(language == result);
+}
+
+void LangTest::shouldParseLanguage()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "LANG:fr"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).langs().count(), 1);
+}
+
+void LangTest::shouldParseWithoutLanguage()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).langs().count(), 0);
+}
+
+void LangTest::shouldCreateVCard()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Lang::List lstLang;
+    KContacts::Lang lang(QStringLiteral("fr"));
+    lstLang << lang;
+    addr.setLangs(lstLang);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "LANG:fr\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void LangTest::shouldCreateVCardWithTwoLang()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Lang::List lstLang;
+    KContacts::Lang lang(QStringLiteral("fr"));
+    KContacts::Lang lang2(QStringLiteral("fr2"));
+    lstLang << lang << lang2;
+    addr.setLangs(lstLang);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "LANG:fr\r\n"
+                        "LANG:fr2\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+
+}
+
+void LangTest::shouldCreateVCardWithParameters()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Lang::List lstLang;
+    KContacts::Lang lang(QStringLiteral("fr"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    lang.setParameters(params);
+    lstLang << lang;
+    addr.setLangs(lstLang);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "LANG;FOO1=bla1,blo1;FOO2=bla2,blo2:fr\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+void LangTest::shouldNotGenerateLangForVCard3()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Lang::List lstLang;
+    KContacts::Lang lang(QStringLiteral("fr"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    lang.setParameters(params);
+    lstLang << lang;
+    addr.setLangs(lstLang);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:3.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+QTEST_MAIN(LangTest)
diff --git a/autotests/langtest.h b/autotests/langtest.h
new file mode 100644 (file)
index 0000000..97dd918
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef LANGTEST_H
+#define LANGTEST_H
+
+#include <QObject>
+
+class LangTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit LangTest(QObject *parent = Q_NULLPTR);
+    ~LangTest();
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldAssignExternal();
+    void shouldSerialized();
+    void shouldEqualLanguage();
+    void shouldParseLanguage();
+    void shouldParseWithoutLanguage();
+    void shouldCreateVCard();
+    void shouldCreateVCardWithTwoLang();
+    void shouldCreateVCardWithParameters();
+    void shouldNotGenerateLangForVCard3();
+};
+
+#endif // LANGTEST_H
diff --git a/autotests/ldifconvertertest.cpp b/autotests/ldifconvertertest.cpp
new file mode 100644 (file)
index 0000000..289e7ff
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+    This file is part of libkabc.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "ldifconvertertest.h"
+#include "converter/ldifconverter.h"
+#include <qtest.h>
+using namespace KContacts;
+
+LDifConverterTest::LDifConverterTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+LDifConverterTest::~LDifConverterTest()
+{
+
+}
+
+void LDifConverterTest::shouldImportEmail()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "mail: foo@kde.org\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).emails().count(), 1);
+    QCOMPARE(lst.at(0).emails().at(0), QStringLiteral("foo@kde.org"));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportMultiEmails()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "mail: foo@kde.org\n"
+                                 "mail: foo2@kde.org\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).emails().count(), 2);
+    QCOMPARE(lst.at(0).emails().at(0), QStringLiteral("foo@kde.org"));
+    QCOMPARE(lst.at(0).emails().at(1), QStringLiteral("foo2@kde.org"));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportStandardBirthday()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "birthyear: 2015\n"
+                                 "birthmonth: 3\n"
+                                 "birthday: 19\n"
+                                 "mail: foo@kde.org\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(lst.at(0).birthday().date().isValid());
+    QCOMPARE(lst.at(0).birthday().date(), QDate(2015, 3, 19));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportTheBatsBirthday()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "xbatBirthday: 20150319\n"
+                                 "mail: foo@kde.org\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(lst.at(0).birthday().date().isValid());
+    QCOMPARE(lst.at(0).birthday().date(), QDate(2015, 3, 19));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportTheBatsEmails()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "mail: foo@kde.org\n"
+                                 "othermailbox: foo2@kde.org\n"
+                                 "othermailbox: foo3@kde.org\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).emails().count(), 3);
+    QCOMPARE(lst.at(0).emails().at(0), QStringLiteral("foo@kde.org"));
+    QCOMPARE(lst.at(0).emails().at(1), QStringLiteral("foo2@kde.org"));
+    QCOMPARE(lst.at(0).emails().at(2), QStringLiteral("foo3@kde.org"));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportTitle()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "title: foo\n"
+                                 "mail: foo@kde.org\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).title(), QStringLiteral("foo"));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportWorkStreet()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "title: foo\n"
+                                 "mail: foo@kde.org\n"
+                                 "street: work address\n"
+                                 "mozillaWorkStreet2: work address next\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).address(Address::Work).street(), QStringLiteral("work address\nwork address next"));
+    QCOMPARE(contactGroup.count(), 0);
+}
+
+void LDifConverterTest::shouldImportContactGroup()
+{
+    QString str = QStringLiteral("dn: cn=test,mail=\n"
+                                 "cn: test\n"
+                                 "modifyTimeStamp: 20080526T234914Z\n"
+                                 "display-name: Test\n"
+                                 "objectclass: top\n"
+                                 "objectclass: groupOfNames\n"
+                                 "member: cn=Jim Doe,mail=jim.doe@foobar.com\n"
+                                 "member: cn=Jane Doe,mail=jane.doe@foobar.com\n"
+                                 "member: cn=John Doe,mail=john.doe@foobar.com\n");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 0);
+    QCOMPARE(contactGroup.count(), 1);
+    ContactGroup first = contactGroup.first();
+    QCOMPARE(first.name(), QStringLiteral("Test"));
+    QCOMPARE((int)first.count(), 3);
+}
+
+void LDifConverterTest::shouldImportMultiEntries()
+{
+    QString str = QStringLiteral("dn: cn=test1,mail=test1@test.test\n"
+                                 "sn: test1\n"
+                                 "cn: test1\n"
+                                 "uid: jpgdf2NrLQ\n"
+                                 "mail: test1@test.test\n"
+                                 "modifytimestamp: 20121219T140848Z\n"
+                                 "objectclass: top\n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson\n"
+                                 "\n"
+                                 "dn: cn=test2,mail=test2@test.test\n"
+                                 "sn: test2\n"
+                                 "cn: test2\n"
+                                 "uid: ow2mwdUb6A\n"
+                                 "mail: test2@test.test\n"
+                                 "objectclass: top\n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson\n");
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 2);
+    QCOMPARE(contactGroup.count(), 0);
+    QCOMPARE(lst.at(0).emails().at(0), QStringLiteral("test1@test.test"));
+    QCOMPARE(lst.at(1).emails().at(0), QStringLiteral("test2@test.test"));
+}
+
+void LDifConverterTest::shouldImportGroupAndAddress()
+{
+    QString str = QStringLiteral("dn: cn=laurent,mail=foo@kde.org\n"
+                                 "sn: laurent\n"
+                                 "cn: laurent\n"
+                                 "uid: d1d5cdd4-7d5d-484b-828d-58864d8efe74\n"
+                                 "title: foo\n"
+                                 "mail: foo@kde.org\n"
+                                 "street: work address\n"
+                                 "mozillaWorkStreet2: work address next\n"
+                                 "objectclass: top_n"
+                                 "objectclass: person\n"
+                                 "objectclass: organizationalPerson\n"
+                                 "\n"
+                                 "dn: cn=test,mail=\n"
+                                 "cn: test\n"
+                                 "modifyTimeStamp: 20080526T234914Z\n"
+                                 "display-name: Test\n"
+                                 "objectclass: top\n"
+                                 "objectclass: groupOfNames\n"
+                                 "member: cn=Jim Doe,mail=jim.doe@foobar.com\n"
+                                 "member: cn=Jane Doe,mail=jane.doe@foobar.com\n"
+                                 "member: cn=John Doe,mail=john.doe@foobar.com\n");
+
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    bool result = LDIFConverter::LDIFToAddressee(str, lst, contactGroup);
+    QVERIFY(result);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(contactGroup.count(), 1);
+}
+
+void LDifConverterTest::shouldExportEmail()
+{
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+    QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                      "objectclass: top\n"
+                                      "objectclass: person\n"
+                                      "objectclass: organizationalPerson\n"
+                                      "uid: testuid\n"
+                                      "mail: foo@kde.org\n\n");
+
+    QCOMPARE(str, expected);
+}
+
+void LDifConverterTest::shouldExportMultiEmails()
+{
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org") << QStringLiteral("foo2@kde.org") << QStringLiteral("foo3@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+    QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                      "objectclass: top\n"
+                                      "objectclass: person\n"
+                                      "objectclass: organizationalPerson\n"
+                                      "uid: testuid\n"
+                                      "mail: foo@kde.org\n"
+                                      "mozillasecondemail: foo2@kde.org\n"
+                                      "othermailbox: foo3@kde.org\n"
+                                      "\n");
+
+    QCOMPARE(str, expected);
+
+}
+
+void LDifConverterTest::shouldExportBirthday()
+{
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    Addressee addr;
+    QDate date(2015, 3, 3);
+    addr.setBirthday(QDateTime(date));
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+    QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                      "objectclass: top\n"
+                                      "objectclass: person\n"
+                                      "objectclass: organizationalPerson\n"
+                                      "uid: testuid\n"
+                                      "mail: foo@kde.org\n"
+                                      "birthyear: 2015\n"
+                                      "birthmonth: 3\n"
+                                      "birthday: 3\n"
+                                      "\n");
+
+    QCOMPARE(str, expected);
+}
+
+void LDifConverterTest::shouldExportTitle()
+{
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setTitle(QStringLiteral("foo"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+    QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                      "objectclass: top\n"
+                                      "objectclass: person\n"
+                                      "objectclass: organizationalPerson\n"
+                                      "uid: testuid\n"
+                                      "mail: foo@kde.org\n"
+                                      "title: foo\n"
+                                      "\n");
+
+    QCOMPARE(str, expected);
+}
+
+void LDifConverterTest::shouldExportMultiEntries()
+{
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setTitle(QStringLiteral("foo"));
+    addr.setUid(QStringLiteral("testuid"));
+    lst << addr;
+
+    Addressee addr2;
+    QDate date(2015, 3, 3);
+    addr2.setBirthday(QDateTime(date));
+    addr2.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr2.setUid(QStringLiteral("testuid"));
+    lst << addr2;
+
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+
+    const QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                            "objectclass: top\n"
+                                            "objectclass: person\n"
+                                            "objectclass: organizationalPerson\n"
+                                            "uid: testuid\n"
+                                            "mail: foo@kde.org\n"
+                                            "title: foo\n"
+                                            "\n"
+                                            "dn: cn=,mail=foo@kde.org\n"
+                                            "objectclass: top\n"
+                                            "objectclass: person\n"
+                                            "objectclass: organizationalPerson\n"
+                                            "uid: testuid\n"
+                                            "mail: foo@kde.org\n"
+                                            "birthyear: 2015\n"
+                                            "birthmonth: 3\n"
+                                            "birthday: 3\n"
+                                            "\n");
+    QCOMPARE(str, expected);
+}
+
+void LDifConverterTest::shouldExportGroup()
+{
+    AddresseeList lst;
+    ContactGroup::List contactGroup;
+    ContactGroup grp;
+    ContactGroup::Data data;
+    data.setEmail(QStringLiteral("foo@kde.org"));
+    data.setName(QStringLiteral("foo"));
+    grp.append(data);
+    ContactGroup::Data data2;
+    data2.setEmail(QStringLiteral("foo2@kde.org"));
+    data2.setName(QStringLiteral("foo2"));
+    grp.append(data2);
+    contactGroup.append(grp);
+
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+
+    const QString expected = QStringLiteral("objectclass: top\n"
+                                            "objectclass: groupOfNames\n"
+                                            "member: cn=foo,mail=foo@kde.org\n"
+                                            "member: cn=foo2,mail=foo2@kde.org\n"
+                                            "\n");
+    QCOMPARE(str, expected);
+}
+
+void LDifConverterTest::shouldExportWorkStreet()
+{
+    AddresseeList lst;
+    Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    Address address(Address::Work);
+    address.setStreet(QStringLiteral("work address"));
+    address.setPostalCode(QStringLiteral("postal"));
+    addr.insertAddress(address);
+    lst << addr;
+    ContactGroup::List contactGroup;
+
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+
+    const QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                            "objectclass: top\n"
+                                            "objectclass: person\n"
+                                            "objectclass: organizationalPerson\n"
+                                            "uid: testuid\n"
+                                            "mail: foo@kde.org\n"
+                                            "postalcode: postal\n"
+                                            "streetaddress: work address\n"
+                                            "street: work address\n"
+                                            "\n");
+    QCOMPARE(str, expected);
+}
+
+void LDifConverterTest::shouldExportFullName()
+{
+    AddresseeList lst;
+    Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    addr.setName(QStringLiteral("name"));
+    addr.setNickName(QStringLiteral("nickname"));
+    addr.setFamilyName(QStringLiteral("familyname"));
+    lst << addr;
+    ContactGroup::List contactGroup;
+
+    QString str;
+    bool result = LDIFConverter::addresseeAndContactGroupToLDIF(lst, contactGroup, str);
+    QVERIFY(result);
+
+    const QString expected = QStringLiteral("dn: cn=,mail=foo@kde.org\n"
+                                            "objectclass: top\n"
+                                            "objectclass: person\n"
+                                            "objectclass: organizationalPerson\n"
+                                            "sn: familyname\n"
+                                            "uid: testuid\n"
+                                            "nickname: nickname\n"
+                                            "xmozillanickname: nickname\n"
+                                            "mozillanickname: nickname\n"
+                                            "mail: foo@kde.org\n"
+                                            "\n");
+
+    QCOMPARE(str, expected);
+}
+
+QTEST_MAIN(LDifConverterTest)
diff --git a/autotests/ldifconvertertest.h b/autotests/ldifconvertertest.h
new file mode 100644 (file)
index 0000000..a7ef354
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    This file is part of libkabc.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef LDIFCONVERTERTEST_H
+#define LDIFCONVERTERTEST_H
+
+#include <QObject>
+
+class LDifConverterTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit LDifConverterTest(QObject *parent = Q_NULLPTR);
+    ~LDifConverterTest();
+
+private Q_SLOTS:
+    //Import
+    void shouldImportEmail();
+    void shouldImportMultiEmails();
+    void shouldImportStandardBirthday();
+    void shouldImportTheBatsBirthday();
+    void shouldImportTheBatsEmails();
+    void shouldImportTitle();
+    void shouldImportWorkStreet();
+    void shouldImportContactGroup();
+    void shouldImportMultiEntries();
+    void shouldImportGroupAndAddress();
+
+    //Export
+    void shouldExportEmail();
+    void shouldExportBirthday();
+    void shouldExportTitle();
+    void shouldExportMultiEntries();
+    void shouldExportGroup();
+    void shouldExportWorkStreet();
+    void shouldExportMultiEmails();
+    void shouldExportFullName();
+};
+
+#endif // LDIFCONVERTERTEST_H
diff --git a/autotests/phonenumbertest.cpp b/autotests/phonenumbertest.cpp
new file mode 100644 (file)
index 0000000..fead9b1
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "phonenumbertest.h"
+#include "kcontacts/phonenumber.h"
+#include <qtest.h>
+
+QTEST_MAIN(PhoneNumberTest)
+
+void PhoneNumberTest::emptyTest()
+{
+    KContacts::PhoneNumber number;
+
+    QVERIFY(number.isEmpty());
+}
+
+void PhoneNumberTest::storeTest()
+{
+    KContacts::PhoneNumber number;
+
+    number.setId(QStringLiteral("My Id"));
+    number.setType(KContacts::PhoneNumber::Work | KContacts::PhoneNumber::Cell);
+    number.setNumber(QStringLiteral("2734826345"));
+
+    QVERIFY(number.id() == QLatin1String("My Id"));
+    QVERIFY(number.type() == (KContacts::PhoneNumber::Work | KContacts::PhoneNumber::Cell));
+    QVERIFY(number.number() == QLatin1String("2734826345"));
+}
+
+void PhoneNumberTest::equalsTest()
+{
+    KContacts::PhoneNumber number1, number2;
+
+    number1.setId(QStringLiteral("My Id"));
+    number1.setType(KContacts::PhoneNumber::Work | KContacts::PhoneNumber::Cell);
+    number1.setNumber(QStringLiteral("2734826345"));
+
+    number2.setId(QStringLiteral("My Id"));
+    number2.setType(KContacts::PhoneNumber::Work | KContacts::PhoneNumber::Cell);
+    number2.setNumber(QStringLiteral("2734826345"));
+
+    QVERIFY(number1 == number2);
+}
+
+void PhoneNumberTest::differsTest()
+{
+    KContacts::PhoneNumber number1(QStringLiteral("123"), KContacts::PhoneNumber::Home);
+    KContacts::PhoneNumber number2(QStringLiteral("123"), KContacts::PhoneNumber::Work);
+
+    QVERIFY(number1 != number2);
+}
+
+void PhoneNumberTest::assignmentTest()
+{
+    KContacts::PhoneNumber number1, number2;
+
+    number1.setId(QStringLiteral("My Id"));
+    number1.setType(KContacts::PhoneNumber::Work | KContacts::PhoneNumber::Cell);
+    number1.setNumber(QStringLiteral("2734826345"));
+
+    number1 = number2;
+
+    QVERIFY(number1 == number2);
+}
+
+void PhoneNumberTest::serializeTest()
+{
+    KContacts::PhoneNumber number1, number2;
+
+    number1.setId(QStringLiteral("My Id"));
+    number1.setType(KContacts::PhoneNumber::Work | KContacts::PhoneNumber::Cell);
+    number1.setNumber(QStringLiteral("2734826345"));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << number1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> number2;
+
+    QVERIFY(number1 == number2);
+}
+
+void PhoneNumberTest::labelTest()
+{
+    QMap<KContacts::PhoneNumber::Type, QString> labels;
+
+    const KContacts::PhoneNumber::TypeList types = KContacts::PhoneNumber::typeList();
+
+    // check all types standalone
+    Q_FOREACH (KContacts::PhoneNumber::Type type, types) {
+        const KContacts::PhoneNumber phone(QStringLiteral("1"), type);
+        QCOMPARE(phone.type(), type);
+
+        // Pref is special cased
+        if (type != KContacts::PhoneNumber::Pref) {
+            QCOMPARE(phone.typeLabel(), KContacts::PhoneNumber::typeFlagLabel((KContacts::PhoneNumber::TypeFlag)(int)type));
+            labels.insert(type, phone.typeLabel());
+        } else {
+            labels.insert(type, KContacts::PhoneNumber::typeFlagLabel((KContacts::PhoneNumber::TypeFlag)(int)type));
+        }
+        QCOMPARE(KContacts::PhoneNumber::typeLabel(type), phone.typeLabel());
+    }
+
+    // combine all with Pref
+    Q_FOREACH (KContacts::PhoneNumber::Type type, types) {
+        KContacts::PhoneNumber::Type combinedType = type | KContacts::PhoneNumber::Pref;
+        const KContacts::PhoneNumber phone(QLatin1String("1"), combinedType);
+        QCOMPARE(phone.type(), combinedType);
+        QCOMPARE(KContacts::PhoneNumber::typeLabel(combinedType), phone.typeLabel());
+
+        if (type < KContacts::PhoneNumber::Pref) {
+            const QString expectedCombinedString = QStringLiteral("%1/%2").arg(labels[type]).arg(labels[KContacts::PhoneNumber::Pref]);
+            QCOMPARE(phone.typeLabel(), expectedCombinedString);
+        } else if (type > KContacts::PhoneNumber::Pref) {
+            const QString expectedCombinedString = QStringLiteral("%1/%2").arg(labels[KContacts::PhoneNumber::Pref]).arg(labels[type]);
+            QCOMPARE(phone.typeLabel(), expectedCombinedString);
+        }
+    }
+
+    // combine all with Fax
+    Q_FOREACH (KContacts::PhoneNumber::Type type, types) {
+        KContacts::PhoneNumber::Type combinedType = type | KContacts::PhoneNumber::Fax;
+        const KContacts::PhoneNumber phone(QLatin1String("1"), combinedType);
+        QCOMPARE(phone.type(), combinedType);
+        QCOMPARE(KContacts::PhoneNumber::typeLabel(combinedType), phone.typeLabel());
+
+        if (type == KContacts::PhoneNumber::Home ||
+                type == KContacts::PhoneNumber::Work) {
+            // special cased
+        } else if (type < KContacts::PhoneNumber::Fax) {
+            const QString expectedCombinedString = QStringLiteral("%1/%2").arg(labels[type]).arg(labels[KContacts::PhoneNumber::Fax]);
+            QCOMPARE(phone.typeLabel(), expectedCombinedString);
+        } else if (type > KContacts::PhoneNumber::Fax) {
+            const QString expectedCombinedString = QStringLiteral("%1/%2").arg(labels[KContacts::PhoneNumber::Fax]).arg(labels[type]);
+            QCOMPARE(phone.typeLabel(), expectedCombinedString);
+        }
+    }
+
+    // special cases
+    QCOMPARE(KContacts::PhoneNumber::typeLabel(KContacts::PhoneNumber::Pref), QLatin1String("Preferred Number"));
+    QCOMPARE(KContacts::PhoneNumber::typeLabel(KContacts::PhoneNumber::Home |
+             KContacts::PhoneNumber::Fax),
+             QLatin1String("Home Fax"));
+    QCOMPARE(KContacts::PhoneNumber::typeLabel(KContacts::PhoneNumber::Work |
+             KContacts::PhoneNumber::Fax),
+             QLatin1String("Work Fax"));
+    QCOMPARE(KContacts::PhoneNumber::typeLabel(KContacts::PhoneNumber::Home |
+             KContacts::PhoneNumber::Fax  |
+             KContacts::PhoneNumber::Pref),
+             QLatin1String("Home Fax/Preferred"));
+    QCOMPARE(KContacts::PhoneNumber::typeLabel(KContacts::PhoneNumber::Work |
+             KContacts::PhoneNumber::Fax |
+             KContacts::PhoneNumber::Pref),
+             QLatin1String("Work Fax/Preferred"));
+}
diff --git a/autotests/phonenumbertest.h b/autotests/phonenumbertest.h
new file mode 100644 (file)
index 0000000..3920ba5
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef PHONENUMBER_TEST_H
+#define PHONENUMBER_TEST_H
+
+#include <QtCore/QObject>
+
+class PhoneNumberTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void emptyTest();
+    void storeTest();
+    void equalsTest();
+    void differsTest();
+    void assignmentTest();
+    void serializeTest();
+    void labelTest();
+};
+
+#endif
diff --git a/autotests/picturetest.cpp b/autotests/picturetest.cpp
new file mode 100644 (file)
index 0000000..8893e65
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "picturetest.h"
+#include "kcontacts/picture.h"
+#include <QPainter>
+#include <qtest.h>
+#include <QBuffer>
+
+QTEST_MAIN(PictureTest)
+
+static QImage testImage()
+{
+    static QImage image;
+
+    if (image.isNull()) {
+        image = QImage(200, 200, QImage::Format_RGB32);
+        QPainter p(&image);
+        p.drawRect(10, 10, 50, 50);
+    }
+
+    return image;
+}
+
+static QByteArray testImageRawPNG()
+{
+    static QByteArray raw;
+
+    if (raw.isNull()) {
+        QBuffer buffer(&raw);
+        buffer.open(QIODevice::WriteOnly);
+        testImage().save(&buffer, "PNG");
+    }
+
+    return raw;
+}
+
+static QByteArray testImageRawJPEG()
+{
+    static QByteArray raw;
+
+    if (raw.isNull()) {
+        QBuffer buffer(&raw);
+        buffer.open(QIODevice::WriteOnly);
+        testImage().save(&buffer, "JPEG");
+    }
+
+    return raw;
+}
+
+void PictureTest::emptyTest()
+{
+    KContacts::Picture picture;
+
+    QVERIFY(picture.isEmpty());
+}
+
+void PictureTest::storeTestInternImage()
+{
+    KContacts::Picture picture;
+
+    picture.setData(testImage());
+
+    QVERIFY(picture.isEmpty() == false);
+    QVERIFY(picture.isIntern() == true);
+    QVERIFY(picture.type() == QLatin1String("jpeg"));
+    QVERIFY(picture.data() == testImage());
+    QVERIFY(picture.rawData() == testImageRawJPEG());
+}
+
+void PictureTest::storeTestInternRawData()
+{
+    KContacts::Picture picture;
+
+    picture.setRawData(testImageRawPNG(), QStringLiteral("png"));
+
+    QVERIFY(picture.isEmpty() == false);
+    QVERIFY(picture.isIntern() == true);
+    QVERIFY(picture.type() == QLatin1String("png"));
+    QVERIFY(picture.rawData() == testImageRawPNG());
+    QVERIFY(picture.data() == testImage());
+}
+
+void PictureTest::storeTestExtern()
+{
+    KContacts::Picture picture;
+
+    picture.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    QVERIFY(picture.isEmpty() == false);
+    QVERIFY(picture.isIntern() == false);
+    QVERIFY(picture.type() == QLatin1String("png"));
+    QVERIFY(picture.url() == QLatin1String("http://myhomepage.com/foto.png"));
+}
+
+void PictureTest::equalsTestInternImage()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setData(testImage());
+
+    picture2.setData(testImage());
+
+    QVERIFY(picture1 == picture2);
+
+    // access rawData() so a QByteArray is created
+    picture1.rawData();
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::equalsTestInternRawData()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setRawData(testImageRawPNG(), QStringLiteral("png"));
+
+    picture2.setRawData(testImageRawPNG(), QStringLiteral("png"));
+
+    QVERIFY(picture1 == picture2);
+
+    // access data() so a QImage is created
+    picture1.data();
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::equalsTestInternImageAndRawData()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setData(testImage());
+
+    picture2.setRawData(testImageRawJPEG(), QStringLiteral("jpeg"));
+
+    QVERIFY(picture1.rawData() == picture2.rawData());
+}
+
+void PictureTest::equalsTestExtern()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    picture2.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::differsTest()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    picture2.setData(testImage());
+
+    QVERIFY(picture1 != picture2);
+}
+
+void PictureTest::differsTestInternRawData()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setRawData(testImageRawJPEG(), QStringLiteral("jpeg"));
+
+    picture2.setRawData(testImageRawPNG(), QStringLiteral("png"));
+
+    QVERIFY(picture1 != picture2);
+    QVERIFY(picture1.rawData() != picture2.rawData());
+}
+
+void PictureTest::differsTestExtern()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    picture1.setUrl(QStringLiteral("http://myhomepage.com/foto2.png"), QStringLiteral("png"));
+
+    QVERIFY(picture1 != picture2);
+}
+
+void PictureTest::assignmentTestIntern()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setData(testImage());
+
+    picture2 = picture1;
+
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::assignmentTestExtern()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    picture2 = picture1;
+
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::serializeTestInternImage()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setData(testImage());
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << picture1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> picture2;
+
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::serializeTestInternRawData()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setRawData(testImageRawPNG(), QStringLiteral("png"));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << picture1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> picture2;
+
+    QVERIFY(picture1 == picture2);
+}
+
+void PictureTest::serializeTestExtern()
+{
+    KContacts::Picture picture1, picture2;
+
+    picture1.setUrl(QStringLiteral("http://myhomepage.com/foto.png"), QStringLiteral("png"));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << picture1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> picture2;
+
+    QVERIFY(picture1 == picture2);
+}
diff --git a/autotests/picturetest.h b/autotests/picturetest.h
new file mode 100644 (file)
index 0000000..34d84f4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef PICTURE_TEST_H
+#define PICTURE_TEST_H
+
+#include <QtCore/QObject>
+
+class PictureTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void emptyTest();
+    void storeTestInternImage();
+    void storeTestInternRawData();
+    void storeTestExtern();
+    void equalsTestInternImage();
+    void equalsTestInternRawData();
+    void equalsTestInternImageAndRawData();
+    void equalsTestExtern();
+    void differsTest();
+    void differsTestInternRawData();
+    void differsTestExtern();
+    void assignmentTestIntern();
+    void assignmentTestExtern();
+    void serializeTestInternImage();
+    void serializeTestInternRawData();
+    void serializeTestExtern();
+};
+
+#endif
diff --git a/autotests/relatedtest.cpp b/autotests/relatedtest.cpp
new file mode 100644 (file)
index 0000000..1f2167b
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "relatedtest.h"
+#include "related.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+RelatedTest::RelatedTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+RelatedTest::~RelatedTest()
+{
+
+}
+
+void RelatedTest::shouldHaveDefaultValue()
+{
+    KContacts::Related related;
+    QVERIFY(!related.isValid());
+    QVERIFY(related.related().isEmpty());
+    QVERIFY(related.parameters().isEmpty());
+}
+
+void RelatedTest::shouldAssignValue()
+{
+    const QString relatedTo(QStringLiteral("friend"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    KContacts::Related related(relatedTo);
+    related.setParameters(params);
+    QVERIFY(related.isValid());
+    QVERIFY(!related.related().isEmpty());
+    QCOMPARE(related.related(), relatedTo);
+    QVERIFY(!related.parameters().isEmpty());
+    QCOMPARE(related.parameters(), params);
+
+}
+
+void RelatedTest::shouldAssignExternal()
+{
+    KContacts::Related related;
+    const QString relatedTo(QStringLiteral("friend"));
+    related.setRelated(relatedTo);
+    QVERIFY(related.isValid());
+    QCOMPARE(related.related(), relatedTo);
+}
+
+void RelatedTest::shouldSerialized()
+{
+    KContacts::Related related;
+    KContacts::Related result;
+    const QString relatedTo(QStringLiteral("friend"));
+    related.setRelated(relatedTo);
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    related.setParameters(params);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << related;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> result;
+
+    QVERIFY(related == result);
+}
+
+void RelatedTest::shouldEqualRelated()
+{
+    KContacts::Related related;
+    KContacts::Related result;
+    const QString relatedTo(QStringLiteral("friend"));
+    related.setRelated(relatedTo);
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    related.setParameters(params);
+
+    result = related;
+    QVERIFY(related == result);
+}
+
+void RelatedTest::shouldParseRelated()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:4.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "RELATED:friend"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).relationShips().count(), 1);
+}
+
+void RelatedTest::shouldParseWithoutRelated()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).relationShips().count(), 0);
+}
+
+void RelatedTest::shouldCreateVCard4()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Related::List lstRelated;
+    KContacts::Related related(QStringLiteral("friend"));
+    lstRelated << related;
+    addr.setRelationShips(lstRelated);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "RELATED:friend\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void RelatedTest::shouldCreateVCardWithTwoRelated()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Related::List lstRelated;
+    KContacts::Related related(QStringLiteral("friend"));
+    KContacts::Related related2(QStringLiteral("kde"));
+    lstRelated << related << related2;
+    addr.setRelationShips(lstRelated);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "RELATED:friend\r\n"
+                        "RELATED:kde\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+
+}
+
+void RelatedTest::shouldCreateVCardWithParameters()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Related::List lstRelated;
+    KContacts::Related related(QStringLiteral("friend"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    related.setParameters(params);
+    lstRelated << related;
+    addr.setRelationShips(lstRelated);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:4.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "RELATED;FOO1=bla1,blo1;FOO2=bla2,blo2:friend\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+void RelatedTest::shouldNotExportInVcard3()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Related::List lstRelated;
+    KContacts::Related related(QStringLiteral("friend"));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    related.setParameters(params);
+    lstRelated << related;
+    addr.setRelationShips(lstRelated);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected("BEGIN:VCARD\r\n"
+                        "VERSION:3.0\r\n"
+                        "EMAIL:foo@kde.org\r\n"
+                        "N:;;;;\r\n"
+                        "UID:testuid\r\n"
+                        "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
+
+QTEST_MAIN(RelatedTest)
diff --git a/autotests/relatedtest.h b/autotests/relatedtest.h
new file mode 100644 (file)
index 0000000..14eac7d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef RELATEDTEST_H
+#define RELATEDTEST_H
+
+#include <QObject>
+
+class RelatedTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit RelatedTest(QObject *parent = Q_NULLPTR);
+    ~RelatedTest();
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldAssignExternal();
+    void shouldSerialized();
+    void shouldEqualRelated();
+    void shouldParseRelated();
+    void shouldParseWithoutRelated();
+    void shouldCreateVCard4();
+    void shouldCreateVCardWithTwoRelated();
+    void shouldCreateVCardWithParameters();
+    void shouldNotExportInVcard3();
+};
+
+#endif // RELATEDTEST_H
diff --git a/autotests/resourcelocatorurltest.cpp b/autotests/resourcelocatorurltest.cpp
new file mode 100644 (file)
index 0000000..b14f3ba
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "resourcelocatorurltest.h"
+
+#include "resourcelocatorurl.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+using namespace KContacts;
+ResourceLocatorUrlTest::ResourceLocatorUrlTest(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+ResourceLocatorUrlTest::~ResourceLocatorUrlTest()
+{
+
+}
+
+void ResourceLocatorUrlTest::shouldHaveDefaultValue()
+{
+    ResourceLocatorUrl ResourceLocatorUrl;
+    QVERIFY(!ResourceLocatorUrl.isValid());
+    QVERIFY(ResourceLocatorUrl.url().isEmpty());
+    QVERIFY(ResourceLocatorUrl.parameters().isEmpty());
+}
+
+void ResourceLocatorUrlTest::shouldAssignValue()
+{
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    ResourceLocatorUrl ResourceLocatorUrl;
+    ResourceLocatorUrl.setParameters(params);
+    QVERIFY(!ResourceLocatorUrl.isValid());
+    QVERIFY(ResourceLocatorUrl.url().isEmpty());
+    QVERIFY(!ResourceLocatorUrl.parameters().isEmpty());
+    QCOMPARE(ResourceLocatorUrl.parameters(), params);
+}
+
+void ResourceLocatorUrlTest::shouldAssignExternal()
+{
+    ResourceLocatorUrl resourcelocatorurl;
+    QUrl url = QUrl(QStringLiteral("https://www.kde.org"));
+    resourcelocatorurl.setUrl(url);
+    QVERIFY(!resourcelocatorurl.url().isEmpty());
+    QCOMPARE(resourcelocatorurl.url(), url);
+}
+
+void ResourceLocatorUrlTest::shouldSerialized()
+{
+    ResourceLocatorUrl resourcelocatorurl;
+    ResourceLocatorUrl result;
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    resourcelocatorurl.setParameters(params);
+    resourcelocatorurl.setUrl(QUrl(QStringLiteral("mailto:foo@kde.org")));
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << resourcelocatorurl;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> result;
+
+    QVERIFY(resourcelocatorurl == result);
+}
+
+void ResourceLocatorUrlTest::shouldEqualResourceLocatorUrl()
+{
+    ResourceLocatorUrl resourcelocatorurl;
+    ResourceLocatorUrl result;
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo1"), QStringList() << QStringLiteral("bla1") << QStringLiteral("blo1"));
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    resourcelocatorurl.setUrl(QUrl(QStringLiteral("mailto:foo@kde.org")));
+    resourcelocatorurl.setParameters(params);
+
+    result = resourcelocatorurl;
+    QVERIFY(resourcelocatorurl == result);
+}
+
+void ResourceLocatorUrlTest::shouldParseResourceLocatorUrl()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "URL;PREF=1:https://firsturl\n"
+                         "URL;PREF=1:https://sherlockholmes.com/calendar/sherlockholmes\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QCOMPARE(lst.at(0).extraUrlList().count(), 2);
+    const ResourceLocatorUrl calurl = lst.at(0).extraUrlList().at(0);
+    QCOMPARE(calurl.url(), QUrl(QStringLiteral("https://firsturl")));
+    QVERIFY(!calurl.parameters().isEmpty());
+}
+
+void ResourceLocatorUrlTest::shouldGenerateVCard4()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::ResourceLocatorUrl webpage;
+    webpage.setUrl(QUrl(QStringLiteral("https://www.kde.org")));
+    addr.setUrl(webpage);
+    ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    addr.insertExtraUrl(url);
+    url.setUrl(QUrl(QStringLiteral("https://foo.kde.org")));
+    addr.insertExtraUrl(url);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:4.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "UID:testuid\r\n"
+                          "URL:https://www.kde.org\r\n"
+                          "URL:https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                          "URL:https://foo.kde.org\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void ResourceLocatorUrlTest::shouldGenerateVCardWithParameter()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::ResourceLocatorUrl webpage;
+    webpage.setUrl(QUrl(QStringLiteral("https://www.kde.org")));
+    addr.setUrl(webpage);
+    ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    url.setParameters(params);
+    addr.insertExtraUrl(url);
+    url.setUrl(QUrl(QStringLiteral("https://foo.kde.org")));
+    QMap<QString, QStringList> params2;
+    url.setParameters(params2);
+    addr.insertExtraUrl(url);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:4.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "UID:testuid\r\n"
+                          "URL:https://www.kde.org\r\n"
+                          "URL;FOO2=bla2,blo2:https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                          "URL:https://foo.kde.org\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void ResourceLocatorUrlTest::shouldGenerateVCard3()
+{
+    KContacts::AddresseeList lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::ResourceLocatorUrl webpage;
+    webpage.setUrl(QUrl(QStringLiteral("https://www.kde.org")));
+    addr.setUrl(webpage);
+    ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("https://sherlockholmes.com/calendar/sherlockholmes")));
+    QMap<QString, QStringList> params;
+    params.insert(QStringLiteral("Foo2"), QStringList() << QStringLiteral("bla2") << QStringLiteral("blo2"));
+    url.setParameters(params);
+    addr.insertExtraUrl(url);
+    url.setUrl(QUrl(QStringLiteral("https://foo.kde.org")));
+    QMap<QString, QStringList> params2;
+    url.setParameters(params2);
+    addr.insertExtraUrl(url);
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:3.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "UID:testuid\r\n"
+                          "URL:https://www.kde.org\r\n"
+                          "URL;FOO2=bla2,blo2:https://sherlockholmes.com/calendar/sherlockholmes\r\n"
+                          "URL:https://foo.kde.org\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+
+}
+
+QTEST_MAIN(ResourceLocatorUrlTest)
+
diff --git a/autotests/resourcelocatorurltest.h b/autotests/resourcelocatorurltest.h
new file mode 100644 (file)
index 0000000..62a50e8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ResourceLocatorUrlTest_H
+#define ResourceLocatorUrlTest_H
+
+#include <QObject>
+
+class ResourceLocatorUrlTest : public QObject
+{
+    Q_OBJECT
+public:
+    explicit ResourceLocatorUrlTest(QObject *parent = Q_NULLPTR);
+    ~ResourceLocatorUrlTest();
+
+private Q_SLOTS:
+    void shouldHaveDefaultValue();
+    void shouldAssignValue();
+    void shouldAssignExternal();
+    void shouldSerialized();
+    void shouldEqualResourceLocatorUrl();
+    void shouldParseResourceLocatorUrl();
+    void shouldGenerateVCard4();
+    void shouldGenerateVCardWithParameter();
+    void shouldGenerateVCard3();
+};
+
+#endif // ResourceLocatorUrlTest_H
diff --git a/autotests/secrecytest.cpp b/autotests/secrecytest.cpp
new file mode 100644 (file)
index 0000000..40eb81f
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "secrecytest.h"
+#include "kcontacts/secrecy.h"
+#include "vcardtool.h"
+#include <qtest.h>
+
+QTEST_MAIN(SecrecyTest)
+
+void SecrecyTest::emptyTest()
+{
+    KContacts::Secrecy secrecy;
+
+    QVERIFY(secrecy.type() == KContacts::Secrecy::Invalid);
+}
+
+void SecrecyTest::storeTest()
+{
+    KContacts::Secrecy secrecy;
+
+    secrecy.setType(KContacts::Secrecy::Private);
+
+    QVERIFY(secrecy.type() == KContacts::Secrecy::Private);
+}
+
+void SecrecyTest::equalsTest()
+{
+    KContacts::Secrecy secrecy1, secrecy2;
+
+    secrecy1.setType(KContacts::Secrecy::Confidential);
+    secrecy2.setType(KContacts::Secrecy::Confidential);
+
+    QVERIFY(secrecy1 == secrecy2);
+}
+
+void SecrecyTest::differsTest()
+{
+    KContacts::Secrecy secrecy1(KContacts::Secrecy::Private);
+    KContacts::Secrecy secrecy2(KContacts::Secrecy::Confidential);
+
+    QVERIFY(secrecy1 != secrecy2);
+}
+
+void SecrecyTest::assignmentTest()
+{
+    KContacts::Secrecy secrecy1, secrecy2;
+
+    secrecy1.setType(KContacts::Secrecy::Confidential);
+    secrecy1 = secrecy2;
+
+    QVERIFY(secrecy1 == secrecy2);
+}
+
+void SecrecyTest::serializeTest()
+{
+    KContacts::Secrecy secrecy1, secrecy2;
+
+    secrecy1.setType(KContacts::Secrecy::Confidential);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << secrecy1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> secrecy2;
+
+    QVERIFY(secrecy1 == secrecy2);
+}
diff --git a/autotests/secrecytest.h b/autotests/secrecytest.h
new file mode 100644 (file)
index 0000000..3580f1c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef SECRECY_TEST_H
+#define SECRECY_TEST_H
+
+#include <QtCore/QObject>
+
+class SecrecyTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void emptyTest();
+    void storeTest();
+    void equalsTest();
+    void differsTest();
+    void assignmentTest();
+    void serializeTest();
+};
+
+#endif
diff --git a/autotests/soundtest.cpp b/autotests/soundtest.cpp
new file mode 100644 (file)
index 0000000..d2df950
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "soundtest.h"
+#include "kcontacts/sound.h"
+#include "addressee.h"
+#include <qtest.h>
+#include "vcardtool.h"
+
+QTEST_MAIN(SoundTest)
+
+static QByteArray testData()
+{
+    static QByteArray data;
+
+    if (data.isNull()) {
+        for (int i = 0; i < 20; ++i) {
+            data.append(char(i));
+        }
+    }
+
+    return data;
+}
+
+void SoundTest::emptyTest()
+{
+    KContacts::Sound sound;
+
+    QVERIFY(sound.isEmpty());
+}
+
+void SoundTest::storeTestIntern()
+{
+    KContacts::Sound sound;
+
+    sound.setData(testData());
+
+    QVERIFY(sound.isIntern() == true);
+    QVERIFY(sound.data() == testData());
+}
+
+void SoundTest::storeTestExtern()
+{
+    KContacts::Sound sound;
+
+    sound.setUrl(QLatin1String("http://myhomepage.com/sound.wav"));
+
+    QVERIFY(sound.isIntern() == false);
+    QVERIFY(sound.url() == QLatin1String("http://myhomepage.com/sound.wav"));
+}
+
+void SoundTest::equalsTestIntern()
+{
+    KContacts::Sound sound1, sound2;
+
+    sound1.setData(testData());
+    sound2.setData(testData());
+
+    QVERIFY(sound1 == sound2);
+}
+
+void SoundTest::equalsTestExtern()
+{
+    KContacts::Sound sound1, sound2;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    sound2.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+
+    QVERIFY(sound1 == sound2);
+}
+
+void SoundTest::differsTest()
+{
+    KContacts::Sound sound1, sound2;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    sound2.setData(testData());
+
+    QVERIFY(sound1 != sound2);
+}
+
+void SoundTest::assignmentTestIntern()
+{
+    KContacts::Sound sound1, sound2;
+
+    sound1.setData(testData());
+
+    sound2 = sound1;
+
+    QVERIFY(sound1 == sound2);
+}
+
+void SoundTest::assignmentTestExtern()
+{
+    KContacts::Sound sound1, sound2;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+
+    sound2 = sound1;
+
+    QVERIFY(sound1 == sound2);
+}
+
+void SoundTest::serializeTest()
+{
+    KContacts::Sound sound1, sound2;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    sound1.setData(testData());
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << sound1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> sound2;
+
+    QVERIFY(sound1 == sound2);
+}
+
+void SoundTest::shouldParseSource()
+{
+    QByteArray vcarddata("BEGIN:VCARD\n"
+                         "VERSION:3.0\n"
+                         "N:LastName;FirstName;;;\n"
+                         "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n"
+                         "REV:2015-03-14T09:24:45+00:00\n"
+                         "SOUND;VALUE=URI:http://myhomepage.com/sound.wav\n"
+                         "FN:FirstName LastName\n"
+                         "END:VCARD\n");
+
+    KContacts::VCardTool vcard;
+    const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata);
+    QCOMPARE(lst.count(), 1);
+    QVERIFY(!lst.at(0).sound().isEmpty());
+    KContacts::Sound sound = lst.at(0).sound();
+    QVERIFY(!sound.isIntern());
+    QCOMPARE(sound.url(), QStringLiteral("http://myhomepage.com/sound.wav"));
+}
+
+void SoundTest::shouldGenerateVCard4WithData()
+{
+    KContacts::Addressee::List lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Sound sound1;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    sound1.setData(testData());
+    addr.setSound(sound1);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:4.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "SOUND;ENCODING=b:AAECAwQFBgcICQoLDA0ODxAREhM=\r\n"
+                          "UID:testuid\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void SoundTest::shouldGenerateVCard4WithUrl()
+{
+    KContacts::Addressee::List lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Sound sound1;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    addr.setSound(sound1);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:4.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "SOUND;VALUE=URI:http://myhomepage.com/sound.wav\r\n"
+                          "UID:testuid\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void SoundTest::shouldGenerateVCard3WithData()
+{
+    KContacts::Addressee::List lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Sound sound1;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    sound1.setData(testData());
+    addr.setSound(sound1);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:3.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "SOUND;ENCODING=b:AAECAwQFBgcICQoLDA0ODxAREhM=\r\n"
+                          "UID:testuid\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
+
+void SoundTest::shouldGenerateVCard3WithUrl()
+{
+    KContacts::Addressee::List lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::Sound sound1;
+
+    sound1.setUrl(QStringLiteral("http://myhomepage.com/sound.wav"));
+    addr.setSound(sound1);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:3.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "SOUND;VALUE=URI:http://myhomepage.com/sound.wav\r\n"
+                          "UID:testuid\r\n"
+                          "END:VCARD\r\n\r\n");
+
+    QCOMPARE(ba, expected);
+}
diff --git a/autotests/soundtest.h b/autotests/soundtest.h
new file mode 100644 (file)
index 0000000..46adb93
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef SOUND_TEST_H
+#define SOUND_TEST_H
+
+#include <QtCore/QObject>
+
+class SoundTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void emptyTest();
+    void storeTestIntern();
+    void storeTestExtern();
+    void equalsTestIntern();
+    void equalsTestExtern();
+    void differsTest();
+    void assignmentTestIntern();
+    void assignmentTestExtern();
+    void serializeTest();
+    void shouldParseSource();
+    void shouldGenerateVCard4WithData();
+    void shouldGenerateVCard4WithUrl();
+    void shouldGenerateVCard3WithData();
+    void shouldGenerateVCard3WithUrl();
+};
+
+#endif
diff --git a/autotests/testlock.h b/autotests/testlock.h
new file mode 100644 (file)
index 0000000..ead7c64
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    This file is part of the KContacts framework.
+
+    Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+#ifndef KCONTACTS_TESTLOCK_H
+#define KCONTACTS_TESTLOCK_H
+
+#include <QWidget>
+#include <QLabel>
+
+#include "kcontacts/lock.h"
+
+class QLabel;
+class QTreeWidget;
+
+class LockWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    LockWidget(const QString &identifier);
+    ~LockWidget();
+
+protected Q_SLOTS:
+    void lock();
+    void unlock();
+
+    void updateLockView();
+
+private:
+    KContacts::Lock *mLock;
+
+    QLabel *mStatus;
+    QTreeWidget *mLockView;
+};
+
+#endif
diff --git a/autotests/testroundtrip.cpp b/autotests/testroundtrip.cpp
new file mode 100644 (file)
index 0000000..55ec1a9
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2012 Kevin Krammer <krammer@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "addressee.h"
+#include "converter/vcardconverter.h"
+
+#include <qtest.h>
+#include <QDir>
+#include <QObject>
+
+using namespace KContacts;
+
+class RoundtripTest : public QObject
+{
+    Q_OBJECT
+
+private:
+
+    QDir mInputDir;
+    QDir mOutput2_1Dir;
+    QDir mOutput3_0Dir;
+    QDir mOutput4_0Dir;
+
+    QStringList mInputFiles;
+
+private Q_SLOTS:
+    void initTestCase();
+    void testVCardRoundtrip_data();
+    void testVCardRoundtrip();
+};
+
+// check the validity of our test data set
+void RoundtripTest::initTestCase()
+{
+
+    // check that all resource prefixes exist
+
+    mInputDir = QDir(QStringLiteral(":/input"));
+    QVERIFY(mInputDir.exists());
+    QVERIFY(mInputDir.cd(QStringLiteral("data")));
+
+    mOutput2_1Dir = QDir(QStringLiteral(":/output2.1"));
+    QVERIFY(mOutput2_1Dir.exists());
+    QVERIFY(mOutput2_1Dir.cd(QStringLiteral("data")));
+
+    mOutput3_0Dir = QDir(QStringLiteral(":/output3.0"));
+    QVERIFY(mOutput3_0Dir.exists());
+    QVERIFY(mOutput3_0Dir.cd(QStringLiteral("data")));
+
+    mOutput4_0Dir = QDir(QStringLiteral(":/output4.0"));
+    QVERIFY(mOutput4_0Dir.exists());
+    QVERIFY(mOutput4_0Dir.cd(QStringLiteral("data")));
+
+    // check that there are input files
+
+    mInputFiles = mInputDir.entryList();
+
+    QVERIFY(!mInputFiles.isEmpty());
+}
+
+void RoundtripTest::testVCardRoundtrip_data()
+{
+    QTest::addColumn<QString>("inputFile");
+    QTest::addColumn<QString>("output2_1File");
+    QTest::addColumn<QString>("output3_0File");
+    QTest::addColumn<QString>("output4_0File");
+
+    QString outFile21Pattern = QLatin1String("%1.2_1ref");
+    QString outFile4Pattern = QLatin1String("v4_0.%1.ref");
+    QString outFilePattern = QLatin1String("%1.ref");
+    Q_FOREACH (const QString &inputFile, mInputFiles) {
+        const QString outFile = outFilePattern.arg(inputFile);
+        const QString outFileV2_1 = outFile21Pattern.arg(inputFile);
+        const QString outFileV4 = outFile4Pattern.arg(inputFile);
+        QTest::newRow(QFile::encodeName(inputFile).constData())
+                << inputFile
+                << (mOutput2_1Dir.exists(outFileV2_1) ? outFileV2_1 : QString())
+                << (mOutput3_0Dir.exists(outFile) ? outFile : QString())
+                << (mOutput4_0Dir.exists(outFileV4) ? outFileV4 : QString());
+    }
+}
+
+void RoundtripTest::testVCardRoundtrip()
+{
+    QFETCH(QString, inputFile);
+    QFETCH(QString, output2_1File);
+    QFETCH(QString, output3_0File);
+    QFETCH(QString, output4_0File);
+
+    QVERIFY2(!output2_1File.isEmpty()
+             || !output3_0File.isEmpty()
+             || !output4_0File.isEmpty(),
+             "No reference output file for either format version");
+
+    QFile input(QFileInfo(mInputDir, inputFile).absoluteFilePath());
+    QVERIFY(input.open(QIODevice::ReadOnly));
+
+    const QByteArray inputData = input.readAll();
+    QVERIFY(!inputData.isEmpty());
+
+    VCardConverter converter;
+    const Addressee::List list = converter.parseVCards(inputData);
+    QVERIFY(!list.isEmpty());
+
+    if (!output2_1File.isEmpty()) {
+        const QByteArray outputData = converter.createVCards(list, VCardConverter::v2_1);
+
+        QFile outputFile(QFileInfo(mOutput2_1Dir, output2_1File).absoluteFilePath());
+        QVERIFY(outputFile.open(QIODevice::ReadOnly));
+
+        const QByteArray outputRefData = outputFile.readAll();
+        QCOMPARE(outputData.size(), outputRefData.size());
+
+        const QList<QByteArray> outputLines = outputData.split('\n');
+        const QList<QByteArray> outputRefLines = outputRefData.split('\n');
+        QCOMPARE(outputLines.count(), outputRefLines.count());
+        for (int i = 0; i < outputLines.count(); ++i) {
+            const QByteArray actual = outputLines[i];
+            const QByteArray expect = outputRefLines[i];
+
+            if (actual != expect) {
+                qCritical() << "Mismatch in v2.1 output line" << (i + 1);
+                QCOMPARE(actual.count(), expect.count());
+
+                qCritical() << "\nActual:" << actual << "\nExpect:" << expect;
+                QCOMPARE(actual, expect);
+            }
+        }
+    }
+
+    if (!output3_0File.isEmpty()) {
+        const QByteArray outputData = converter.createVCards(list, VCardConverter::v3_0);
+
+        QFile outputFile(QFileInfo(mOutput3_0Dir, output3_0File).absoluteFilePath());
+        QVERIFY(outputFile.open(QIODevice::ReadOnly));
+
+        const QByteArray outputRefData = outputFile.readAll();
+        QCOMPARE(outputData.size(), outputRefData.size());
+
+        const QList<QByteArray> outputLines = outputData.split('\n');
+        const QList<QByteArray> outputRefLines = outputRefData.split('\n');
+        QCOMPARE(outputLines.count(), outputRefLines.count());
+
+        for (int i = 0; i < outputLines.count(); ++i) {
+            const QByteArray actual = outputLines[i];
+            const QByteArray expect = outputRefLines[i];
+
+            if (actual != expect) {
+                qCritical() << "Mismatch in v3.0 output line" << (i + 1);
+
+                qCritical() << "\nActual:" << actual << "\nExpect:" << expect;
+                QCOMPARE(actual.count(), expect.count());
+                QCOMPARE(actual, expect);
+            }
+        }
+    }
+#if 0
+    if (!output4_0File.isEmpty()) {
+        const QByteArray outputData = converter.createVCards(list, VCardConverter::v4_0);
+
+        QFile outputFile(QFileInfo(mOutput4_0Dir, output4_0File).absoluteFilePath());
+        QVERIFY(outputFile.open(QIODevice::ReadOnly));
+
+        const QByteArray outputRefData = outputFile.readAll();
+        //QCOMPARE( outputData.size(), outputRefData.size() );
+
+        const QList<QByteArray> outputLines = outputData.split('\n');
+        const QList<QByteArray> outputRefLines = outputRefData.split('\n');
+        //QCOMPARE(outputLines.count(), outputRefLines.count());
+
+        for (int i = 0; i < outputLines.count(); ++i) {
+            const QByteArray actual = outputLines[i];
+            const QByteArray expect = outputRefLines[i];
+
+            if (actual != expect) {
+                qCritical() << "Mismatch in v4.0 output line" << (i + 1);
+
+                qCritical() << "\nActual:" << actual << "\nExpect:" << expect;
+                QCOMPARE(actual.count(), expect.count());
+                QCOMPARE(actual, expect);
+            }
+        }
+    }
+#endif
+}
+
+QTEST_MAIN(RoundtripTest)
+
+#include "testroundtrip.moc"
diff --git a/autotests/testroundtrip.qrc b/autotests/testroundtrip.qrc
new file mode 100644 (file)
index 0000000..fdb4844
--- /dev/null
@@ -0,0 +1,61 @@
+<!DOCTYPE RCC><RCC version="1.0">
+  <!-- input files -->
+  <qresource prefix="/input">
+    <file>data/vcard1.vcf</file>
+    <file>data/vcard2.vcf</file>
+    <file>data/vcard3.vcf</file>
+    <file>data/vcard4.vcf</file>
+    <file>data/vcard5.vcf</file>
+    <file>data/vcard6.vcf</file>
+    <file>data/vcard7.vcf</file>
+    <file>data/vcard8.vcf</file>
+    <file>data/vcard9.vcf</file>
+    <file>data/vcard10.vcf</file>
+    <file>data/vcard11.vcf</file>
+    <file>data/vcard12.vcf</file>
+    <file>data/vcard13.vcf</file>
+    <file>data/vcard14.vcf</file>
+  </qresource>
+  
+  <!-- output files formatted for vcard version 2.1 -->
+  <qresource prefix="/output2.1">
+    <file>data/vcard1.vcf.2_1ref</file>
+    <file>data/vcard2.vcf.2_1ref</file>
+    <file>data/vcard3.vcf.2_1ref</file>
+    <file>data/vcard6.vcf.2_1ref</file>
+    <file>data/vcard7.vcf.2_1ref</file>
+    <file>data/vcard12.vcf.2_1ref</file>
+    <file>data/vcard14.vcf.2_1ref</file>
+  </qresource>
+  
+  <!-- output files formatted for vcard version 3.0 -->
+  <qresource prefix="/output3.0">
+    <file>data/vcard1.vcf.ref</file>
+    <file>data/vcard2.vcf.ref</file>
+    <file>data/vcard3.vcf.ref</file>
+    <file>data/vcard4.vcf.ref</file>
+    <file>data/vcard5.vcf.ref</file>
+    <file>data/vcard6.vcf.ref</file>
+    <file>data/vcard7.vcf.ref</file>
+    <file>data/vcard8.vcf.ref</file>
+    <file>data/vcard9.vcf.ref</file>
+    <file>data/vcard10.vcf.ref</file>
+    <file>data/vcard11.vcf.ref</file>
+    <file>data/vcard12.vcf.ref</file>
+    <file>data/vcard13.vcf.ref</file>
+    <file>data/vcard14.vcf.ref</file>
+  </qresource>
+
+  <!-- output files formatted for vcard version 4.0 -->
+  <qresource prefix="/output4.0">
+    <file>data/v4_0.vcard1.vcf.ref</file>
+    <file>data/v4_0.vcard2.vcf.ref</file>
+    <file>data/v4_0.vcard3.vcf.ref</file>
+    <file>data/v4_0.vcard4.vcf.ref</file>
+    <file>data/v4_0.vcard5.vcf.ref</file>
+    <file>data/v4_0.vcard6.vcf.ref</file>
+    <file>data/v4_0.vcard7.vcf.ref</file>
+    <file>data/v4_0.vcard8.vcf.ref</file>
+  </qresource>
+
+</RCC>
diff --git a/autotests/timezonetest.cpp b/autotests/timezonetest.cpp
new file mode 100644 (file)
index 0000000..6f91aa7
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "timezonetest.h"
+#include "kcontacts/timezone.h"
+#include <qtest.h>
+#include "vcardtool.h"
+
+QTEST_MAIN(TimeZoneTest)
+
+void TimeZoneTest::emptyTest()
+{
+    KContacts::TimeZone timezone;
+
+    QVERIFY(timezone.isValid() == false);
+}
+
+void TimeZoneTest::storeTest()
+{
+    KContacts::TimeZone timezone;
+
+    timezone.setOffset(2);
+
+    QVERIFY(timezone.offset() == 2);
+}
+
+void TimeZoneTest::equalsTest()
+{
+    KContacts::TimeZone timezone1, timezone2;
+
+    timezone1.setOffset(2);
+    timezone2.setOffset(2);
+
+    QVERIFY(timezone1 == timezone2);
+}
+
+void TimeZoneTest::differsTest()
+{
+    KContacts::TimeZone timezone1(2);
+    KContacts::TimeZone timezone2(3);
+
+    QVERIFY(timezone1 != timezone2);
+    QVERIFY(timezone1 != KContacts::TimeZone());
+}
+
+void TimeZoneTest::assignmentTest()
+{
+    KContacts::TimeZone timezone1, timezone2;
+
+    timezone1.setOffset(2);
+    timezone1 = timezone2;
+
+    QVERIFY(timezone1 == timezone2);
+}
+
+void TimeZoneTest::serializeTest()
+{
+    KContacts::TimeZone timezone1, timezone2;
+
+    timezone1.setOffset(2);
+
+    QByteArray data;
+    QDataStream s(&data, QIODevice::WriteOnly);
+    s << timezone1;
+
+    QDataStream t(&data, QIODevice::ReadOnly);
+    t >> timezone2;
+
+    QVERIFY(timezone1 == timezone2);
+}
+
+void TimeZoneTest::shouldGenerateVCard3()
+{
+    KContacts::Addressee::List lst;
+    KContacts::Addressee addr;
+    addr.setEmails(QStringList() << QStringLiteral("foo@kde.org"));
+    addr.setUid(QStringLiteral("testuid"));
+    KContacts::TimeZone timezone;
+
+    timezone.setOffset(2);
+    addr.setTimeZone(timezone);
+
+    lst << addr;
+    KContacts::VCardTool vcard;
+    const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0);
+    QByteArray expected;
+    expected = QByteArray("BEGIN:VCARD\r\n"
+                          "VERSION:3.0\r\n"
+                          "EMAIL:foo@kde.org\r\n"
+                          "N:;;;;\r\n"
+                          "TZ:+00:02\r\n"
+                          "UID:testuid\r\n"
+                          "END:VCARD\r\n\r\n");
+    QCOMPARE(ba, expected);
+}
diff --git a/autotests/timezonetest.h b/autotests/timezonetest.h
new file mode 100644 (file)
index 0000000..1ebb237
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef TIMEZONE_TEST_H
+#define TIMEZONE_TEST_H
+
+#include <QtCore/QObject>
+
+class TimeZoneTest : public QObject
+{
+    Q_OBJECT
+
+private Q_SLOTS:
+    void emptyTest();
+    void storeTest();
+    void equalsTest();
+    void differsTest();
+    void assignmentTest();
+    void serializeTest();
+    void shouldGenerateVCard3();
+};
+
+#endif
diff --git a/autotests/vcarddragtest.cpp b/autotests/vcarddragtest.cpp
new file mode 100644 (file)
index 0000000..141ac90
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+  This file is part of the KContacts framework.
+  Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
+  Author: Kevin Krammer, krake@kdab.com
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include <qtest.h>
+
+#include "converter/kcontacts/vcardconverter.h"
+#include "kcontacts/vcarddrag.h"
+
+#include <QMimeData>
+using namespace KContacts;
+
+class VCardDragTest : public QObject
+{
+    Q_OBJECT
+
+public:
+    VCardDragTest();
+
+private:
+    Addressee mAddressee1;
+    Addressee mAddressee2;
+
+private Q_SLOTS:
+    void testPopulate();
+    void testCanDecode();
+    void testFromMimeData();
+};
+
+QTEST_MAIN(VCardDragTest)
+
+VCardDragTest::VCardDragTest()
+    : QObject()
+{
+    mAddressee1.setGivenName(QStringLiteral("Kevin"));
+    mAddressee1.setFamilyName(QStringLiteral("Krammer"));
+    mAddressee1.insertEmail(QStringLiteral("kevin.krammer@gmx.at"));
+
+    mAddressee2.setGivenName(QStringLiteral("Tobias"));
+    mAddressee2.setFamilyName(QStringLiteral("König"));
+    mAddressee2.insertEmail(QStringLiteral("tokoe@kde.org"));
+}
+
+void VCardDragTest::testPopulate()
+{
+    VCardConverter converter;
+    const QByteArray vcard = converter.createVCard(mAddressee1);
+    QVERIFY(!vcard.isEmpty());
+
+    QMimeData *data = new QMimeData();
+    bool result = VCardDrag::populateMimeData(data, vcard);
+    QVERIFY(result);
+    QVERIFY(data->hasFormat(KContacts::Addressee::mimeType()));
+    QCOMPARE(data->data(KContacts::Addressee::mimeType()), vcard);
+    delete data;
+
+    data = new QMimeData();
+    result = VCardDrag::populateMimeData(data, Addressee::List() << mAddressee1);
+    QVERIFY(result);
+    QVERIFY(data->hasFormat(KContacts::Addressee::mimeType()));
+    QCOMPARE(data->data(KContacts::Addressee::mimeType()), vcard);
+    delete data;
+
+    const QByteArray vcards = converter.createVCards(Addressee::List() << mAddressee1 << mAddressee2);
+    data = new QMimeData();
+    result = VCardDrag::populateMimeData(data, Addressee::List() << mAddressee1 << mAddressee2);
+    QVERIFY(result);
+    QVERIFY(data->hasFormat(KContacts::Addressee::mimeType()));
+    QCOMPARE(data->data(KContacts::Addressee::mimeType()), vcards);
+    delete data;
+
+    data = new QMimeData();
+    result = VCardDrag::populateMimeData(data, Addressee::List());
+    QVERIFY(!result);
+    QVERIFY(!data->hasFormat(KContacts::Addressee::mimeType()));
+    delete data;
+}
+
+void VCardDragTest::testCanDecode()
+{
+    VCardConverter converter;
+    const QByteArray vcard = converter.createVCard(mAddressee1);
+    QVERIFY(!vcard.isEmpty());
+
+    QMimeData *data = new QMimeData();
+    data->setData(Addressee::mimeType(), vcard);
+    QVERIFY(VCardDrag::canDecode(data));
+    delete data;
+
+    data = new QMimeData();
+    data->setData(QStringLiteral("text/x-vcard"), vcard);
+    QVERIFY(VCardDrag::canDecode(data));
+    delete data;
+
+    data = new QMimeData();
+    data->setData(QStringLiteral("application/octetstream"), vcard);
+    QVERIFY(!VCardDrag::canDecode(data));
+    delete data;
+}
+
+void VCardDragTest::testFromMimeData()
+{
+    VCardConverter converter;
+    const QByteArray vcard = converter.createVCard(mAddressee1);
+    QVERIFY(!vcard.isEmpty());
+
+    QByteArray content;
+
+    QMimeData *data = new QMimeData();
+    data->setData(Addressee::mimeType(), vcard);
+    bool result = VCardDrag::fromMimeData(data, content);
+    QVERIFY(result);
+    QCOMPARE(content, vcard);
+    delete data;
+
+    data = new QMimeData();
+    data->setData(QStringLiteral("text/x-vcard"), vcard);
+    result = VCardDrag::fromMimeData(data, content);
+    QVERIFY(result);
+    QCOMPARE(content, vcard);
+    delete data;
+
+    data = new QMimeData();
+    data->setData(QStringLiteral("application/octetstream"), vcard);
+    result = VCardDrag::fromMimeData(data, content);
+    QVERIFY(!result);
+    delete data;
+
+    const QByteArray vcards = converter.createVCards(Addressee::List() << mAddressee1 << mAddressee2);
+    QVERIFY(!vcards.isEmpty());
+
+    Addressee::List addresseeList;
+
+    data = new QMimeData();
+    data->setData(Addressee::mimeType(), vcards);
+    result = VCardDrag::fromMimeData(data, addresseeList);
+    QVERIFY(result);
+    QCOMPARE(addresseeList.count(), 2);
+    QCOMPARE(addresseeList[0], mAddressee1);
+    QCOMPARE(addresseeList[1], mAddressee2);
+    delete data;
+
+    data = new QMimeData();
+    data->setData(QStringLiteral("text/x-vcard"), vcards);
+    result = VCardDrag::fromMimeData(data, addresseeList);
+    QVERIFY(result);
+    QCOMPARE(addresseeList.count(), 2);
+    QCOMPARE(addresseeList[0], mAddressee1);
+    QCOMPARE(addresseeList[1], mAddressee2);
+    delete data;
+
+    data = new QMimeData();
+    data->setData(QStringLiteral("application/octetstream"), vcards);
+    result = VCardDrag::fromMimeData(data, addresseeList);
+    QVERIFY(!result);
+    delete data;
+
+    data = new QMimeData();
+    data->setData(Addressee::mimeType(), QByteArray());
+    result = VCardDrag::fromMimeData(data, addresseeList);
+    QVERIFY(!result);
+    delete data;
+}
+
+#include "vcarddragtest.moc"
diff --git a/metainfo.yaml b/metainfo.yaml
new file mode 100644 (file)
index 0000000..261fdb5
--- /dev/null
@@ -0,0 +1,14 @@
+maintainer:
+description: Address book API for KDE 
+tier: 3
+type: functional
+platforms:
+    - name: All
+portingAid: false
+deprecated: false
+release: false
+libraries:
+ - qmake: KContacts
+   cmake: "KF5::Contacts"
+cmakename: KF5Contacts
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7b0b766
--- /dev/null
@@ -0,0 +1,120 @@
+set(vcardparser_SRCS
+  vcardparser/vcard.cpp
+  vcardparser/vcardline.cpp
+  vcardparser/vcardparser.cpp
+)
+
+set(kcontacts_SRCS
+  address.cpp
+  addressee.cpp
+  addresseehelper.cpp
+  addresseelist.cpp
+  calendarurl.cpp
+  contactgroup.cpp
+  contactgrouptool.cpp
+  email.cpp
+  field.cpp
+  geo.cpp
+  gender.cpp
+  impp.cpp
+  key.cpp
+  lang.cpp
+  ldapdn.cpp
+  ldif.cpp
+  phonenumber.cpp
+  picture.cpp
+  related.cpp
+  resourcelocatorurl.cpp
+  secrecy.cpp
+  sortmode.cpp
+  sound.cpp
+  timezone.cpp
+  vcarddrag.cpp
+  vcardtool.cpp
+  ${vcardparser_SRCS}
+)
+
+set(kcontacts_converter_SRCS
+  converter/vcardconverter.cpp
+  converter/ldifconverter.cpp
+)
+
+ecm_qt_declare_logging_category(kcontacts_converter_SRCS HEADER kcontacts_debug.h IDENTIFIER KCONTACTS_LOG CATEGORY_NAME log_kcontacts)
+
+add_library(KF5Contacts ${kcontacts_SRCS} ${kcontacts_converter_SRCS})
+
+generate_export_header(KF5Contacts BASE_NAME kcontacts)
+
+add_library(KF5::Contacts ALIAS KF5Contacts)
+
+target_include_directories(KF5Contacts INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF5}/KContacts>")
+target_include_directories(KF5Contacts PUBLIC "$<BUILD_INTERFACE:${KContacts_SOURCE_DIR}/src;${KContacts_BINARY_DIR}/src>")
+target_include_directories(KF5Contacts PUBLIC "$<BUILD_INTERFACE:${KContacts_SOURCE_DIR}/src/vcardparser>")
+
+target_link_libraries(KF5Contacts
+PUBLIC
+    KF5::CoreAddons
+    Qt5::Gui
+PRIVATE
+    Qt5::DBus
+    KF5::ConfigCore
+    KF5::I18n
+    KF5::Codecs # for the vcard parser
+)
+
+set_target_properties(KF5Contacts PROPERTIES
+    VERSION ${KContacts_VERSION_STRING}
+    SOVERSION ${KContacts_SOVERSION}
+    EXPORT_NAME Contacts
+)
+
+install(TARGETS KF5Contacts EXPORT KF5ContactsTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
+
+ecm_generate_headers(KContacts_CamelCase_HEADERS
+  HEADER_NAMES
+  Address
+  Addressee
+  AddresseeList
+  CalendarUrl
+  ContactGroup
+  ContactGroupTool
+  Email
+  Field
+  Geo
+  Gender
+  Key
+  Lang
+  Impp
+  PhoneNumber
+  Picture
+  Related
+  ResourceLocatorUrl
+  Secrecy
+  SortMode
+  Sound
+  TimeZone
+  VCardDrag
+  PREFIX KContacts
+  REQUIRED_HEADERS KContacts_HEADERS
+)
+
+add_subdirectory(converter)
+
+install(FILES
+    ${CMAKE_CURRENT_BINARY_DIR}/kcontacts_export.h
+    ${KContacts_HEADERS}
+    DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KContacts/kcontacts
+    COMPONENT Devel
+)
+
+install(FILES
+    ${KContacts_CamelCase_HEADERS}
+    DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KContacts/KContacts
+    COMPONENT Devel
+)
+
+
+install(FILES countrytransl.map DESTINATION ${KDE_INSTALL_DATADIR_KF5}/kcontacts)
+
+ecm_generate_pri_file(BASE_NAME KContacts LIB_NAME KF5Contacts DEPS "CoreAddons" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KContacts)
+install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
diff --git a/src/Messages.sh b/src/Messages.sh
new file mode 100644 (file)
index 0000000..98e3c3c
--- /dev/null
@@ -0,0 +1,2 @@
+#! /usr/bin/env bash
+$XGETTEXT *.cpp -o $podir/kcontacts5.pot
diff --git a/src/address.cpp b/src/address.cpp
new file mode 100644 (file)
index 0000000..3fbe0b5
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "address.h"
+
+#include "kcontacts_debug.h"
+#include <krandom.h>
+#include <klocalizedstring.h>
+#include <kconfig.h>
+
+#include <kconfiggroup.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QMap>
+#include <QtCore/QTextStream>
+#include <QtCore/QSharedData>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QLocale>
+#include <QtCore/QDataStream>
+
+using namespace KContacts;
+
+// template tags for address formatting localization
+#define KCONTACTS_FMTTAG_realname   QStringLiteral("%n")
+#define KCONTACTS_FMTTAG_REALNAME   QStringLiteral("%N")
+#define KCONTACTS_FMTTAG_company    QStringLiteral("%cm")
+#define KCONTACTS_FMTTAG_COMPANY    QStringLiteral("%CM")
+#define KCONTACTS_FMTTAG_pobox      QStringLiteral("%p")
+#define KCONTACTS_FMTTAG_street     QStringLiteral("%s")
+#define KCONTACTS_FMTTAG_STREET     QStringLiteral("%S")
+#define KCONTACTS_FMTTAG_zipcode    QStringLiteral("%z")
+#define KCONTACTS_FMTTAG_location   QStringLiteral("%l")
+#define KCONTACTS_FMTTAG_LOCATION   QStringLiteral("%L")
+#define KCONTACTS_FMTTAG_region     QStringLiteral("%r")
+#define KCONTACTS_FMTTAG_REGION     QStringLiteral("%R")
+#define KCONTACTS_FMTTAG_newline    QStringLiteral("\\n")
+#define KCONTACTS_FMTTAG_condcomma  QStringLiteral("%,")
+#define KCONTACTS_FMTTAG_condwhite  QStringLiteral("%w")
+#define KCONTACTS_FMTTAG_purgeempty QStringLiteral("%0")
+
+/**
+  Finds the balanced closing bracket starting from the opening bracket at
+  pos in tsection.
+  @return  position of closing bracket, -1 for unbalanced brackets
+*/
+static int findBalancedBracket(const QString &tsection, int pos)
+{
+    int balancecounter = 0;
+    for (int i = pos + 1; i < tsection.length(); ++i) {
+        if (QLatin1Char(')') == tsection[i] && 0 == balancecounter) {
+            // found end of brackets
+            return i;
+        } else {
+            if (QLatin1Char('(') == tsection[i]) {
+                // nested brackets
+                balancecounter++;
+            }
+        }
+    }
+    return -1;
+}
+
+/**
+  Parses a snippet of an address template
+  @param tsection   the template string to be parsed
+  @param result     QString reference in which the result will be stored
+  @return           true if at least one tag evaluated positively, else false
+*/
+static bool parseAddressTemplateSection(const QString &tsection, QString &result,
+                                        const QString &realName, const QString &orgaName,
+                                        const KContacts::Address &address)
+{
+    // This method first parses and substitutes any bracketed sections and
+    // after that replaces any tags with their values. If a bracketed section
+    // or a tag evaluate to zero, they are not just removed but replaced
+    // with a placeholder. This is because in the last step conditionals are
+    // resolved which depend on information about zero-evaluations.
+    result = tsection;
+    int stpos = 0;
+    bool ret = false;
+
+    // first check for brackets that have to be evaluated first
+    int fpos = result.indexOf(KCONTACTS_FMTTAG_purgeempty, stpos);
+    while (-1 != fpos) {
+        int bpos1 = fpos + KCONTACTS_FMTTAG_purgeempty.length();
+        int bpos2;
+        // expect opening bracket and find next balanced closing bracket. If
+        // next char is no opening bracket, continue parsing (no valid tag)
+        if (QLatin1Char('(') == result[bpos1]) {
+            bpos2 = findBalancedBracket(result, bpos1);
+            if (-1 != bpos2) {
+                // we have balanced brackets, recursively parse:
+                QString rplstr;
+                bool purge = !parseAddressTemplateSection(result.mid(bpos1 + 1,
+                             bpos2 - bpos1 - 1), rplstr,
+                             realName, orgaName, address);
+                if (purge) {
+                    // purge -> remove all
+                    // replace with !_P_!, so conditional tags work later
+                    result.replace(fpos, bpos2 - fpos + 1, QStringLiteral("!_P_!"));
+                    // leave stpos as it is
+                } else {
+                    // no purge -> replace with recursively parsed string
+                    result.replace(fpos, bpos2 - fpos + 1, rplstr);
+                    ret = true;
+                    stpos = fpos + rplstr.length();
+                }
+            } else {
+                // unbalanced brackets:  keep on parsing (should not happen
+                // and will result in bad formatting)
+                stpos = bpos1;
+            }
+        }
+        fpos = result.indexOf(KCONTACTS_FMTTAG_purgeempty, stpos);
+    }
+
+    // after sorting out all purge tags, we just search'n'replace the rest,
+    // keeping track of whether at least one tag evaluates to something.
+    // The following macro needs QString for R_FIELD
+    // It substitutes !_P_! for empty fields so conditional tags work later
+#define REPLTAG(R_TAG,R_FIELD) \
+    if ( result.indexOf( R_TAG ) != -1 ) { \
+        QString rpl = R_FIELD.isEmpty() ? QStringLiteral( "!_P_!" ) : R_FIELD; \
+        result.replace( R_TAG, rpl ); \
+        if ( !R_FIELD.isEmpty() ) { \
+            ret = true; \
+        } \
+    }
+    REPLTAG(KCONTACTS_FMTTAG_realname, realName);
+    REPLTAG(KCONTACTS_FMTTAG_REALNAME, realName.toUpper());
+    REPLTAG(KCONTACTS_FMTTAG_company, orgaName);
+    REPLTAG(KCONTACTS_FMTTAG_COMPANY, orgaName.toUpper());
+    REPLTAG(KCONTACTS_FMTTAG_pobox, address.postOfficeBox());
+    REPLTAG(KCONTACTS_FMTTAG_street, address.street());
+    REPLTAG(KCONTACTS_FMTTAG_STREET, address.street().toUpper());
+    REPLTAG(KCONTACTS_FMTTAG_zipcode, address.postalCode());
+    REPLTAG(KCONTACTS_FMTTAG_location, address.locality());
+    REPLTAG(KCONTACTS_FMTTAG_LOCATION, address.locality().toUpper());
+    REPLTAG(KCONTACTS_FMTTAG_region, address.region());
+    REPLTAG(KCONTACTS_FMTTAG_REGION, address.region().toUpper());
+    result.replace(KCONTACTS_FMTTAG_newline, QLatin1String("\n"));
+#undef REPLTAG
+
+    // conditional comma
+    fpos = result.indexOf(KCONTACTS_FMTTAG_condcomma, 0);
+    while (-1 != fpos) {
+        const QString str1 = result.mid(fpos - 5, 5);
+        const QString str2 = result.mid(fpos + 2, 5);
+        if (str1 != QLatin1String("!_P_!") && str2 != QLatin1String("!_P_!")) {
+            result.replace(fpos, 2, QStringLiteral(", "));
+        } else {
+            result.remove(fpos, 2);
+        }
+        fpos = result.indexOf(KCONTACTS_FMTTAG_condcomma, fpos);
+    }
+    // conditional whitespace
+    fpos = result.indexOf(KCONTACTS_FMTTAG_condwhite, 0);
+    while (-1 != fpos) {
+        const QString str1 = result.mid(fpos - 5, 5);
+        const QString str2 = result.mid(fpos + 2, 5);
+        if (str1 != QLatin1String("!_P_!") && str2 != QLatin1String("!_P_!")) {
+            result.replace(fpos, 2, QStringLiteral(" "));
+        } else {
+            result.remove(fpos, 2);
+        }
+        fpos = result.indexOf(KCONTACTS_FMTTAG_condwhite, fpos);
+    }
+
+    // remove purged:
+    result.remove(QStringLiteral("!_P_!"));
+
+    return ret;
+}
+
+class Q_DECL_HIDDEN Address::Private : public QSharedData
+{
+public:
+    Private()
+        : mEmpty(true)
+        , mType(Q_NULLPTR)
+    {
+        mId = KRandom::randomString(10);
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mEmpty = other.mEmpty;
+        mId = other.mId;
+        mType = other.mType;
+
+        mPostOfficeBox = other.mPostOfficeBox;
+        mExtended = other.mExtended;
+        mStreet = other.mStreet;
+        mLocality = other.mLocality;
+        mRegion = other.mRegion;
+        mPostalCode = other.mPostalCode;
+        mCountry = other.mCountry;
+        mLabel = other.mLabel;
+    }
+
+    bool mEmpty;
+    QString mId;
+    Type mType;
+
+    QString mPostOfficeBox;
+    QString mExtended;
+    QString mStreet;
+    QString mLocality;
+    QString mRegion;
+    QString mPostalCode;
+    QString mCountry;
+    QString mLabel;
+};
+
+Address::Address()
+    : d(new Private)
+{
+}
+
+Address::Address(Type type)
+    : d(new Private)
+{
+    d->mType = type;
+}
+
+Address::Address(const Address &other)
+    : d(other.d)
+{
+}
+
+Address::~Address()
+{
+}
+
+Address &Address::operator=(const Address &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool Address::operator==(const Address &other) const
+{
+    if (d->mId != other.d->mId) {
+        return false;
+    }
+    if (d->mType != other.d->mType) {
+        return false;
+    }
+    if (d->mPostOfficeBox != other.d->mPostOfficeBox) {
+        return false;
+    }
+    if (d->mExtended != other.d->mExtended) {
+        return false;
+    }
+    if (d->mStreet != other.d->mStreet) {
+        return false;
+    }
+    if (d->mLocality != other.d->mLocality) {
+        return false;
+    }
+    if (d->mRegion != other.d->mRegion) {
+        return false;
+    }
+    if (d->mPostalCode != other.d->mPostalCode) {
+        return false;
+    }
+    if (d->mCountry != other.d->mCountry) {
+        return false;
+    }
+    if (d->mLabel != other.d->mLabel) {
+        return false;
+    }
+
+    return true;
+}
+
+bool Address::operator!=(const Address &a) const
+{
+    return !(a == *this);
+}
+
+bool Address::isEmpty() const
+{
+    return d->mEmpty;
+}
+
+void Address::clear()
+{
+    *this = Address();
+}
+
+void Address::setId(const QString &id)
+{
+    d->mEmpty = false;
+    d->mId = id;
+}
+
+QString Address::id() const
+{
+    return d->mId;
+}
+
+void Address::setType(Type type)
+{
+    d->mEmpty = false;
+    d->mType = type;
+}
+
+Address::Type Address::type() const
+{
+    return d->mType;
+}
+
+QString Address::typeLabel(Type type)
+{
+    QString label;
+    bool first = true;
+    const TypeList list = typeList();
+
+    TypeList::ConstIterator it;
+    TypeList::ConstIterator end(list.end());
+    for (it = list.begin(); it != end; ++it) {
+        // these are actually flags
+        const TypeFlag flag = static_cast<TypeFlag>(static_cast<int>(*it));
+        if (type & flag) {
+            if (!first) {
+                label.append(QLatin1Char('/'));
+            }
+
+            label.append(typeFlagLabel(flag));
+
+            if (first) {
+                first = false;
+            }
+        }
+    }
+    return label;
+}
+
+QString Address::typeLabel() const
+{
+    QString label;
+    bool first = true;
+
+    const TypeList list = typeList();
+
+    TypeList::ConstIterator it;
+    for (it = list.begin(); it != list.end(); ++it) {
+        if ((type() & (*it)) && ((*it) != Pref)) {
+            if (!first) {
+                label.append(QLatin1Char('/'));
+            }
+            label.append(typeLabel(*it));
+            if (first) {
+                first = false;
+            }
+        }
+    }
+
+    return label;
+}
+
+void Address::setPostOfficeBox(const QString &postOfficeBox)
+{
+    d->mEmpty = false;
+    d->mPostOfficeBox = postOfficeBox;
+}
+
+QString Address::postOfficeBox() const
+{
+    return d->mPostOfficeBox;
+}
+
+QString Address::postOfficeBoxLabel()
+{
+    return i18n("Post Office Box");
+}
+
+void Address::setExtended(const QString &extended)
+{
+    d->mEmpty = false;
+    d->mExtended = extended;
+}
+
+QString Address::extended() const
+{
+    return d->mExtended;
+}
+
+QString Address::extendedLabel()
+{
+    return i18n("Extended Address Information");
+}
+
+void Address::setStreet(const QString &street)
+{
+    d->mEmpty = false;
+    d->mStreet = street;
+}
+
+QString Address::street() const
+{
+    return d->mStreet;
+}
+
+QString Address::streetLabel()
+{
+    return i18n("Street");
+}
+
+void Address::setLocality(const QString &locality)
+{
+    d->mEmpty = false;
+    d->mLocality = locality;
+}
+
+QString Address::locality() const
+{
+    return d->mLocality;
+}
+
+QString Address::localityLabel()
+{
+    return i18n("Locality");
+}
+
+void Address::setRegion(const QString &region)
+{
+    d->mEmpty = false;
+    d->mRegion = region;
+}
+
+QString Address::region() const
+{
+    return d->mRegion;
+}
+
+QString Address::regionLabel()
+{
+    return i18n("Region");
+}
+
+void Address::setPostalCode(const QString &postalCode)
+{
+    d->mEmpty = false;
+    d->mPostalCode = postalCode;
+}
+
+QString Address::postalCode() const
+{
+    return d->mPostalCode;
+}
+
+QString Address::postalCodeLabel()
+{
+    return i18n("Postal Code");
+}
+
+void Address::setCountry(const QString &country)
+{
+    d->mEmpty = false;
+    d->mCountry = country;
+}
+
+QString Address::country() const
+{
+    return d->mCountry;
+}
+
+QString Address::countryLabel()
+{
+    return i18n("Country");
+}
+
+void Address::setLabel(const QString &label)
+{
+    d->mEmpty = false;
+    d->mLabel = label;
+}
+
+QString Address::label() const
+{
+    return d->mLabel;
+}
+
+QString Address::labelLabel()
+{
+    return i18n("Delivery Label");
+}
+
+Address::TypeList Address::typeList()
+{
+    static TypeList list;
+
+    if (list.isEmpty()) {
+        list << Dom << Intl << Postal << Parcel << Home << Work << Pref;
+    }
+
+    return list;
+}
+
+QString Address::typeFlagLabel(TypeFlag type)
+{
+    switch (type) {
+    case Dom:
+        return i18nc("Address is in home country", "Domestic");
+    case Intl:
+        return i18nc("Address is not in home country", "International");
+    case Postal:
+        return i18nc("Address for delivering letters", "Postal");
+    case Parcel:
+        return i18nc("Address for delivering packages", "Parcel");
+    case Home:
+        return i18nc("Home Address", "Home");
+    case Work:
+        return i18nc("Work Address", "Work");
+    case Pref:
+        return i18n("Preferred Address");
+    default:
+        return i18nc("another type of address", "Other");
+    }
+}
+
+QString Address::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Address {\n");
+    str += QStringLiteral("  IsEmpty: %1\n").
+           arg(d->mEmpty ? QStringLiteral("true") : QStringLiteral("false"));
+    str += QStringLiteral("  Id: %1\n").arg(d->mId);
+    str += QStringLiteral("  Type: %1\n").arg(typeLabel(d->mType));
+    str += QStringLiteral("  Post office box: %1\n").arg(d->mPostOfficeBox);
+    str += QStringLiteral("  Extended: %1\n").arg(d->mExtended);
+    str += QStringLiteral("  Street: %1\n").arg(d->mStreet);
+    str += QStringLiteral("  Locality: %1\n").arg(d->mLocality);
+    str += QStringLiteral("  Region: %1\n").arg(d->mRegion);
+    str += QStringLiteral("  Postal code: %1\n").arg(d->mPostalCode);
+    str += QStringLiteral("  Country: %1\n").arg(d->mCountry);
+    str += QStringLiteral("  Label: %1\n").arg(d->mLabel);
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+QString Address::formattedAddress(const QString &realName,
+                                  const QString &orgaName) const
+{
+    QString ciso;
+    QString addrTemplate;
+    QString ret;
+
+    // FIXME: first check for iso-country-field and prefer that one
+    if (!country().isEmpty()) {
+        ciso = countryToISO(country());
+    } else {
+        // fall back to our own country
+        ciso = QLocale().bcp47Name();
+    }
+    KConfig entry(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + ciso + QLatin1String("/country.desktop")));
+
+    KConfigGroup group = entry.group("KCM Locale");
+    // decide whether this needs special business address formatting
+    if (orgaName.isEmpty()) {
+        addrTemplate = group.readEntry("AddressFormat");
+    } else {
+        addrTemplate = group.readEntry("BusinessAddressFormat");
+        if (addrTemplate.isEmpty()) {
+            addrTemplate = group.readEntry("AddressFormat");
+        }
+    }
+
+    // in the case there's no format found at all, default to what we've always
+    // used:
+    if (addrTemplate.isEmpty()) {
+        qCWarning(KCONTACTS_LOG) << "address format database incomplete"
+                                 << "(no format for locale" << ciso
+                                 << "found). Using default address formatting.";
+        addrTemplate = QStringLiteral("%0(%n\\n)%0(%cm\\n)%0(%s\\n)%0(PO BOX %p\\n)%0(%l%w%r)%,%z");
+    }
+
+    // scan
+    parseAddressTemplateSection(addrTemplate, ret, realName, orgaName, *this);
+
+    // now add the country line if needed (formatting this time according to
+    // the rules of our own system country )
+    if (!country().isEmpty()) {
+        KConfig entry(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") +
+                                             QLocale().name() +
+                                             QLatin1String("/country.desktop")));
+        KConfigGroup group = entry.group("KCM Locale");
+        QString cpos = group.readEntry("AddressCountryPosition");
+        if (QLatin1String("BELOW") == cpos || cpos.isEmpty()) {
+            ret = ret + QLatin1String("\n\n") + country().toUpper();
+        } else if (QLatin1String("below") == cpos) {
+            ret = ret + QLatin1String("\n\n") + country();
+        } else if (QLatin1String("ABOVE") == cpos) {
+            ret = country().toUpper() + QLatin1String("\n\n") + ret;
+        } else if (QLatin1String("above") == cpos) {
+            ret = country() + QLatin1String("\n\n") + ret;
+        }
+    }
+
+    return ret;
+}
+
+typedef QMap<QString, QString> StringMap;
+Q_GLOBAL_STATIC(StringMap, sISOMap)
+
+QString Address::countryToISO(const QString &cname)
+{
+    // we search a map file for translations from country names to
+    // iso codes, storing caching things in a QMap for faster future
+    // access.
+
+    QMap<QString, QString>::ConstIterator it;
+    it = sISOMap->constFind(cname);
+    if (it != sISOMap->constEnd()) {
+        return it.value();
+    }
+
+    QString mapfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kcontacts/countrytransl.map"));
+    if (mapfile.isEmpty()) {
+        qWarning() << "Installation error, couldn't find the countrytransl.map file";
+    }
+
+    QFile file(mapfile);
+    if (file.open(QIODevice::ReadOnly)) {
+        QTextStream s(&file);
+        QString strbuf = s.readLine();
+        while (!strbuf.isEmpty()) {
+            QStringList countryInfo = strbuf.split(QLatin1Char('\t'), QString::KeepEmptyParts);
+            if (countryInfo[0] == cname) {
+                file.close();
+                sISOMap->insert(cname, countryInfo[1]);
+                return countryInfo[1];
+            }
+            strbuf = s.readLine();
+        }
+        file.close();
+    }
+
+    // fall back to system country
+    QString systemCountry = QLocale().bcp47Name();
+    sISOMap->insert(cname, systemCountry);
+    return systemCountry;
+}
+
+QString Address::ISOtoCountry(const QString &ISOname)
+{
+    // get country name from ISO country code (e.g. "no" -> i18n("Norway"))
+    if (ISOname.simplified().isEmpty()) {
+        return QString();
+    }
+
+    QString mapfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kabc/countrytransl.map"));
+
+    QFile file(mapfile);
+    if (file.open(QIODevice::ReadOnly)) {
+        QTextStream s(&file);
+        QString searchStr = QLatin1Char('\t') + ISOname.simplified().toLower();
+        QString strbuf = s.readLine();
+        int pos;
+        while (!strbuf.isEmpty()) {
+            if ((pos = strbuf.indexOf(searchStr)) != -1) {
+                file.close();
+                return i18n(strbuf.left(pos).toUtf8().constData());
+            }
+            strbuf = s.readLine();
+        }
+        file.close();
+    }
+
+    return ISOname;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Address &addr)
+{
+    return s << addr.d->mId << (uint)addr.d->mType << addr.d->mPostOfficeBox
+           << addr.d->mExtended << addr.d->mStreet << addr.d->mLocality
+           << addr.d->mRegion << addr.d->mPostalCode << addr.d->mCountry
+           << addr.d->mLabel << addr.d->mEmpty;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Address &addr)
+{
+    uint type;
+    s >> addr.d->mId >> type >> addr.d->mPostOfficeBox >> addr.d->mExtended
+      >> addr.d->mStreet >> addr.d->mLocality >> addr.d->mRegion
+      >> addr.d->mPostalCode >> addr.d->mCountry >> addr.d->mLabel
+      >> addr.d->mEmpty;
+
+    addr.d->mType = Address::Type(type);
+
+    return s;
+}
diff --git a/src/address.h b/src/address.h
new file mode 100644 (file)
index 0000000..9784c5b
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_ADDRESS_H
+#define KCONTACTS_ADDRESS_H
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+#include "kcontacts_export.h"
+
+namespace KContacts
+{
+
+/**
+  @brief
+  Postal address information.
+
+  This class represents information about a postal address.
+*/
+class KCONTACTS_EXPORT Address
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &s, const Address &addr);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &s, Address &addr);
+
+public:
+    /**
+      List of addresses.
+    */
+    typedef QVector<Address> List;
+
+    /**
+      Address types:
+    */
+    enum TypeFlag {
+        Dom = 1,     /**< domestic */
+        Intl = 2,    /**< international */
+        Postal = 4,  /**< postal */
+        Parcel = 8,  /**< parcel */
+        Home = 16,   /**< home address */
+        Work = 32,   /**< address at work */
+        Pref = 64    /**< preferred address */
+    };
+
+    Q_DECLARE_FLAGS(Type, TypeFlag)
+
+    /**
+      List of address types.
+    */
+    typedef QList<TypeFlag> TypeList;
+
+    /**
+      Creates an empty address.
+    */
+    Address();
+
+    /**
+      Creates an address of the given @p type.
+    */
+    Address(Type type);
+
+    /**
+      Copy constructor.
+    */
+    Address(const Address &address);
+
+    /**
+      Destroys the address.
+    */
+    ~Address();
+
+    /**
+      Equality operator.
+
+      @param addr the address to compare to
+      @return @c true if @c this and @p addr are equal, otherwise @c false
+    */
+    bool operator==(const Address &other) const;
+
+    /**
+      Not-equal operator.
+
+      @param addr the address to compare to
+      @return @c true if @c this and @p addr are not equal, otherwise @c false
+    */
+    bool operator!=(const Address &other) const;
+
+    /**
+      Assignment operator.
+
+      @param addr the address data to assign to @c this
+      @return a reference to @c this
+    */
+    Address &operator=(const Address &other);
+
+    /**
+      Returns true, if the address is empty.
+    */
+    bool isEmpty() const;
+
+    /**
+      Clears all entries of the address.
+    */
+    void clear();
+
+    /**
+      Sets the unique @p identifier.
+    */
+    void setId(const QString &identifier);
+
+    /**
+      Returns the unique identifier.
+    */
+    QString id() const;
+
+    /**
+      Sets the type of address. See enum for definiton of types.
+
+      @param type type, can be a bitwise or of multiple types.
+    */
+    void setType(Type type);
+
+    /**
+      Returns the type of address. Can be a bitwise or of multiple types.
+    */
+    Type type() const;
+
+    /**
+      Returns a translated string of all types the address has.
+    */
+    QString typeLabel() const;
+
+    /**
+      Sets the post office box.
+    */
+    void setPostOfficeBox(const QString &postOfficeBox);
+
+    /**
+      Returns the post office box.
+    */
+    QString postOfficeBox() const;
+
+    /**
+      Returns the translated label for post office box field.
+    */
+    static QString postOfficeBoxLabel();
+
+    /**
+      Sets the @p extended address information.
+    */
+    void setExtended(const QString &extended);
+
+    /**
+      Returns the extended address information.
+    */
+    QString extended() const;
+
+    /**
+      Returns the translated label for extended field.
+    */
+    static QString extendedLabel();
+
+    /**
+      Sets the @p street (including house number).
+    */
+    void setStreet(const QString &street);
+
+    /**
+      Returns the street.
+    */
+    QString street() const;
+
+    /**
+      Returns the translated label for street field.
+    */
+    static QString streetLabel();
+
+    /**
+      Sets the @p locality, e.g. city.
+
+      @param locality the locality of the address, e.g. city
+    */
+    void setLocality(const QString &locality);
+
+    /**
+      Returns the locality.
+    */
+    QString locality() const;
+
+    /**
+      Returns the translated label for locality field.
+    */
+    static QString localityLabel();
+
+    /**
+      Sets the @p region, e.g. state.
+
+      @param region the region the address falls into, e.g. state
+    */
+    void setRegion(const QString &region);
+
+    /**
+      Returns the region.
+    */
+    QString region() const;
+
+    /**
+      Returns the translated label for region field.
+    */
+    static QString regionLabel();
+
+    /**
+      Sets the postal @p code.
+    */
+    void setPostalCode(const QString &code);
+
+    /**
+      Returns the postal code.
+    */
+    QString postalCode() const;
+
+    /**
+      Returns the translated label for postal code field.
+    */
+    static QString postalCodeLabel();
+
+    /**
+      Sets the @p country.
+    */
+    void setCountry(const QString &country);
+
+    /**
+      Returns the country.
+    */
+    QString country() const;
+
+    /**
+      Returns the translated label for country field.
+    */
+    static QString countryLabel();
+
+    /**
+      Sets the delivery @p label. This is the literal text to be used as label.
+
+      @param label the string to use for delivery labels
+    */
+    void setLabel(const QString &label);
+
+    /**
+      Returns the delivery label.
+    */
+    QString label() const;
+
+    /**
+      Returns the translated label for delivery label field.
+    */
+    static QString labelLabel();
+
+    /**
+      Returns the list of available types.
+    */
+    static TypeList typeList();
+
+    /**
+      Returns the translated label for the given @p type.
+    */
+    static QString typeLabel(Type type);
+
+    /**
+      Returns a string representation of the address.
+    */
+    QString toString() const;
+
+    /**
+      Returns this address formatted according to the country-specific
+      address formatting rules. The formatting rules applied depend on
+      either the addresses {@link #country country} field, or (if the
+      latter is empty) on the system country setting. If companyName is
+      provided, an available business address format will be preferred.
+
+      @param realName   the formatted name of the contact
+      @param orgaName   the name of the organization or company
+      @return           the formatted address (containing newline characters)
+    */
+    QString formattedAddress(const QString &realName = QString(),
+                             const QString &orgaName = QString()) const;
+
+    /**
+      Returns ISO code for a localized country name. Only localized country
+      names will be understood. This might be replaced by a KLocale method in
+      the future.
+      @param cname  name of the country
+      @return       two digit ISO code
+    */
+    static QString countryToISO(const QString &cname);
+
+    /**
+      Returns a localized country name for a ISO code.
+      This might be replaced by a KLocale method in the future.
+      @param ISOname two digit ISO code
+      @return        localized name of the country
+    */
+    static QString ISOtoCountry(const QString &ISOname);
+
+    static QString typeFlagLabel(TypeFlag type);
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Address::Type)
+
+/**
+  Serializes the @p address object into the @p stream.
+*/
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Address &address);
+
+/**
+  Initializes the @p address object from the @p stream.
+*/
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Address &address);
+
+}
+
+#endif
diff --git a/src/addressee.cpp b/src/addressee.cpp
new file mode 100644 (file)
index 0000000..1894e2a
--- /dev/null
@@ -0,0 +1,2381 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+    Copyright (c) 2003 Carsten Pfeiffer <pfeiffer@kde.org>
+    Copyright (c) 2005 Ingo Kloecker <kloecker@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include <QtCore/QList>
+#include <QtCore/QRegExp>
+#include <QtCore/QSharedData>
+#include <QtCore/QUuid>
+
+#include "kcontacts_debug.h"
+#include <klocalizedstring.h>
+
+#include "addresseehelper.h"
+#include "field.h"
+#include "sortmode.h"
+
+#include "addressee.h"
+
+using namespace KContacts;
+
+static bool matchBinaryPattern(int value, int pattern);
+
+template <class L>
+static bool listEquals(const QVector<L> &list, const QVector<L> &pattern);
+static bool listEquals(const QStringList &list, const QStringList &pattern);
+
+class Q_DECL_HIDDEN Addressee::Private : public QSharedData
+{
+public:
+    Private()
+        : mUid(QUuid::createUuid().toString().mid(1, 36))
+        , mEmpty(true)
+        , mChanged(false)
+    {
+        //We avoid the curly braces so the string is RFC4122 compliant and can be used as urn
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mUid = other.mUid;
+        mName = other.mName;
+        mFormattedName = other.mFormattedName;
+        mFamilyName = other.mFamilyName;
+        mGivenName = other.mGivenName;
+        mAdditionalName = other.mAdditionalName;
+        mPrefix = other.mPrefix;
+        mSuffix = other.mSuffix;
+        mNickName = other.mNickName;
+        mBirthday = other.mBirthday;
+        mMailer = other.mMailer;
+        mTimeZone = other.mTimeZone;
+        mGeo = other.mGeo;
+        mTitle = other.mTitle;
+        mRole = other.mRole;
+        mOrganization = other.mOrganization;
+        mDepartment = other.mDepartment;
+        mNote = other.mNote;
+        mProductId = other.mProductId;
+        mRevision = other.mRevision;
+        mSortString = other.mSortString;
+        mSecrecy = other.mSecrecy;
+        mLogo = other.mLogo;
+        mPhoto = other.mPhoto;
+        mSound = other.mSound;
+
+        mPhoneNumbers = other.mPhoneNumbers;
+        mAddresses = other.mAddresses;
+        mKeys = other.mKeys;
+        mLangs =  other.mLangs;
+        mGender = other.mGender;
+        mEmails = other.mEmails;
+        mCategories = other.mCategories;
+        mCustomFields = other.mCustomFields;
+        mCalendarUrl = other.mCalendarUrl;
+        mSoundListExtra = other.mSoundListExtra;
+        mPhotoExtraList = other.mPhotoExtraList;
+        mLogoExtraList = other.mLogoExtraList;
+        mUrlExtraList = other.mUrlExtraList;
+        mMembers = other.mMembers;
+        mRelationShips = other.mRelationShips;
+        mSources = other.mSources;
+        mEmpty = other.mEmpty;
+        mImpps = other.mImpps;
+        mChanged = other.mChanged;
+    }
+
+    ~Private()
+    {
+    }
+
+    QString mUid;
+    QString mName;
+    QString mFormattedName;
+    QString mFamilyName;
+    QString mGivenName;
+    QString mAdditionalName;
+    QString mPrefix;
+    QString mSuffix;
+    QString mNickName;
+    QDateTime mBirthday;
+    QString mMailer;
+    TimeZone mTimeZone;
+    Geo mGeo;
+    QString mTitle;
+    QString mRole;
+    QString mOrganization;
+    QString mDepartment;
+    QString mNote;
+    QString mProductId;
+    QDateTime mRevision;
+    QString mSortString;
+    Secrecy mSecrecy;
+    Picture mLogo;
+    Picture mPhoto;
+    Sound mSound;
+
+    PhoneNumber::List mPhoneNumbers;
+    Address::List mAddresses;
+    Key::List mKeys;
+    Email::List mEmails;
+    Lang::List mLangs;
+    Impp::List mImpps;
+    Gender mGender;
+    QString mKind;
+    QStringList mCategories;
+    QHash<QString, QString> mCustomFields;
+    CalendarUrl::List mCalendarUrl;
+    Sound::List mSoundListExtra;
+    Picture::List mPhotoExtraList;
+    Picture::List mLogoExtraList;
+    ResourceLocatorUrl::List mUrlExtraList;
+    QVector<QUrl> mSources;
+    QStringList mMembers;
+    Related::List mRelationShips;
+    bool mEmpty    : 1;
+    bool mChanged  : 1;
+
+    static KContacts::SortMode *mSortMode;
+};
+
+KContacts::SortMode *Addressee::Private::mSortMode = Q_NULLPTR;
+
+Addressee::Addressee()
+    : d(new Private)
+{
+}
+
+Addressee::~Addressee()
+{
+}
+
+Addressee::Addressee(const Addressee &other)
+    : d(other.d)
+{
+}
+
+Addressee &Addressee::operator=(const Addressee &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool Addressee::operator==(const Addressee &addressee) const
+{
+    if (d->mUid != addressee.d->mUid) {
+        qCDebug(KCONTACTS_LOG) << "uid differs";
+        return false;
+    }
+
+    if (d->mName != addressee.d->mName &&
+            !(d->mName.isEmpty() && addressee.d->mName.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "name differs";
+        return false;
+    }
+
+    if (d->mFormattedName != addressee.d->mFormattedName &&
+            !(d->mFormattedName.isEmpty() && addressee.d->mFormattedName.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "formattedName differs";
+        return false;
+    }
+
+    if (d->mFamilyName != addressee.d->mFamilyName &&
+            !(d->mFamilyName.isEmpty() && addressee.d->mFamilyName.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "familyName differs";
+        return false;
+    }
+
+    if (d->mGivenName != addressee.d->mGivenName &&
+            !(d->mGivenName.isEmpty() && addressee.d->mGivenName.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "givenName differs";
+        return false;
+    }
+
+    if (d->mAdditionalName != addressee.d->mAdditionalName &&
+            !(d->mAdditionalName.isEmpty() && addressee.d->mAdditionalName.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "additionalName differs";
+        return false;
+    }
+
+    if (d->mPrefix != addressee.d->mPrefix &&
+            !(d->mPrefix.isEmpty() && addressee.d->mPrefix.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "prefix differs";
+        return false;
+    }
+
+    if (d->mSuffix != addressee.d->mSuffix &&
+            !(d->mSuffix.isEmpty() && addressee.d->mSuffix.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "suffix differs";
+        return false;
+    }
+
+    if (d->mNickName != addressee.d->mNickName &&
+            !(d->mNickName.isEmpty() && addressee.d->mNickName.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "nickName differs";
+        return false;
+    }
+
+    if (d->mBirthday != addressee.d->mBirthday) {
+        qCDebug(KCONTACTS_LOG) << "birthday differs";
+        return false;
+    }
+
+    if (d->mMailer != addressee.d->mMailer &&
+            !(d->mMailer.isEmpty() && addressee.d->mMailer.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "mailer differs";
+        return false;
+    }
+
+    if (d->mTimeZone != addressee.d->mTimeZone) {
+        qCDebug(KCONTACTS_LOG) << "timeZone differs";
+        return false;
+    }
+
+    if (d->mGeo != addressee.d->mGeo) {
+        qCDebug(KCONTACTS_LOG) << "geo differs";
+        return false;
+    }
+
+    if (d->mTitle != addressee.d->mTitle &&
+            !(d->mTitle.isEmpty() && addressee.d->mTitle.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "title differs";
+        return false;
+    }
+
+    if (d->mRole != addressee.d->mRole &&
+            !(d->mRole.isEmpty() && addressee.d->mRole.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "role differs";
+        return false;
+    }
+
+    if (d->mOrganization != addressee.d->mOrganization &&
+            !(d->mOrganization.isEmpty() && addressee.d->mOrganization.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "organization differs";
+        return false;
+    }
+
+    if (d->mDepartment != addressee.d->mDepartment &&
+            !(d->mDepartment.isEmpty() && addressee.d->mDepartment.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "department differs";
+        return false;
+    }
+
+    if (d->mNote != addressee.d->mNote &&
+            !(d->mNote.isEmpty() && addressee.d->mNote.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "note differs";
+        return false;
+    }
+
+    if (d->mProductId != addressee.d->mProductId &&
+            !(d->mProductId.isEmpty() && addressee.d->mProductId.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "productId differs";
+        return false;
+    }
+
+    if (d->mSortString != addressee.d->mSortString &&
+            !(d->mSortString.isEmpty() && addressee.d->mSortString.isEmpty())) {
+        qCDebug(KCONTACTS_LOG) << "sortString differs";
+        return false;
+    }
+
+    if (d->mSecrecy != addressee.d->mSecrecy) {
+        qCDebug(KCONTACTS_LOG) << "secrecy differs";
+        return false;
+    }
+
+    if (d->mLogo != addressee.d->mLogo) {
+        qCDebug(KCONTACTS_LOG) << "logo differs";
+        return false;
+    }
+
+    if (d->mPhoto != addressee.d->mPhoto) {
+        qCDebug(KCONTACTS_LOG) << "photo differs";
+        return false;
+    }
+
+    if (d->mSound != addressee.d->mSound) {
+        qCDebug(KCONTACTS_LOG) << "sound differs";
+        return false;
+    }
+
+    if (!listEquals(d->mPhoneNumbers, addressee.d->mPhoneNumbers)) {
+        qCDebug(KCONTACTS_LOG) << "phoneNumbers differs";
+        return false;
+    }
+
+    if (!listEquals(d->mAddresses, addressee.d->mAddresses)) {
+        qCDebug(KCONTACTS_LOG) << "addresses differs";
+        return false;
+    }
+
+    if (!listEquals(d->mKeys, addressee.d->mKeys)) {
+        qCDebug(KCONTACTS_LOG) << "keys differs";
+        return false;
+    }
+    if (!listEquals(d->mEmails, addressee.d->mEmails)) {
+        qCDebug(KCONTACTS_LOG) << "emails differs";
+        return false;
+    }
+
+    if (!listEquals(d->mCategories, addressee.d->mCategories)) {
+        qCDebug(KCONTACTS_LOG) << "categories differs";
+        return false;
+    }
+
+    if (d->mCustomFields != addressee.d->mCustomFields) {
+        qCDebug(KCONTACTS_LOG) << "custom differs";
+        return false;
+    }
+    if (d->mLangs != addressee.d->mLangs) {
+        qCDebug(KCONTACTS_LOG) << "langs differs";
+        return false;
+    }
+    if (d->mImpps != addressee.d->mImpps) {
+        qCDebug(KCONTACTS_LOG) << "impps differs";
+        return false;
+    }
+    if (d->mGender != addressee.d->mGender) {
+        qCDebug(KCONTACTS_LOG) << "gender differs";
+        return false;
+    }
+    if (d->mKind != addressee.d->mKind) {
+        qCDebug(KCONTACTS_LOG) << "kind differs";
+        return false;
+    }
+    if (!listEquals(d->mCalendarUrl, addressee.d->mCalendarUrl)) {
+        qCDebug(KCONTACTS_LOG) << "calendarUrl differs";
+        return false;
+    }
+    if (!listEquals(d->mSoundListExtra, addressee.d->mSoundListExtra)) {
+        qCDebug(KCONTACTS_LOG) << "Extra sound differs";
+        return false;
+    }
+    if (!listEquals(d->mPhotoExtraList, addressee.d->mPhotoExtraList)) {
+        qCDebug(KCONTACTS_LOG) << "Extra photo differs";
+        return false;
+    }
+    if (!listEquals(d->mLogoExtraList, addressee.d->mLogoExtraList)) {
+        qCDebug(KCONTACTS_LOG) << "Extra logo differs";
+        return false;
+    }
+    if (!listEquals(d->mUrlExtraList, addressee.d->mUrlExtraList)) {
+        qCDebug(KCONTACTS_LOG) << "Extra url differs";
+        return false;
+    }
+    if (!listEquals(d->mMembers, addressee.d->mMembers)) {
+        qCDebug(KCONTACTS_LOG) << "Extra url differs";
+        return false;
+    }
+    if (!listEquals(d->mRelationShips, addressee.d->mRelationShips)) {
+        qCDebug(KCONTACTS_LOG) << "RelationShips differs";
+        return false;
+    }
+    if (!listEquals(d->mSources, addressee.d->mSources)) {
+        qCDebug(KCONTACTS_LOG) << "Sources differs";
+        return false;
+    }
+
+    return true;
+}
+
+bool Addressee::operator!=(const Addressee &a) const
+{
+    return !(a == *this);
+}
+
+bool Addressee::isEmpty() const
+{
+    return d->mEmpty;
+}
+
+void Addressee::setUid(const QString &id)
+{
+    if (id == d->mUid) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mUid = id;
+}
+
+QString Addressee::uid() const
+{
+    return d->mUid;
+}
+
+QString Addressee::uidLabel()
+{
+    return i18n("Unique Identifier");
+}
+
+void Addressee::setName(const QString &name)
+{
+    if (name == d->mName) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mName = name;
+}
+
+QString Addressee::name() const
+{
+    return d->mName;
+}
+
+QString Addressee::nameLabel()
+{
+    return i18n("Name");
+}
+
+void Addressee::setKind(const QString &kind)
+{
+    if (kind == d->mKind) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mKind = kind;
+}
+
+QString Addressee::kind() const
+{
+    return d->mKind;
+}
+
+void Addressee::insertExtraSound(const Sound &sound)
+{
+    d->mEmpty = false;
+    d->mSoundListExtra.append(sound);
+}
+
+Sound::List Addressee::extraSoundList() const
+{
+    return d->mSoundListExtra;
+}
+
+void Addressee::insertExtraPhoto(const Picture &picture)
+{
+    d->mEmpty = false;
+    d->mPhotoExtraList.append(picture);
+}
+
+Picture::List Addressee::extraPhotoList() const
+{
+    return d->mPhotoExtraList;
+}
+
+void Addressee::insertExtraLogo(const Picture &logo)
+{
+    d->mEmpty = false;
+    d->mLogoExtraList.append(logo);
+}
+
+Picture::List Addressee::extraLogoList() const
+{
+    return d->mLogoExtraList;
+}
+
+void Addressee::setExtraSoundList(const Sound::List &soundList)
+{
+    d->mEmpty = false;
+    d->mSoundListExtra = soundList;
+}
+
+void Addressee::setExtraPhotoList(const Picture::List &pictureList)
+{
+    d->mEmpty = false;
+    d->mPhotoExtraList = pictureList;
+}
+
+void Addressee::setExtraLogoList(const Picture::List &logoList)
+{
+    d->mEmpty = false;
+    d->mLogoExtraList = logoList;
+}
+
+void Addressee::insertExtraUrl(const ResourceLocatorUrl &url)
+{
+    if (url.isValid()) {
+        d->mEmpty = false;
+        d->mUrlExtraList.append(url);
+    }
+}
+
+void Addressee::setExtraUrlList(const ResourceLocatorUrl::List &urlList)
+{
+    d->mEmpty = false;
+    d->mUrlExtraList = urlList;
+}
+
+ResourceLocatorUrl::List Addressee::extraUrlList() const
+{
+    return d->mUrlExtraList;
+}
+
+void Addressee::insertSourceUrl(const QUrl &url)
+{
+    d->mEmpty = false;
+    d->mSources.append(url);
+}
+
+void Addressee::setSourcesUrlList(const QVector<QUrl> &urlList)
+{
+    d->mEmpty = false;
+    d->mSources = urlList;
+}
+
+QVector<QUrl> Addressee::sourcesUrlList() const
+{
+    return d->mSources;
+}
+
+void Addressee::insertImpp(const Impp &impp)
+{
+    if (impp.isValid()) {
+        d->mEmpty = false;
+        //Don't duplicate ?
+        d->mImpps.append(impp);
+    }
+}
+
+void Addressee::setImppList(const Impp::List &imppList)
+{
+    d->mEmpty = false;
+    d->mImpps = imppList;
+}
+
+Impp::List Addressee::imppList() const
+{
+    return d->mImpps;
+}
+
+void Addressee::insertCalendarUrl(const CalendarUrl &calendarUrl)
+{
+    d->mEmpty = false;
+    //TODO verify that there is not same calendarurl
+    if (calendarUrl.isValid()) {
+        d->mCalendarUrl.append(calendarUrl);
+    }
+}
+
+CalendarUrl::List Addressee::calendarUrlList() const
+{
+    return d->mCalendarUrl;
+}
+
+void Addressee::setFormattedName(const QString &formattedName)
+{
+    if (formattedName == d->mFormattedName) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mFormattedName = formattedName;
+}
+
+QString Addressee::formattedName() const
+{
+    return d->mFormattedName;
+}
+
+QString Addressee::formattedNameLabel()
+{
+    return i18n("Formatted Name");
+}
+
+void Addressee::setFamilyName(const QString &familyName)
+{
+    if (familyName == d->mFamilyName) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mFamilyName = familyName;
+}
+
+QString Addressee::familyName() const
+{
+    return d->mFamilyName;
+}
+
+QString Addressee::familyNameLabel()
+{
+    return i18n("Family Name");
+}
+
+void Addressee::setGivenName(const QString &givenName)
+{
+    if (givenName == d->mGivenName) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mGivenName = givenName;
+}
+
+QString Addressee::givenName() const
+{
+    return d->mGivenName;
+}
+
+QString Addressee::givenNameLabel()
+{
+    return i18n("Given Name");
+}
+
+void Addressee::setAdditionalName(const QString &additionalName)
+{
+    if (additionalName == d->mAdditionalName) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mAdditionalName = additionalName;
+}
+
+QString Addressee::additionalName() const
+{
+    return d->mAdditionalName;
+}
+
+QString Addressee::additionalNameLabel()
+{
+    return i18n("Additional Names");
+}
+
+void Addressee::setPrefix(const QString &prefix)
+{
+    if (prefix == d->mPrefix) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mPrefix = prefix;
+}
+
+QString Addressee::prefix() const
+{
+    return d->mPrefix;
+}
+
+QString Addressee::prefixLabel()
+{
+    return i18n("Honorific Prefixes");
+}
+
+void Addressee::setSuffix(const QString &suffix)
+{
+    if (suffix == d->mSuffix) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mSuffix = suffix;
+}
+
+QString Addressee::suffix() const
+{
+    return d->mSuffix;
+}
+
+QString Addressee::suffixLabel()
+{
+    return i18n("Honorific Suffixes");
+}
+
+void Addressee::setNickName(const QString &nickName)
+{
+    if (nickName == d->mNickName) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mNickName = nickName;
+}
+
+QString Addressee::nickName() const
+{
+    return d->mNickName;
+}
+
+QString Addressee::nickNameLabel()
+{
+    return i18n("Nick Name");
+}
+
+void Addressee::setBirthday(const QDateTime &birthday)
+{
+    if (birthday == d->mBirthday) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mBirthday = birthday;
+}
+
+QDateTime Addressee::birthday() const
+{
+    return d->mBirthday;
+}
+
+QString Addressee::birthdayLabel()
+{
+    return i18n("Birthday");
+}
+
+QString Addressee::homeAddressStreetLabel()
+{
+    return i18n("Home Address Street");
+}
+
+QString Addressee::homeAddressPostOfficeBoxLabel()
+{
+    return i18n("Home Address Post Office Box");
+}
+
+QString Addressee::homeAddressLocalityLabel()
+{
+    return i18n("Home Address City");
+}
+
+QString Addressee::homeAddressRegionLabel()
+{
+    return i18n("Home Address State");
+}
+
+QString Addressee::homeAddressPostalCodeLabel()
+{
+    return i18n("Home Address Zip Code");
+}
+
+QString Addressee::homeAddressCountryLabel()
+{
+    return i18n("Home Address Country");
+}
+
+QString Addressee::homeAddressLabelLabel()
+{
+    return i18n("Home Address Label");
+}
+
+QString Addressee::businessAddressStreetLabel()
+{
+    return i18n("Business Address Street");
+}
+
+QString Addressee::businessAddressPostOfficeBoxLabel()
+{
+    return i18n("Business Address Post Office Box");
+}
+
+QString Addressee::businessAddressLocalityLabel()
+{
+    return i18n("Business Address City");
+}
+
+QString Addressee::businessAddressRegionLabel()
+{
+    return i18n("Business Address State");
+}
+
+QString Addressee::businessAddressPostalCodeLabel()
+{
+    return i18n("Business Address Zip Code");
+}
+
+QString Addressee::businessAddressCountryLabel()
+{
+    return i18n("Business Address Country");
+}
+
+QString Addressee::businessAddressLabelLabel()
+{
+    return i18n("Business Address Label");
+}
+
+QString Addressee::homePhoneLabel()
+{
+    return i18n("Home Phone");
+}
+
+QString Addressee::businessPhoneLabel()
+{
+    return i18n("Business Phone");
+}
+
+QString Addressee::mobilePhoneLabel()
+{
+    return i18n("Mobile Phone");
+}
+
+QString Addressee::homeFaxLabel()
+{
+    return i18n("Home Fax");
+}
+
+QString Addressee::businessFaxLabel()
+{
+    return i18n("Business Fax");
+}
+
+QString Addressee::carPhoneLabel()
+{
+    return i18n("Car Phone");
+}
+
+QString Addressee::isdnLabel()
+{
+    return i18n("ISDN");
+}
+
+QString Addressee::pagerLabel()
+{
+    return i18n("Pager");
+}
+
+QString Addressee::emailLabel()
+{
+    return i18n("Email Address");
+}
+
+void Addressee::setMailer(const QString &mailer)
+{
+    if (mailer == d->mMailer) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mMailer = mailer;
+}
+
+QString Addressee::mailer() const
+{
+    return d->mMailer;
+}
+
+QString Addressee::mailerLabel()
+{
+    return i18n("Mail Client");
+}
+
+void Addressee::setTimeZone(const TimeZone &timeZone)
+{
+    if (timeZone == d->mTimeZone) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mTimeZone = timeZone;
+}
+
+TimeZone Addressee::timeZone() const
+{
+    return d->mTimeZone;
+}
+
+QString Addressee::timeZoneLabel()
+{
+    return i18n("Time Zone");
+}
+
+void Addressee::setGeo(const Geo &geo)
+{
+    if (geo == d->mGeo) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mGeo = geo;
+}
+
+Geo Addressee::geo() const
+{
+    return d->mGeo;
+}
+
+QString Addressee::geoLabel()
+{
+    return i18n("Geographic Position");
+}
+
+void Addressee::setTitle(const QString &title)
+{
+    if (title == d->mTitle) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mTitle = title;
+}
+
+QString Addressee::title() const
+{
+    return d->mTitle;
+}
+
+QString Addressee::titleLabel()
+{
+    return i18nc("a person's title", "Title");
+}
+
+void Addressee::setRole(const QString &role)
+{
+    if (role == d->mRole) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mRole = role;
+}
+
+QString Addressee::role() const
+{
+    return d->mRole;
+}
+
+QString Addressee::roleLabel()
+{
+    return i18nc("of a person in an organization", "Role");
+}
+
+void Addressee::setOrganization(const QString &organization)
+{
+    if (organization == d->mOrganization) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mOrganization = organization;
+}
+
+QString Addressee::organization() const
+{
+    return d->mOrganization;
+}
+
+QString Addressee::organizationLabel()
+{
+    return i18n("Organization");
+}
+
+void Addressee::setDepartment(const QString &department)
+{
+    if (department == d->mDepartment) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mDepartment = department;
+}
+
+QString Addressee::department() const
+{
+    return d->mDepartment;
+}
+
+QString Addressee::departmentLabel()
+{
+    return i18n("Department");
+}
+
+void Addressee::setNote(const QString &note)
+{
+    if (note == d->mNote) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mNote = note;
+}
+
+QString Addressee::note() const
+{
+    return d->mNote;
+}
+
+QString Addressee::noteLabel()
+{
+    return i18n("Note");
+}
+
+void Addressee::setProductId(const QString &productId)
+{
+    if (productId == d->mProductId) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mProductId = productId;
+}
+
+QString Addressee::productId() const
+{
+    return d->mProductId;
+}
+
+QString Addressee::productIdLabel()
+{
+    return i18n("Product Identifier");
+}
+
+void Addressee::setRevision(const QDateTime &revision)
+{
+    if (revision == d->mRevision) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mRevision = revision;
+}
+
+QDateTime Addressee::revision() const
+{
+    return d->mRevision;
+}
+
+QString Addressee::revisionLabel()
+{
+    return i18n("Revision Date");
+}
+
+void Addressee::setSortString(const QString &sortString)
+{
+    if (sortString == d->mSortString) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mSortString = sortString;
+}
+
+QString Addressee::sortString() const
+{
+    return d->mSortString;
+}
+
+QString Addressee::sortStringLabel()
+{
+    return i18n("Sort String");
+}
+
+void Addressee::setUrl(const QUrl &url)
+{
+    KContacts::ResourceLocatorUrl resourceLocator;
+    resourceLocator.setUrl(url);
+    insertExtraUrl(resourceLocator);
+}
+
+void Addressee::setUrl(const ResourceLocatorUrl &url)
+{
+    insertExtraUrl(url);
+}
+
+ResourceLocatorUrl Addressee::url() const
+{
+    if (d->mUrlExtraList.isEmpty()) {
+        return ResourceLocatorUrl();
+    } else {
+        return d->mUrlExtraList.at(0);
+    }
+}
+
+QString Addressee::urlLabel()
+{
+    return i18n("Homepage");
+}
+
+void Addressee::setSecrecy(const Secrecy &secrecy)
+{
+    if (secrecy == d->mSecrecy) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mSecrecy = secrecy;
+}
+
+Secrecy Addressee::secrecy() const
+{
+    return d->mSecrecy;
+}
+
+QString Addressee::secrecyLabel()
+{
+    return i18n("Security Class");
+}
+
+void Addressee::setLogo(const Picture &logo)
+{
+    if (logo == d->mLogo) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mLogo = logo;
+}
+
+Picture Addressee::logo() const
+{
+    return d->mLogo;
+}
+
+QString Addressee::logoLabel()
+{
+    return i18n("Logo");
+}
+
+void Addressee::setPhoto(const Picture &photo)
+{
+    if (photo == d->mPhoto) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mPhoto = photo;
+}
+
+Picture Addressee::photo() const
+{
+    return d->mPhoto;
+}
+
+QString Addressee::photoLabel()
+{
+    return i18n("Photo");
+}
+
+void Addressee::setSound(const Sound &sound)
+{
+    if (sound == d->mSound) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mSound = sound;
+}
+
+Sound Addressee::sound() const
+{
+    return d->mSound;
+}
+
+QString Addressee::soundLabel()
+{
+    return i18n("Sound");
+}
+
+void Addressee::setNameFromString(const QString &s)
+{
+    QString str = s;
+    //remove enclosing quotes from string
+    if (str.length() > 1  &&
+            s[0] == QLatin1Char('"') &&
+            s[s.length() - 1] == QLatin1Char('"')) {
+        str = s.mid(1, s.length() - 2);
+    }
+
+    setFormattedName(str);
+    setName(str);
+
+    // clear all name parts
+    setPrefix(QString());
+    setGivenName(QString());
+    setAdditionalName(QString());
+    setFamilyName(QString());
+    setSuffix(QString());
+
+    if (str.isEmpty()) {
+        return;
+    }
+
+    static QString spaceStr = QStringLiteral(" ");
+    static QString emptyStr = QStringLiteral("");
+    AddresseeHelper *helper = AddresseeHelper::self();
+
+    int i = str.indexOf(QLatin1Char(','));
+    if (i < 0) {
+        QStringList parts = str.split(spaceStr);
+        int leftOffset = 0;
+        int rightOffset = parts.count() - 1;
+
+        QString suffix;
+        while (rightOffset >= 0) {
+            if (helper->containsSuffix(parts[rightOffset])) {
+                suffix.prepend(parts[rightOffset] + (suffix.isEmpty() ? emptyStr : spaceStr));
+                rightOffset--;
+            } else {
+                break;
+            }
+        }
+        setSuffix(suffix);
+
+        if (rightOffset < 0) {
+            return;
+        }
+
+        if (rightOffset - 1 >= 0 && helper->containsPrefix(parts[rightOffset - 1].toLower())) {
+            setFamilyName(parts[rightOffset - 1] + spaceStr + parts[rightOffset]);
+            rightOffset--;
+        } else {
+            if (helper->tradeAsFamilyName()) {
+                setFamilyName(parts[rightOffset]);
+            } else {
+                setGivenName(parts[rightOffset]);
+            }
+        }
+
+        QString prefix;
+        while (leftOffset < rightOffset) {
+            if (helper->containsTitle(parts[leftOffset])) {
+                prefix.append((prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
+                leftOffset++;
+            } else {
+                break;
+            }
+        }
+        setPrefix(prefix);
+
+        if (leftOffset < rightOffset) {
+            setGivenName(parts[leftOffset]);
+            leftOffset++;
+        }
+
+        QString additionalName;
+        while (leftOffset < rightOffset) {
+            additionalName.append((additionalName.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
+            leftOffset++;
+        }
+        setAdditionalName(additionalName);
+    } else {
+        QString part1 = str.left(i);
+        QString part2 = str.mid(i + 1);
+
+        QStringList parts = part1.split(spaceStr);
+        int leftOffset = 0;
+        int rightOffset = parts.count() - 1;
+
+        if (parts.count() > 0) {
+
+            QString suffix;
+            while (rightOffset >= 0) {
+                if (helper->containsSuffix(parts[rightOffset])) {
+                    suffix.prepend(parts[rightOffset] + (suffix.isEmpty() ? emptyStr : spaceStr));
+                    rightOffset--;
+                } else {
+                    break;
+                }
+            }
+            setSuffix(suffix);
+
+            if (rightOffset - 1 >= 0 && helper->containsPrefix(parts[rightOffset - 1].toLower())) {
+                setFamilyName(parts[rightOffset - 1] + spaceStr + parts[rightOffset]);
+                rightOffset--;
+            } else {
+                setFamilyName(parts[rightOffset]);
+            }
+
+            QString prefix;
+            while (leftOffset < rightOffset) {
+                if (helper->containsTitle(parts[leftOffset])) {
+                    prefix.append((prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
+                    leftOffset++;
+                } else {
+                    break;
+                }
+            }
+        } else {
+            setPrefix(QString());
+            setFamilyName(QString());
+            setSuffix(QString());
+        }
+
+        parts = part2.split(spaceStr);
+
+        leftOffset = 0;
+        rightOffset = parts.count();
+
+        if (parts.count() > 0) {
+
+            QString prefix;
+            while (leftOffset < rightOffset) {
+                if (helper->containsTitle(parts[leftOffset])) {
+                    prefix.append((prefix.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
+                    leftOffset++;
+                } else {
+                    break;
+                }
+            }
+            setPrefix(prefix);
+
+            if (leftOffset < rightOffset) {
+                setGivenName(parts[leftOffset]);
+                leftOffset++;
+            }
+
+            QString additionalName;
+            while (leftOffset < rightOffset) {
+                additionalName.append((additionalName.isEmpty() ? emptyStr : spaceStr) + parts[leftOffset]);
+                leftOffset++;
+            }
+            setAdditionalName(additionalName);
+        } else {
+            setGivenName(QString());
+            setAdditionalName(QString());
+        }
+    }
+}
+
+QString Addressee::realName() const
+{
+    QString n(formattedName());
+    if (!n.isEmpty()) {
+        return n;
+    }
+
+    n = assembledName();
+    if (!n.isEmpty()) {
+        return n;
+    }
+
+    n = name();
+    if (!n.isEmpty()) {
+        return n;
+    }
+
+    return organization();
+}
+
+QString Addressee::assembledName() const
+{
+    const QString name = prefix() + QLatin1Char(' ') +
+                         givenName() + QLatin1Char(' ') +
+                         additionalName() + QLatin1Char(' ') +
+                         familyName() + QLatin1Char(' ') +
+                         suffix();
+
+    return name.simplified();
+}
+
+QString Addressee::fullEmail(const QString &email) const
+{
+    QString e;
+    if (email.isNull()) {
+        e = preferredEmail();
+    } else {
+        e = email;
+    }
+    if (e.isEmpty()) {
+        return QString();
+    }
+
+    QString text;
+    if (realName().isEmpty()) {
+        text = e;
+    } else {
+        QRegExp needQuotes(QStringLiteral("[^ 0-9A-Za-z\\x0080-\\xFFFF]"));
+        if (realName().indexOf(needQuotes) != -1) {
+            QString name = realName();
+            name.replace(QLatin1String("\""), QLatin1String("\\\""));
+            text = QLatin1String("\"") + name + QLatin1String("\" <") + e + QLatin1Char('>');
+        } else {
+            text = realName() + QLatin1String(" <") + e + QLatin1Char('>');
+        }
+    }
+
+    return text;
+}
+void Addressee::insertEmail(const QString &email, bool preferred, const QMap<QString, QStringList> &param)
+{
+    if (email.simplified().isEmpty()) {
+        return;
+    }
+
+    for (int i = 0; i < d->mEmails.size(); ++i) {
+        if (d->mEmails.at(i).mail() == email)  {
+            if (!preferred || i == 0) {
+                return;
+            }
+            Email tempMail = d->mEmails.takeAt(i);
+            d->mEmails.prepend(tempMail);
+            return;
+        }
+    }
+
+    Email mail(email);
+    mail.setParameters(param);
+    d->mEmpty = false;
+    if (preferred) {
+        d->mEmails.prepend(mail);
+    } else {
+        d->mEmails.append(mail);
+    }
+}
+
+void Addressee::removeEmail(const QString &email)
+{
+    for (int i = 0; i < d->mEmails.size(); ++i) {
+        if (d->mEmails.at(i).mail() == email)  {
+            d->mEmails.removeAt(i);
+        }
+    }
+}
+
+QString Addressee::preferredEmail() const
+{
+    if (d->mEmails.count() == 0) {
+        return QString();
+    } else {
+        return d->mEmails.first().mail();
+    }
+}
+
+QStringList Addressee::emails() const
+{
+    QStringList list;
+    const int numberOfEmail = d->mEmails.size();
+    list.reserve(numberOfEmail);
+    for (int i = 0; i < numberOfEmail; ++i) {
+        list << d->mEmails.at(i).mail();
+    }
+
+    return list;
+}
+
+Email::List Addressee::emailList() const
+{
+    return d->mEmails;
+}
+
+void Addressee::setEmails(const QStringList &emails)
+{
+    d->mEmails.clear();
+    const int numEmails = emails.size();
+    d->mEmails.reserve(numEmails);
+    for (int i = 0; i < numEmails; ++i) {
+        d->mEmails.append(Email(emails.at(i)));
+    }
+    d->mEmpty = false;
+}
+
+void Addressee::setEmailList(const Email::List &list)
+{
+    d->mEmails = list;
+    d->mEmpty = false;
+}
+
+void Addressee::removeLang(const QString &language)
+{
+    for (int i = 0; i < d->mLangs.size(); ++i) {
+        if (d->mLangs.at(i).language() == language)  {
+            d->mLangs.removeAt(i);
+        }
+    }
+}
+
+void Addressee::setLangs(const Lang::List &langs)
+{
+    d->mLangs = langs;
+    d->mEmpty = false;
+}
+
+void Addressee::insertLang(const Lang &language)
+{
+    const QString languageStr = language.language();
+    if (languageStr.simplified().isEmpty()) {
+        return;
+    }
+    d->mEmpty = false;
+
+    Lang::List::Iterator it;
+    Lang::List::Iterator end(d->mLangs.end());
+    for (it = d->mLangs.begin(); it != end; ++it) {
+        if ((*it).language() == languageStr) {
+            (*it).setParameters(language.parameters());
+            return;
+        }
+    }
+    d->mLangs.append(language);
+}
+
+Lang::List Addressee::langs() const
+{
+    return d->mLangs;
+}
+
+void Addressee::setGender(const Gender &gender)
+{
+    if (gender == d->mGender) {
+        return;
+    }
+
+    d->mEmpty = false;
+    d->mGender = gender;
+}
+
+Gender Addressee::gender() const
+{
+    return d->mGender;
+}
+
+void Addressee::insertPhoneNumber(const PhoneNumber &phoneNumber)
+{
+    d->mEmpty = false;
+
+    PhoneNumber::List::Iterator it;
+    for (it = d->mPhoneNumbers.begin(); it != d->mPhoneNumbers.end(); ++it) {
+        if ((*it).id() == phoneNumber.id()) {
+            *it = phoneNumber;
+            return;
+        }
+    }
+    if (!phoneNumber.number().simplified().isEmpty()) {
+        d->mPhoneNumbers.append(phoneNumber);
+    }
+}
+
+void Addressee::removePhoneNumber(const PhoneNumber &phoneNumber)
+{
+    PhoneNumber::List::Iterator it;
+    for (it = d->mPhoneNumbers.begin(); it != d->mPhoneNumbers.end(); ++it) {
+        if ((*it).id() == phoneNumber.id()) {
+            d->mPhoneNumbers.erase(it);
+            return;
+        }
+    }
+}
+
+PhoneNumber Addressee::phoneNumber(PhoneNumber::Type type) const
+{
+    PhoneNumber phoneNumber(QString(), type);
+    PhoneNumber::List::ConstIterator it;
+    for (it = d->mPhoneNumbers.constBegin(); it != d->mPhoneNumbers.constEnd(); ++it) {
+        if (matchBinaryPattern((*it).type(), type)) {
+            if ((*it).type() & PhoneNumber::Pref) {
+                return (*it);
+            } else if (phoneNumber.number().isEmpty()) {
+                phoneNumber = (*it);
+            }
+        }
+    }
+
+    return phoneNumber;
+}
+
+PhoneNumber::List Addressee::phoneNumbers() const
+{
+    return d->mPhoneNumbers;
+}
+
+void Addressee::setPhoneNumbers(const PhoneNumber::List &phoneNumbers)
+{
+    d->mEmpty = false;
+    d->mPhoneNumbers.clear();
+    d->mPhoneNumbers = phoneNumbers;
+}
+
+PhoneNumber::List Addressee::phoneNumbers(PhoneNumber::Type type) const
+{
+    PhoneNumber::List list;
+
+    PhoneNumber::List::ConstIterator it;
+    PhoneNumber::List::ConstIterator end(d->mPhoneNumbers.constEnd());
+    for (it = d->mPhoneNumbers.constBegin(); it != end; ++it) {
+        if (matchBinaryPattern((*it).type(), type)) {
+            list.append(*it);
+        }
+    }
+    return list;
+}
+
+PhoneNumber Addressee::findPhoneNumber(const QString &id) const
+{
+    PhoneNumber::List::ConstIterator it;
+    PhoneNumber::List::ConstIterator end(d->mPhoneNumbers.constEnd());
+    for (it = d->mPhoneNumbers.constBegin(); it != end; ++it) {
+        if ((*it).id() == id) {
+            return *it;
+        }
+    }
+    return PhoneNumber();
+}
+
+void Addressee::insertKey(const Key &key)
+{
+    d->mEmpty = false;
+
+    Key::List::Iterator it;
+    for (it = d->mKeys.begin(); it != d->mKeys.end(); ++it) {
+        if ((*it).id() == key.id()) {
+            *it = key;
+            return;
+        }
+    }
+    d->mKeys.append(key);
+}
+
+void vectorRemoveAll(Key::List &t, const Key &key)
+{
+    t.removeAll(key);
+}
+
+void Addressee::removeKey(const Key &key)
+{
+    Key::List::Iterator it;
+    for (it = d->mKeys.begin(); it != d->mKeys.end(); ++it) {
+        if ((*it).id() == key.id()) {
+            //Just use d->mKeys.removeAll(key) when depending on Qt 5.4
+            vectorRemoveAll(d->mKeys, key);
+            return;
+        }
+    }
+}
+
+Key Addressee::key(Key::Type type, const QString &customTypeString) const
+{
+    Key::List::ConstIterator it;
+    Key::List::ConstIterator end(d->mKeys.constEnd());
+    for (it = d->mKeys.constBegin(); it != end; ++it) {
+        if ((*it).type() == type) {
+            if (type == Key::Custom) {
+                if (customTypeString.isEmpty()) {
+                    return *it;
+                } else {
+                    if ((*it).customTypeString() == customTypeString) {
+                        return (*it);
+                    }
+                }
+            } else {
+                return *it;
+            }
+        }
+    }
+    return Key(QString(), type);
+}
+
+void Addressee::setKeys(const Key::List &list)
+{
+    d->mKeys = list;
+    d->mEmpty = false;
+}
+
+Key::List Addressee::keys() const
+{
+    return d->mKeys;
+}
+
+Key::List Addressee::keys(Key::Type type, const QString &customTypeString) const
+{
+    Key::List list;
+
+    Key::List::ConstIterator it;
+    Key::List::ConstIterator end(d->mKeys.constEnd());
+    for (it = d->mKeys.constBegin(); it != end; ++it) {
+        if ((*it).type() == type) {
+            if (type == Key::Custom) {
+                if (customTypeString.isEmpty()) {
+                    list.append(*it);
+                } else {
+                    if ((*it).customTypeString() == customTypeString) {
+                        list.append(*it);
+                    }
+                }
+            } else {
+                list.append(*it);
+            }
+        }
+    }
+    return list;
+}
+
+Key Addressee::findKey(const QString &id) const
+{
+    Key::List::ConstIterator it;
+    Key::List::ConstIterator end(d->mKeys.constEnd());
+    for (it = d->mKeys.constBegin(); it != end; ++it) {
+        if ((*it).id() == id) {
+            return *it;
+        }
+    }
+    return Key();
+}
+
+QString Addressee::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Addressee {\n");
+    str += QStringLiteral("  Uid: %1\n").arg(uid());
+
+    str += QStringLiteral("  Name: %1\n").arg(name());
+    str += QStringLiteral("  FormattedName: %1\n").arg(formattedName());
+    str += QStringLiteral("  FamilyName: %1\n").arg(familyName());
+    str += QStringLiteral("  GivenName: %1\n").arg(givenName());
+    str += QStringLiteral("  AdditionalName: %1\n").arg(additionalName());
+    str += QStringLiteral("  Prefix: %1\n").arg(prefix());
+    str += QStringLiteral("  Suffix: %1\n").arg(suffix());
+    str += QStringLiteral("  NickName: %1\n").arg(nickName());
+    str += QStringLiteral("  Birthday: %1\n").arg(birthday().toString());
+    str += QStringLiteral("  Mailer: %1\n").arg(mailer());
+    str += QStringLiteral("  TimeZone: %1\n").arg(timeZone().toString());
+    str += QStringLiteral("  Geo: %1\n").arg(geo().toString());
+    str += QStringLiteral("  Title: %1\n").arg(title());
+    str += QStringLiteral("  Role: %1\n").arg(role());
+    str += QStringLiteral("  Organization: %1\n").arg(organization());
+    str += QStringLiteral("  Department: %1\n").arg(department());
+    str += QStringLiteral("  Note: %1\n").arg(note());
+    str += QStringLiteral("  ProductId: %1\n").arg(productId());
+    str += QStringLiteral("  Revision: %1\n").arg(revision().toString());
+    str += QStringLiteral("  SortString: %1\n").arg(sortString());
+    str += QStringLiteral("  Url: %1\n").arg(url().url().url());
+    str += QStringLiteral("  Secrecy: %1\n").arg(secrecy().toString());
+    str += QStringLiteral("  Logo: %1\n").arg(logo().toString());
+    str += QStringLiteral("  Photo: %1\n").arg(photo().toString());
+    str += QStringLiteral("  Sound: %1\n").arg(sound().toString());
+    str += QStringLiteral("  Gender: %1\n").arg(gender().toString());
+    str += QStringLiteral("  Kind: %1\n").arg(kind());
+
+    str += QLatin1String("  Emails {\n");
+    const Email::List listEmail = d->mEmails;
+    Email::List::ConstIterator it5;
+    for (it5 = listEmail.begin(); it5 != listEmail.end(); ++it5) {
+        str += (*it5).toString();
+    }
+    str += QLatin1String("  }\n");
+
+    str += QLatin1String("  Langs {\n");
+    const Lang::List listLang = d->mLangs;
+    Lang::List::ConstIterator it6;
+    for (it6 = listLang.begin(); it6 != listLang.end(); ++it6) {
+        str += (*it6).toString();
+    }
+    str += QLatin1String("  }\n");
+
+    str += QLatin1String("  PhoneNumbers {\n");
+    const PhoneNumber::List p = phoneNumbers();
+    PhoneNumber::List::ConstIterator it2;
+    for (it2 = p.begin(); it2 != p.end(); ++it2) {
+        str += (*it2).toString();
+    }
+    str += QLatin1String("  }\n");
+
+    str += QLatin1String("  Addresses {\n");
+    const Address::List a = addresses();
+    Address::List::ConstIterator it3;
+    for (it3 = a.begin(); it3 != a.end(); ++it3) {
+        str += (*it3).toString();
+    }
+    str += QLatin1String("  }\n");
+
+    str += QLatin1String("  Keys {\n");
+    const Key::List k = keys();
+    Key::List::ConstIterator it4;
+    for (it4 = k.begin(); it4 != k.end(); ++it4) {
+        str += (*it4).toString();
+    }
+    str += QLatin1String("  }\n");
+
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+void Addressee::insertAddress(const Address &address)
+{
+    if (address.isEmpty()) {
+        return;
+    }
+
+    d->mEmpty = false;
+
+    Address::List::Iterator it;
+    for (it = d->mAddresses.begin(); it != d->mAddresses.end(); ++it) {
+        if ((*it).id() == address.id()) {
+            *it = address;
+            return;
+        }
+    }
+
+    d->mAddresses.append(address);
+}
+
+void Addressee::removeAddress(const Address &address)
+{
+    Address::List::Iterator it;
+    for (it = d->mAddresses.begin(); it != d->mAddresses.end(); ++it) {
+        if ((*it).id() == address.id()) {
+            d->mAddresses.erase(it);
+            return;
+        }
+    }
+}
+
+Address Addressee::address(Address::Type type) const
+{
+    Address address(type);
+    Address::List::ConstIterator it;
+    Address::List::ConstIterator end(d->mAddresses.constEnd());
+    for (it = d->mAddresses.constBegin(); it != end; ++it) {
+        if (matchBinaryPattern((*it).type(), type)) {
+            if ((*it).type() & Address::Pref) {
+                return (*it);
+            } else if (address.isEmpty()) {
+                address = (*it);
+            }
+        }
+    }
+
+    return address;
+}
+
+Address::List Addressee::addresses() const
+{
+    return d->mAddresses;
+}
+
+Address::List Addressee::addresses(Address::Type type) const
+{
+    Address::List list;
+
+    Address::List::ConstIterator it;
+    Address::List::ConstIterator end(d->mAddresses.constEnd());
+    for (it = d->mAddresses.constBegin(); it != end; ++it) {
+        if (matchBinaryPattern((*it).type(), type)) {
+            list.append(*it);
+        }
+    }
+
+    return list;
+}
+
+Address Addressee::findAddress(const QString &id) const
+{
+    Address::List::ConstIterator it;
+    Address::List::ConstIterator end(d->mAddresses.constEnd());
+    for (it = d->mAddresses.constBegin(); it != end; ++it) {
+        if ((*it).id() == id) {
+            return *it;
+        }
+    }
+    return Address();
+}
+
+void Addressee::insertCategory(const QString &c)
+{
+    d->mEmpty = false;
+
+    if (d->mCategories.contains(c)) {
+        return;
+    }
+
+    d->mCategories.append(c);
+}
+
+void Addressee::removeCategory(const QString &category)
+{
+    if (d->mCategories.contains(category)) {
+        d->mCategories.removeAll(category);
+    }
+}
+
+bool Addressee::hasCategory(const QString &category) const
+{
+    return d->mCategories.contains(category);
+}
+
+void Addressee::setCategories(const QStringList &c)
+{
+    d->mEmpty = false;
+
+    d->mCategories = c;
+}
+
+QStringList Addressee::categories() const
+{
+    return d->mCategories;
+}
+
+void Addressee::insertMember(const QString &member)
+{
+    d->mEmpty = false;
+
+    if (d->mMembers.contains(member)) {
+        return;
+    }
+
+    d->mMembers.append(member);
+}
+
+void Addressee::setMembers(const QStringList &m)
+{
+    d->mEmpty = false;
+    d->mMembers = m;
+}
+
+QStringList Addressee::members() const
+{
+    return d->mMembers;
+}
+
+void Addressee::insertRelationShip(const Related &relation)
+{
+    d->mEmpty = false;
+
+    if (d->mRelationShips.contains(relation)) {
+        return;
+    }
+
+    d->mRelationShips.append(relation);
+}
+
+void Addressee::setRelationShips(const Related::List &c)
+{
+    d->mEmpty = false;
+    d->mRelationShips = c;
+}
+
+Related::List Addressee::relationShips() const
+{
+    return d->mRelationShips;
+}
+
+void Addressee::insertCustom(const QString &app, const QString &name,
+                             const QString &value)
+{
+    if (value.isEmpty() || name.isEmpty() || app.isEmpty()) {
+        return;
+    }
+
+    d->mEmpty = false;
+
+    const QString qualifiedName = app + QLatin1Char('-') + name;
+
+    d->mCustomFields.insert(qualifiedName, value);
+}
+
+void Addressee::removeCustom(const QString &app, const QString &name)
+{
+    const QString qualifiedName = app + QLatin1Char('-') + name;
+
+    d->mCustomFields.remove(qualifiedName);
+}
+
+QString Addressee::custom(const QString &app, const QString &name) const
+{
+    const QString qualifiedName = app + QLatin1Char('-') + name;
+
+    return d->mCustomFields.value(qualifiedName);
+}
+
+void Addressee::setCustoms(const QStringList &customs)
+{
+    d->mEmpty = false;
+
+    d->mCustomFields.clear();
+
+    foreach (const QString &custom, customs) {
+        const int index = custom.indexOf(QLatin1Char(':'));
+        if (index == -1) {
+            continue;
+        }
+
+        const QString qualifiedName = custom.left(index);
+        const QString value = custom.mid(index + 1);
+
+        d->mCustomFields.insert(qualifiedName, value);
+    }
+}
+
+QStringList Addressee::customs() const
+{
+    QStringList result;
+
+    QHashIterator<QString, QString> it(d->mCustomFields);
+    result.reserve(d->mCustomFields.count());
+    while (it.hasNext()) {
+        it.next();
+        result << it.key() + QLatin1Char(':') + it.value();
+    }
+
+    return result;
+}
+
+void Addressee::parseEmailAddress(const QString &rawEmail, QString &fullName,
+                                  QString &email)
+{
+    // This is a simplified version of KPIM::splitAddress().
+
+    fullName.clear();
+    email.clear();
+    if (rawEmail.isEmpty()) {
+        return;    // KPIM::AddressEmpty;
+    }
+
+    // The code works on 8-bit strings, so convert the input to UTF-8.
+    QByteArray address = rawEmail.toUtf8();
+
+    QByteArray displayName;
+    QByteArray addrSpec;
+    QByteArray comment;
+
+    // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
+    // The purpose is to extract a displayable string from the mailboxes.
+    // Comments in the addr-spec are not handled. No error checking is done.
+
+    enum sourceLevel {
+        TopLevel,
+        InComment,
+        InAngleAddress
+    };
+    sourceLevel context = TopLevel;
+    bool inQuotedString = false;
+    int commentLevel = 0;
+    bool stop = false;
+
+    for (char *p = address.data(); *p && !stop; ++p) {
+        switch (context) {
+        case TopLevel : {
+            switch (*p) {
+            case '"':
+                inQuotedString = !inQuotedString;
+                displayName += *p;
+                break;
+            case '(':
+                if (!inQuotedString) {
+                    context = InComment;
+                    commentLevel = 1;
+                } else {
+                    displayName += *p;
+                }
+                break;
+            case '<':
+                if (!inQuotedString) {
+                    context = InAngleAddress;
+                } else {
+                    displayName += *p;
+                }
+                break;
+            case '\\': // quoted character
+                displayName += *p;
+                ++p; // skip the '\'
+                if (*p) {
+                    displayName += *p;
+                } else {
+                    //return KPIM::UnexpectedEnd;
+                    goto ABORT_PARSING;
+                }
+                break;
+            case ',':
+                if (!inQuotedString) {
+                    //if ( allowMultipleAddresses )
+                    //  stop = true;
+                    //else
+                    //  return KPIM::UnexpectedComma;
+                    goto ABORT_PARSING;
+                } else {
+                    displayName += *p;
+                }
+                break;
+            default:
+                displayName += *p;
+            }
+            break;
+        }
+        case InComment: {
+            switch (*p) {
+            case '(':
+                ++commentLevel;
+                comment += *p;
+                break;
+            case ')':
+                --commentLevel;
+                if (commentLevel == 0) {
+                    context = TopLevel;
+                    comment += ' '; // separate the text of several comments
+                } else {
+                    comment += *p;
+                }
+                break;
+            case '\\': // quoted character
+                comment += *p;
+                ++p; // skip the '\'
+                if (*p) {
+                    comment += *p;
+                } else {
+                    //return KPIM::UnexpectedEnd;
+                    goto ABORT_PARSING;
+                }
+                break;
+            default:
+                comment += *p;
+            }
+            break;
+        }
+        case InAngleAddress: {
+            switch (*p) {
+            case '"':
+                inQuotedString = !inQuotedString;
+                addrSpec += *p;
+                break;
+            case '>':
+                if (!inQuotedString) {
+                    context = TopLevel;
+                } else {
+                    addrSpec += *p;
+                }
+                break;
+            case '\\': // quoted character
+                addrSpec += *p;
+                ++p; // skip the '\'
+                if (*p) {
+                    addrSpec += *p;
+                } else {
+                    //return KPIM::UnexpectedEnd;
+                    goto ABORT_PARSING;
+                }
+                break;
+            default:
+                addrSpec += *p;
+            }
+            break;
+        }
+        } // switch ( context )
+    }
+
+ABORT_PARSING:
+    displayName = displayName.trimmed();
+    comment = comment.trimmed();
+    addrSpec = addrSpec.trimmed();
+    fullName = QString::fromUtf8(displayName);
+    email = QString::fromUtf8(addrSpec);  // check for errors
+    if (inQuotedString) {
+        return;    // KPIM::UnbalancedQuote;
+    }
+    if (context == InComment) {
+        return;    // KPIM::UnbalancedParens;
+    }
+    if (context == InAngleAddress) {
+        return;    // KPIM::UnclosedAngleAddr;
+    }
+
+    if (addrSpec.isEmpty()) {
+        if (displayName.isEmpty()) {
+            return;    // KPIM::NoAddressSpec;
+        } else {
+            //addrSpec = displayName;
+            //displayName.truncate( 0 );
+            // Address of the form "foo@bar" or "foo@bar (Name)".
+            email = fullName;
+            fullName = QString::fromUtf8(comment);
+        }
+    }
+
+    email = email.toLower();
+    // Check that we do not have any extra characters on the end of the
+    // strings
+    unsigned int len = fullName.length();
+    if (fullName[0] == QLatin1Char('"') && fullName[len - 1] == QLatin1Char('"')) {
+        fullName = fullName.mid(1, len - 2);
+    }
+}
+
+void Addressee::setChanged(bool value)
+{
+    d->mChanged = value;
+}
+
+bool Addressee::changed() const
+{
+    return d->mChanged;
+}
+
+void Addressee::setSortMode(KContacts::SortMode *mode)
+{
+    Private::mSortMode = mode;
+}
+
+bool Addressee::operator<(const Addressee &addr) const
+{
+    if (!Private::mSortMode) {
+        return false;
+    } else {
+        return Private::mSortMode->lesser(*this, addr);
+    }
+}
+
+QString Addressee::mimeType()
+{
+    return QStringLiteral("text/directory");
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Addressee &a)
+{
+    s << a.d->mUid;
+
+    s << a.d->mName;
+    s << a.d->mFormattedName;
+    s << a.d->mFamilyName;
+    s << a.d->mGivenName;
+    s << a.d->mAdditionalName;
+    s << a.d->mPrefix;
+    s << a.d->mSuffix;
+    s << a.d->mNickName;
+    s << a.d->mBirthday;
+    s << a.d->mMailer;
+    s << a.d->mTimeZone;
+    s << a.d->mGeo;
+    s << a.d->mTitle;
+    s << a.d->mRole;
+    s << a.d->mOrganization;
+    s << a.d->mDepartment;
+    s << a.d->mNote;
+    s << a.d->mProductId;
+    s << a.d->mRevision;
+    s << a.d->mSortString;
+    s << a.d->mSecrecy;
+    s << a.d->mLogo;
+    s << a.d->mPhoto;
+    s << a.d->mSound;
+    s << a.d->mPhoneNumbers;
+    s << a.d->mAddresses;
+    s << a.d->mEmails;
+    s << a.d->mCategories;
+    s << a.customs();
+    s << a.d->mKeys;
+    s << a.d->mLangs;
+    s << a.d->mGender;
+    s << a.d->mKind;
+    s << a.d->mCalendarUrl;
+    s << a.d->mSoundListExtra;
+    s << a.d->mPhotoExtraList;
+    s << a.d->mLogoExtraList;
+    s << a.d->mUrlExtraList;
+    s << a.d->mMembers;
+    s << a.d->mRelationShips;
+    s << a.d->mSources;
+    s << a.d->mImpps;
+
+    return s;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Addressee &a)
+{
+    s >> a.d->mUid;
+
+    s >> a.d->mName;
+    s >> a.d->mFormattedName;
+    s >> a.d->mFamilyName;
+    s >> a.d->mGivenName;
+    s >> a.d->mAdditionalName;
+    s >> a.d->mPrefix;
+    s >> a.d->mSuffix;
+    s >> a.d->mNickName;
+    s >> a.d->mBirthday;
+    s >> a.d->mMailer;
+    s >> a.d->mTimeZone;
+    s >> a.d->mGeo;
+    s >> a.d->mTitle;
+    s >> a.d->mRole;
+    s >> a.d->mOrganization;
+    s >> a.d->mDepartment;
+    s >> a.d->mNote;
+    s >> a.d->mProductId;
+    s >> a.d->mRevision;
+    s >> a.d->mSortString;
+    s >> a.d->mSecrecy;
+    s >> a.d->mLogo;
+    s >> a.d->mPhoto;
+    s >> a.d->mSound;
+    s >> a.d->mPhoneNumbers;
+    s >> a.d->mAddresses;
+    s >> a.d->mEmails;
+    s >> a.d->mCategories;
+    QStringList customFields;
+    s >> customFields;
+    a.setCustoms(customFields);
+    s >> a.d->mKeys;
+    s >> a.d->mLangs;
+    s >> a.d->mGender;
+    s >> a.d->mKind;
+    s >> a.d->mCalendarUrl;
+    s >> a.d->mSoundListExtra;
+    s >> a.d->mPhotoExtraList;
+    s >> a.d->mLogoExtraList;
+    s >> a.d->mUrlExtraList;
+    s >> a.d->mMembers;
+    s >> a.d->mRelationShips;
+    s >> a.d->mSources;
+    s >> a.d->mImpps;
+    a.d->mEmpty = false;
+
+    return s;
+}
+
+bool matchBinaryPattern(int value, int pattern)
+{
+    /**
+      We want to match all telephonnumbers/addresses which have the bits in the
+      pattern set. More are allowed.
+      if pattern == 0 we have a special handling, then we want only those with
+      exactly no bit set.
+     */
+    if (pattern == 0) {
+        return (value == 0);
+    } else {
+        return (pattern == (pattern & value));
+    }
+}
+
+template <class L>
+bool listEquals(const QVector<L> &list, const QVector<L> &pattern)
+{
+    if (list.count() != pattern.count()) {
+        return false;
+    }
+    const int numberOfElement(list.count());
+    for (int i = 0; i < numberOfElement; ++i) {
+        if (!pattern.contains(list[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool listEquals(const QStringList &list, const QStringList &pattern)
+{
+    if (list.count() != pattern.count()) {
+        return false;
+    }
+
+    const int numberOfElement(list.count());
+    for (int i = 0; i < numberOfElement; ++i) {
+        if (!pattern.contains(list[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}
diff --git a/src/addressee.h b/src/addressee.h
new file mode 100644 (file)
index 0000000..6de9ed3
--- /dev/null
@@ -0,0 +1,1053 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+                  2013 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_ADDRESSEE_H
+#define KCONTACTS_ADDRESSEE_H
+
+#include <QtCore/QDateTime>
+#include <QtCore/QStringList>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QUrl>
+#include <QtCore/QMap>
+
+#include "address.h"
+#include "geo.h"
+#include "key.h"
+#include "email.h"
+#include "lang.h"
+#include "gender.h"
+#include "phonenumber.h"
+#include "picture.h"
+#include "secrecy.h"
+#include "sound.h"
+#include "impp.h"
+#include "timezone.h"
+#include "calendarurl.h"
+#include "related.h"
+#include "addresseelist.h"
+#include "resourcelocatorurl.h"
+
+namespace KContacts
+{
+
+class SortMode;
+
+/**
+  @short address book entry
+
+  This class represents an entry in the address book.
+
+  The data of this class is implicitly shared. You can pass this class by value.
+
+  If you need the name of a field for presenting it to the user you should use
+  the functions ending in Label(). They return a translated string which can be
+  used as label for the corresponding field.
+
+  About the name fields:
+
+  givenName() is the first name and familyName() the last name. In some
+  countries the family name comes first, that's the reason for the
+  naming. formattedName() is the full name with the correct formatting.
+  It is used as an override, when the correct formatting can't be generated
+  from the other name fields automatically.
+
+  realName() returns a fully formatted name(). It uses formattedName, if set,
+  otherwise it constucts the name from the name fields. As fallback, if
+  nothing else is set it uses name().
+
+  name() is the NAME type of RFC2426. It can be used as internal name for the
+  data enty, but shouldn't be used for displaying the data to the user.
+ */
+class KCONTACTS_EXPORT Addressee
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Addressee &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Addressee &);
+
+public:
+    /**
+      A list of addressee objects
+     */
+    typedef AddresseeList List;
+    /**
+      A map from unique identifier to addressee.
+
+      @see uid()
+     */
+    typedef QHash<QString, Addressee> Map;
+
+    /**
+      Construct an empty address book entry.
+     */
+    Addressee();
+
+    /**
+      Destroys the address book entry.
+     */
+    ~Addressee();
+
+    /**
+      Copy constructor.
+     */
+    Addressee(const Addressee &other);
+
+    /**
+      Assignment operator.
+
+      @return a reference to @c this
+    */
+    Addressee &operator=(const Addressee &other);
+
+    /**
+      Equality operator.
+
+      @return @c true if @c this and the given addressee are equal,
+              otherwise @c false
+    */
+    bool operator==(const Addressee &other) const;
+
+    /**
+      Not-equal operator.
+
+      @return @c true if @c this and the given addressee are not equal,
+              otherwise @c false
+    */
+    bool operator!=(const Addressee &other) const;
+
+    /**
+      Return if the address book entry is empty.
+     */
+    bool isEmpty() const;
+
+    /**
+      Set unique identifier.
+
+      @param uid the KABC unique identifier
+     */
+    void setUid(const QString &uid);
+
+    /**
+      Return unique identifier.
+     */
+    QString uid() const;
+
+    /**
+      Return translated label for uid field.
+     */
+    static QString uidLabel();
+
+    /**
+      Set name.
+     */
+    void setName(const QString &name);
+
+    /**
+      Return name.
+     */
+    QString name() const;
+
+    /**
+      Return translated label for name field.
+     */
+    static QString nameLabel();
+
+    /**
+      Set formatted name.
+     */
+    void setFormattedName(const QString &formattedName);
+
+    /**
+      Return formatted name.
+     */
+    QString formattedName() const;
+
+    /**
+      Return translated label for formattedName field.
+     */
+    static QString formattedNameLabel();
+
+    /**
+      Set family name.
+     */
+    void setFamilyName(const QString &familyName);
+
+    /**
+      Return family name.
+     */
+    QString familyName() const;
+
+    /**
+      Return translated label for familyName field.
+     */
+    static QString familyNameLabel();
+
+    /**
+      Set given name.
+     */
+    void setGivenName(const QString &givenName);
+
+    /**
+      Return given name.
+     */
+    QString givenName() const;
+
+    /**
+      Return translated label for givenName field.
+     */
+    static QString givenNameLabel();
+
+    /**
+      Set additional names.
+     */
+    void setAdditionalName(const QString &additionalName);
+
+    /**
+      Return additional names.
+     */
+    QString additionalName() const;
+
+    /**
+      Return translated label for additionalName field.
+     */
+    static QString additionalNameLabel();
+
+    /**
+      Set honorific prefixes.
+     */
+    void setPrefix(const QString &prefix);
+
+    /**
+      Return honorific prefixes.
+     */
+    QString prefix() const;
+
+    /**
+      Return translated label for prefix field.
+     */
+    static QString prefixLabel();
+
+    /**
+      Set honorific suffixes.
+     */
+    void setSuffix(const QString &suffix);
+
+    /**
+      Return honorific suffixes.
+     */
+    QString suffix() const;
+
+    /**
+      Return translated label for suffix field.
+     */
+    static QString suffixLabel();
+
+    /**
+      Set nick name.
+     */
+    void setNickName(const QString &nickName);
+
+    /**
+      Return nick name.
+     */
+    QString nickName() const;
+
+    /**
+      Return translated label for nickName field.
+     */
+    static QString nickNameLabel();
+
+    /**
+      Set birthday.
+     */
+    void setBirthday(const QDateTime &birthday);
+
+    /**
+      Return birthday.
+     */
+    QDateTime birthday() const;
+
+    /**
+      Return translated label for birthday field.
+     */
+    static QString birthdayLabel();
+
+    /**
+      Return translated label for homeAddressStreet field.
+     */
+    static QString homeAddressStreetLabel();
+
+    /**
+      Return translated label for homeAddressPostOfficeBox field.
+     */
+    static QString homeAddressPostOfficeBoxLabel();
+
+    /**
+      Return translated label for homeAddressLocality field.
+     */
+    static QString homeAddressLocalityLabel();
+
+    /**
+      Return translated label for homeAddressRegion field.
+     */
+    static QString homeAddressRegionLabel();
+
+    /**
+      Return translated label for homeAddressPostalCode field.
+     */
+    static QString homeAddressPostalCodeLabel();
+
+    /**
+      Return translated label for homeAddressCountry field.
+     */
+    static QString homeAddressCountryLabel();
+
+    /**
+      Return translated label for homeAddressLabel field.
+     */
+    static QString homeAddressLabelLabel();
+
+    /**
+      Return translated label for businessAddressStreet field.
+     */
+    static QString businessAddressStreetLabel();
+
+    /**
+      Return translated label for businessAddressPostOfficeBox field.
+     */
+    static QString businessAddressPostOfficeBoxLabel();
+
+    /**
+      Return translated label for businessAddressLocality field.
+     */
+    static QString businessAddressLocalityLabel();
+
+    /**
+      Return translated label for businessAddressRegion field.
+     */
+    static QString businessAddressRegionLabel();
+
+    /**
+      Return translated label for businessAddressPostalCode field.
+     */
+    static QString businessAddressPostalCodeLabel();
+
+    /**
+      Return translated label for businessAddressCountry field.
+     */
+    static QString businessAddressCountryLabel();
+
+    /**
+      Return translated label for businessAddressLabel field.
+     */
+    static QString businessAddressLabelLabel();
+
+    /**
+      Return translated label for homePhone field.
+     */
+    static QString homePhoneLabel();
+
+    /**
+      Return translated label for businessPhone field.
+     */
+    static QString businessPhoneLabel();
+
+    /**
+      Return translated label for mobilePhone field.
+     */
+    static QString mobilePhoneLabel();
+
+    /**
+      Return translated label for homeFax field.
+     */
+    static QString homeFaxLabel();
+
+    /**
+      Return translated label for businessFax field.
+     */
+    static QString businessFaxLabel();
+
+    /**
+      Return translated label for carPhone field.
+     */
+    static QString carPhoneLabel();
+
+    /**
+      Return translated label for isdn field.
+     */
+    static QString isdnLabel();
+
+    /**
+      Return translated label for pager field.
+     */
+    static QString pagerLabel();
+
+    /**
+      Return translated label for email field.
+     */
+    static QString emailLabel();
+
+    /**
+      Set mail client.
+     */
+    void setMailer(const QString &mailer);
+
+    /**
+      Return mail client.
+     */
+    QString mailer() const;
+
+    /**
+      Return translated label for mailer field.
+     */
+    static QString mailerLabel();
+
+    /**
+      Set time zone.
+     */
+    void setTimeZone(const TimeZone &timeZone);
+
+    /**
+      Return time zone.
+     */
+    TimeZone timeZone() const;
+
+    /**
+      Return translated label for timeZone field.
+     */
+    static QString timeZoneLabel();
+
+    /**
+      Set geographic position.
+     */
+    void setGeo(const Geo &geo);
+
+    /**
+      Return geographic position.
+     */
+    Geo geo() const;
+
+    /**
+      Return translated label for geo field.
+     */
+    static QString geoLabel();
+
+    /**
+      Set title.
+     */
+    void setTitle(const QString &title);
+
+    /**
+      Return title.
+     */
+    QString title() const;
+
+    /**
+      Return translated label for title field.
+     */
+    static QString titleLabel();
+
+    /**
+      Set role.
+     */
+    void setRole(const QString &role);
+
+    /**
+      Return role.
+     */
+    QString role() const;
+
+    /**
+      Return translated label for role field.
+     */
+    static QString roleLabel();
+
+    /**
+      Set organization.
+     */
+    void setOrganization(const QString &organization);
+
+    /**
+      Return organization.
+     */
+    QString organization() const;
+
+    /**
+      Return translated label for organization field.
+     */
+    static QString organizationLabel();
+
+    /**
+      Set department.
+     */
+    void setDepartment(const QString &department);
+
+    /**
+      Return department.
+     */
+    QString department() const;
+
+    /**
+      Return translated label for department field.
+     */
+    static QString departmentLabel();
+
+    /**
+      Set note.
+     */
+    void setNote(const QString &note);
+
+    /**
+      Return note.
+     */
+    QString note() const;
+
+    /**
+      Return translated label for note field.
+     */
+    static QString noteLabel();
+
+    /**
+      Set product identifier.
+     */
+    void setProductId(const QString &productId);
+
+    /**
+      Return product identifier.
+     */
+    QString productId() const;
+
+    /**
+      Return translated label for productId field.
+     */
+    static QString productIdLabel();
+
+    /**
+      Set revision date.
+     */
+    void setRevision(const QDateTime &revision);
+
+    /**
+      Return revision date.
+     */
+    QDateTime revision() const;
+
+    /**
+      Return translated label for revision field.
+     */
+    static QString revisionLabel();
+
+    /**
+      Set sort string.
+     */
+    void setSortString(const QString &sortString);
+
+    /**
+      Return sort string.
+     */
+    QString sortString() const;
+
+    /**
+      Return translated label for sortString field.
+     */
+    static QString sortStringLabel();
+
+    /**
+      Set homepage.
+     */
+    void setUrl(const ResourceLocatorUrl &url);
+    void setUrl(const QUrl &url);
+
+    /**
+      Return homepage.
+     */
+    ResourceLocatorUrl url() const;
+
+    /**
+      Return translated label for url field.
+     */
+    static QString urlLabel();
+
+    /**
+      Set security class.
+     */
+    void setSecrecy(const Secrecy &secrecy);
+
+    /**
+      Return security class.
+     */
+    Secrecy secrecy() const;
+
+    /**
+      Return translated label for secrecy field.
+     */
+    static QString secrecyLabel();
+
+    /**
+      Set logo.
+     */
+    void setLogo(const Picture &logo);
+
+    /**
+      Return logo.
+     */
+    Picture logo() const;
+
+    /**
+      Return translated label for logo field.
+     */
+    static QString logoLabel();
+
+    /**
+      Set photo.
+     */
+    void setPhoto(const Picture &photo);
+
+    /**
+      Return photo.
+     */
+    Picture photo() const;
+
+    /**
+      Return translated label for photo field.
+     */
+    static QString photoLabel();
+
+    /**
+      Set sound.
+     */
+    void setSound(const Sound &sound);
+
+    /**
+      Return sound.
+     */
+    Sound sound() const;
+
+    /**
+      Return translated label for sound field.
+     */
+    static QString soundLabel();
+
+    /**
+      Set name fields by parsing the given string and trying to associate the
+      parts of the string with according fields. This function should probably
+      be a bit more clever.
+     */
+    void setNameFromString(const QString &s);
+
+    /**
+      Return the name of the addressee. This is calculated from all the name
+      fields.
+     */
+    QString realName() const;
+
+    /**
+      Return the name that consists of all name parts.
+     */
+    QString assembledName() const;
+
+    /**
+      Return email address including real name.
+
+      @param email Email address to be used to construct the full email string.
+                   If this is QString() the preferred email address is used.
+     */
+    QString fullEmail(const QString &email = QString()) const;
+
+    /**
+      Insert an email address. If the email address already exists in this
+      addressee it is not duplicated.
+
+      @param email Email address
+      @param preferred Set to true, if this is the preferred email address of
+                       the addressee.
+     */
+    void insertEmail(const QString &email, bool preferred = false, const QMap<QString, QStringList> &param = QMap<QString, QStringList>());
+
+    /**
+      Remove email address. If the email address doesn't exist, nothing happens.
+
+      @param email Email address to remove
+     */
+    void removeEmail(const QString &email);
+
+    /**
+      Return preferred email address. This is the first email address or the
+      last one added with insertEmail() with a set preferred parameter.
+     */
+    QString preferredEmail() const;
+
+    /**
+      Return list of all email addresses.
+     */
+    QStringList emails() const;
+
+    /**
+       Set the emails to @p list.
+       The first email address gets the preferred one!
+       @param list The list of email addresses.
+     */
+    void setEmails(const QStringList &list);
+
+    /**
+      Insert a phone number. If a phone number with the same id already exists
+      in this addressee it is not duplicated.
+
+      @param phoneNumber The telephone number to insert to the addressee
+     */
+    void insertPhoneNumber(const PhoneNumber &phoneNumber);
+
+    /**
+      Remove phone number. If no phone number with the given id exists for this
+      addresse nothing happens.
+
+      @param phoneNumber The telephone number to remove from the addressee
+     */
+    void removePhoneNumber(const PhoneNumber &phoneNumber);
+
+    /**
+      Return phone number, which matches the given type.
+
+      @param type The type of phone number to get
+     */
+    PhoneNumber phoneNumber(PhoneNumber::Type type) const;
+
+    /**
+      Return list of all phone numbers.
+     */
+    PhoneNumber::List phoneNumbers() const;
+
+    void setPhoneNumbers(const PhoneNumber::List &phoneNumbers);
+
+    /**
+      Return list of phone numbers with a special type.
+
+      @param type The type of phone number to get
+     */
+    PhoneNumber::List phoneNumbers(PhoneNumber::Type type) const;
+
+    /**
+      Return phone number with the given id.
+
+      @param id The identifier of the phone number to look for.
+                See PhoneNumber::id()
+     */
+    PhoneNumber findPhoneNumber(const QString &id) const;
+
+    /**
+      Insert a key. If a key with the same id already exists
+      in this addressee it is not duplicated.
+
+      @param key The key to insert
+     */
+    void insertKey(const Key &key);
+
+    /**
+      Remove a key. If no key with the given id exists for this
+      addresse nothing happens.
+
+      @param key The key to remove
+     */
+    void removeKey(const Key &key);
+
+    /**
+      Return key, which matches the given type.
+      If @p type == Key::Custom you can specify a string
+      that should match. If you leave the string empty, the first
+      key with a custom value is returned.
+
+      @param type The type of key to look for
+      @param customTypeString A string to match custom keys against when
+             @p type is @c Key::Custom
+     */
+    Key key(Key::Type type, const QString &customTypeString = QString()) const;
+
+    /**
+      Return list of all keys.
+     */
+    Key::List keys() const;
+
+    /**
+       Set the list of keys
+       @param keys The keys to be set.
+     */
+    void setKeys(const Key::List &keys);
+
+    /**
+      Return list of keys with a special type.
+      If @p type == Key::Custom you can specify a string
+      that should match. If you leave the string empty, all custom
+      keys will be returned.
+
+      @param type The type of key to look for
+      @param customTypeString A string to match custom keys against when
+             @p type is @c Key::Custom
+     */
+    Key::List keys(Key::Type type, const QString &customTypeString = QString()) const;
+
+    /**
+      Return key with the given id.
+
+      @param id The identifier of the key to look for. See Key::id()
+     */
+    Key findKey(const QString &id) const;
+
+    /**
+      Insert an address. If an address with the same id already exists
+      in this addressee it is not duplicated.
+
+      @param address The address to insert
+     */
+    void insertAddress(const Address &address);
+
+    /**
+      Remove address. If no address with the given id exists for this
+      addresse nothing happens.
+
+      @param address The address to remove
+     */
+    void removeAddress(const Address &address);
+
+    /**
+      Return address, which matches the given type.
+
+      @param type The type of address to look for
+     */
+    Address address(Address::Type type) const;
+
+    /**
+      Return list of all addresses.
+     */
+    Address::List addresses() const;
+
+    /**
+      Return list of addresses with a special type.
+
+      @param type The type of addresses to look for
+     */
+    Address::List addresses(Address::Type type) const;
+
+    /**
+      Return address with the given id.
+
+      @param id The identifier of the address to look for. See Address::id()
+     */
+    Address findAddress(const QString &id) const;
+
+    /**
+      Insert category. If the category already exists it is not duplicated.
+     */
+    void insertCategory(const QString &category);
+
+    /**
+      Remove category.
+     */
+    void removeCategory(const QString &category);
+
+    /**
+      Return, if addressee has the given category.
+     */
+    bool hasCategory(const QString &category) const;
+
+    /**
+      Set categories to given value.
+     */
+    void setCategories(const QStringList &category);
+
+    /**
+      Return list of all set categories.
+     */
+    QStringList categories() const;
+
+    /**
+      Insert custom entry. The entry is identified by the name of the inserting
+      application and a unique name. If an entry with the given app and name
+      already exists its value is replaced with the new given value.
+
+      An empty value isn't allowed (nothing happens if this is called with
+      any of the three arguments being empty)
+
+      @param app Name of the application inserting this custom entry
+      @param name Name of this application specific custom entry
+      @param value Value of this application specific custom entry
+     */
+    void insertCustom(const QString &app, const QString &name,
+                      const QString &value);
+
+    /**
+      Remove custom entry.
+
+      @param app Name of the application which has inserted this custom entry
+      @param name Name of this application specific custom entry
+     */
+    void removeCustom(const QString &app, const QString &name);
+
+    /**
+      Return value of custom entry, identified by app and entry name.
+
+      @param app Name of the application which has inserted this custom entry
+      @param name Name of this application specific custom entry
+     */
+    QString custom(const QString &app, const QString &name) const;
+
+    /**
+      Set all custom entries.
+     */
+    void setCustoms(const QStringList &customs);
+
+    /**
+      Return list of all custom entries.
+
+      The format of the custom entries is 'app-key:value' and the list is sorted
+      alphabetically by 'app-key'.
+     */
+    QStringList customs() const;
+
+    /**
+      Parse full email address. The result is given back in fullName and email.
+
+      @param rawEmail The input string to parse for name and email
+      @param fullName The name part of the @p rawEmail input, if it contained one
+      @param email The email part of the @p rawEmail input, if it contained one
+     */
+    static void parseEmailAddress(const QString &rawEmail, QString &fullName,
+                                  QString &email);
+
+    /**
+      Returns string representation of the addressee.
+     */
+    QString toString() const;
+
+    /**
+      Mark addressee as changed.
+
+      @param value Sets the status indicating changed data
+     */
+    void setChanged(bool value);
+
+    /**
+      Return whether the addressee is changed.
+     */
+    bool changed() const;
+
+    /**
+      Sets the sort mode implementation.
+
+      Comparing to addressee objects is delegated to an instance
+      of a subclass of KContacts::SortMode.
+
+      @param mode The sort mode implementator to use
+     */
+    static void setSortMode(KContacts::SortMode *mode);
+
+    /**
+      Return whether this addressee is "less-than" a given one
+
+      @param addr The addressee object to compare with
+
+      @see setSortMode()
+     */
+    bool operator<(const Addressee &addr) const;
+
+    /**
+      Returns the MIME type used for Addressees
+     */
+    static QString mimeType();
+
+    KContacts::Email::List emailList() const;
+    void setEmailList(const Email::List &list);
+
+    /**
+     * Remove Language
+     * @brief removeLang
+     * @param language
+     */
+    void removeLang(const QString &language);
+    /**
+     * Insert Language
+     * @brief insertLang
+     * @param language
+     */
+    void insertLang(const Lang &language);
+    /**
+     * @brief langs
+     * @return List of lang
+     */
+    Lang::List langs() const;
+    void setLangs(const Lang::List &langs);
+
+    void setGender(const Gender &gender);
+    Gender gender() const;
+
+    QString kind() const;
+    void setKind(const QString &kind);
+
+    void insertCalendarUrl(const CalendarUrl &calendarUrl);
+    CalendarUrl::List calendarUrlList() const;
+
+    void insertExtraSound(const Sound &sound);
+    void setExtraSoundList(const Sound::List &soundList);
+    Sound::List extraSoundList() const;
+
+    void insertExtraPhoto(const Picture &picture);
+    void setExtraPhotoList(const Picture::List &pictureList);
+    Picture::List extraPhotoList() const;
+
+    void insertExtraLogo(const Picture &logo);
+    void setExtraLogoList(const Picture::List &logoList);
+    Picture::List extraLogoList() const;
+
+    ResourceLocatorUrl::List extraUrlList() const;
+    void setExtraUrlList(const ResourceLocatorUrl::List &urlList);
+    void insertExtraUrl(const ResourceLocatorUrl &url);
+
+    //Member
+    void insertMember(const QString &member);
+    void setMembers(const QStringList &c);
+    QStringList members() const;
+
+    //Relation
+    void insertRelationShip(const Related &related);
+    void setRelationShips(const Related::List &c);
+    Related::List relationShips() const;
+
+    //Source
+    void insertSourceUrl(const QUrl &url);
+    void setSourcesUrlList(const QVector<QUrl> &urlList);
+    QVector<QUrl> sourcesUrlList() const;
+
+    //Impp
+    Impp::List imppList() const;
+    void setImppList(const Impp::List &imppList);
+    void insertImpp(const Impp &impp);
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Addressee &);
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Addressee &);
+
+}
+
+#define KCONTACTS_ADDRESSEE_METATYPE_DEFINED 1
+Q_DECLARE_METATYPE(KContacts::Addressee)
+
+#endif
diff --git a/src/addresseehelper.cpp b/src/addresseehelper.cpp
new file mode 100644 (file)
index 0000000..d695d08
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+    This file is part of the KDE libraries
+    Copyright (C) 2003 Carsten Pfeiffer <pfeiffer@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation, version 2.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "addresseehelper.h"
+
+#include <kconfig.h>
+#include <klocalizedstring.h>
+#include <kconfiggroup.h>
+
+#include <QCoreApplication>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN KContacts::AddresseeHelperPrivate
+{
+public:
+    QSet<QString> mTitles;
+    QSet<QString> mPrefixes;
+    QSet<QString> mSuffixes;
+    bool mTradeAsFamilyName;
+};
+
+Q_GLOBAL_STATIC(AddresseeHelper, s_self)
+
+// static
+AddresseeHelper *AddresseeHelper::self()
+{
+    return s_self;
+}
+
+AddresseeHelper::AddresseeHelper()
+    : QObject()
+    , d(new AddresseeHelperPrivate)
+{
+    initSettings();
+}
+
+AddresseeHelper::~AddresseeHelper()
+{
+}
+
+static void addToSet(const QStringList &list, QSet<QString> &container)
+{
+    QStringList::ConstIterator it;
+    QStringList::ConstIterator end(list.end());
+    for (it = list.begin(); it != end; ++it) {
+        if (!(*it).isEmpty()) {
+            container.insert(*it);
+        }
+    }
+}
+
+void AddresseeHelper::initSettings()
+{
+    d->mTitles.clear();
+    d->mSuffixes.clear();
+    d->mPrefixes.clear();
+
+    d->mTitles.insert(i18n("Dr."));
+    d->mTitles.insert(i18n("Miss"));
+    d->mTitles.insert(i18n("Mr."));
+    d->mTitles.insert(i18n("Mrs."));
+    d->mTitles.insert(i18n("Ms."));
+    d->mTitles.insert(i18n("Prof."));
+
+    d->mSuffixes.insert(i18n("I"));
+    d->mSuffixes.insert(i18n("II"));
+    d->mSuffixes.insert(i18n("III"));
+    d->mSuffixes.insert(i18n("Jr."));
+    d->mSuffixes.insert(i18n("Sr."));
+
+    d->mPrefixes.insert(QStringLiteral("van"));
+    d->mPrefixes.insert(QStringLiteral("von"));
+    d->mPrefixes.insert(QStringLiteral("de"));
+
+    KConfig _config(QStringLiteral("kabcrc"), KConfig::NoGlobals);
+    KConfigGroup config(&_config, "General");
+
+    addToSet(config.readEntry("Prefixes", QStringList()), d->mTitles);
+    addToSet(config.readEntry("Inclusions", QStringList()), d->mPrefixes);
+    addToSet(config.readEntry("Suffixes", QStringList()), d->mSuffixes);
+    d->mTradeAsFamilyName = config.readEntry("TradeAsFamilyName", true);
+}
+
+bool AddresseeHelper::containsTitle(const QString &title) const
+{
+    return d->mTitles.contains(title);
+}
+
+bool AddresseeHelper::containsPrefix(const QString &prefix) const
+{
+    return d->mPrefixes.contains(prefix);
+}
+
+bool AddresseeHelper::containsSuffix(const QString &suffix) const
+{
+    return d->mSuffixes.contains(suffix);
+}
+
+bool AddresseeHelper::tradeAsFamilyName() const
+{
+    return d->mTradeAsFamilyName;
+}
diff --git a/src/addresseehelper.h b/src/addresseehelper.h
new file mode 100644 (file)
index 0000000..3621045
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+    This file is part of the KDE libraries
+    Copyright (C) 2003 Carsten Pfeiffer <pfeiffer@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation, version 2.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_ADDRESSEEHELPER_H
+#define KCONTACTS_ADDRESSEEHELPER_H
+
+#include "kcontacts_export.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QSet>
+
+namespace KContacts
+{
+class AddresseeHelperPrivate;
+
+/**
+ * This singleton class stores static data, which is shared
+ * by all Addressee objects. It maintains three lists of
+ * strings, which can be queried using this class:
+ *
+ * - a list of honoric prefixes, like "Mrs.", "Prof." etc,
+ *   see containsTitle()
+ * - a list of inclusions, such as "van" or "de", see
+ *   containsPrefix()
+ * - a list of honoric suffixes, such as "I" or "Jr.", see
+ *   containsSuffix()
+ *
+ * All of these lists have a hardcoded and a configurable
+ * part. The configurable part is found in @c kabcrc, group
+ * @c General, fields @c Prefixes, @c Inclusions, and
+ * @c Suffixes.
+ *
+ * In addition to the above, this class stores one conveniece
+ * setting: it stores whether or not a single name component
+ * should be interpreted as a family name (see
+ * tradeAsFamilyName()). The corresponding configuration
+ * field is @c TradeAsFamilyName.
+ */
+class KCONTACTS_EXPORT AddresseeHelper : public QObject
+{
+    Q_OBJECT
+
+public:
+    /**
+     * Singleton interface to this class
+     *
+     * @return a pointer to the unique instance of this class.
+     */
+    static AddresseeHelper *self();
+
+    /**
+     * Queries the list of honoric prefixes.
+     *
+     * @param title the honoric prefix to search for
+     * @return @c true, if @p title was found in the list,
+     *         @c false otherwise
+     */
+    bool containsTitle(const QString &title) const;
+
+    /**
+     * Queries the list of inclusions.
+     *
+     * @param prefix the inclusion to search for
+     * @return @c true, if @p prefix was found in the list,
+     *         @c false otherwise
+     */
+    bool containsPrefix(const QString &prefix) const;
+
+    /**
+     * Queries the list of honoric suffixes.
+     *
+     * @param suffix the honoric suffix to search for
+     * @return @c true, if @p suffix was found in the list,
+     *         @c false otherwise
+     */
+    bool containsSuffix(const QString &suffix) const;
+
+    /**
+     * Returns whether or not a single name component should
+     * be interpreted as a family name.
+     *
+     * @return @c true if single name component is a family name,
+     *         @c false otherwise.
+     */
+    bool tradeAsFamilyName() const;
+
+    /** @internal */
+    AddresseeHelper();
+
+    ~AddresseeHelper();
+
+public Q_SLOTS:
+    /**
+     * Recreates the static data and reparses the configuration.
+     */
+    void initSettings();
+
+private:
+    QScopedPointer<AddresseeHelperPrivate> d;
+};
+
+}
+
+#endif
diff --git a/src/addresseelist.cpp b/src/addresseelist.cpp
new file mode 100644 (file)
index 0000000..b62bc02
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Jost Schenck <jost@schenck.de>
+                  2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "addresseelist.h"
+#include "field.h"
+#include "sortmode.h"
+#include "kcontacts_debug.h"
+
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+//
+//
+// Traits
+//
+//
+
+SortingTraits::Uid::Uid()
+    : d(Q_NULLPTR)
+{
+}
+
+SortingTraits::Uid::~Uid()
+{
+}
+
+bool SortingTraits::Uid::eq(const Addressee &a1, const Addressee &a2)
+{
+    // locale awareness doesn't make sense sorting ids
+    return QString::compare(a1.uid(), a2.uid()) == 0;
+}
+
+bool SortingTraits::Uid::lt(const Addressee &a1, const Addressee &a2)
+{
+    // locale awareness doesn't make sense sorting ids
+    return QString::compare(a1.uid(), a2.uid()) < 0;
+}
+
+SortingTraits::Name::Name()
+    : d(Q_NULLPTR)
+{
+}
+
+SortingTraits::Name::~Name()
+{
+}
+
+bool SortingTraits::Name::eq(const Addressee &a1, const Addressee &a2)
+{
+    return QString::localeAwareCompare(a1.name(), a2.name()) == 0;
+}
+
+bool SortingTraits::Name::lt(const Addressee &a1, const Addressee &a2)
+{
+    return QString::localeAwareCompare(a1.name(), a2.name()) < 0;
+}
+
+SortingTraits::FormattedName::FormattedName()
+    : d(Q_NULLPTR)
+{
+}
+
+SortingTraits::FormattedName::~FormattedName()
+{
+}
+
+bool SortingTraits::FormattedName::eq(const Addressee &a1, const Addressee &a2)
+{
+    return QString::localeAwareCompare(a1.formattedName(), a2.formattedName()) == 0;
+}
+
+bool SortingTraits::FormattedName::lt(const Addressee &a1, const Addressee &a2)
+{
+    return QString::localeAwareCompare(a1.formattedName(), a2.formattedName()) < 0;
+}
+
+SortingTraits::FamilyName::FamilyName()
+    : d(Q_NULLPTR)
+{
+}
+
+SortingTraits::FamilyName::~FamilyName()
+{
+}
+
+bool SortingTraits::FamilyName::eq(const Addressee &a1, const Addressee &a2)
+{
+    return
+        QString::localeAwareCompare(a1.familyName(), a2.familyName()) == 0 &&
+        QString::localeAwareCompare(a1.givenName(), a2.givenName()) == 0;
+}
+
+bool SortingTraits::FamilyName::lt(const Addressee &a1, const Addressee &a2)
+{
+    int family = QString::localeAwareCompare(a1.familyName(), a2.familyName());
+    if (0 == family) {
+        return QString::localeAwareCompare(a1.givenName(), a2.givenName()) < 0;
+    } else {
+        return family < 0;
+    }
+}
+
+SortingTraits::GivenName::GivenName()
+    : d(Q_NULLPTR)
+{
+}
+
+SortingTraits::GivenName::~GivenName()
+{
+}
+
+bool SortingTraits::GivenName::eq(const Addressee &a1, const Addressee &a2)
+{
+    return
+        QString::localeAwareCompare(a1.givenName(), a2.givenName()) == 0 &&
+        QString::localeAwareCompare(a1.familyName(), a2.familyName()) == 0;
+}
+
+bool SortingTraits::GivenName::lt(const Addressee &a1, const Addressee &a2)
+{
+    int given = QString::localeAwareCompare(a1.givenName(), a2.givenName());
+    if (0 == given) {
+        return QString::localeAwareCompare(a1.familyName(), a2.familyName()) < 0;
+    } else {
+        return given < 0;
+    }
+}
+
+//
+//
+// AddresseeList
+//
+//
+
+static Field *sActiveField = Q_NULLPTR;
+
+class Q_DECL_HIDDEN AddresseeList::Private : public QSharedData
+{
+public:
+    Private()
+        : mReverseSorting(false)
+        , mActiveSortingCriterion(FormattedName)
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mReverseSorting = other.mReverseSorting;
+        mActiveSortingCriterion = other.mActiveSortingCriterion;
+    }
+
+    bool mReverseSorting;
+    SortingCriterion mActiveSortingCriterion;
+};
+
+AddresseeList::AddresseeList()
+    : QVector<Addressee>()
+    , d(new Private)
+{
+}
+
+AddresseeList::~AddresseeList()
+{
+}
+
+AddresseeList::AddresseeList(const AddresseeList &other)
+    : QVector<Addressee>(other)
+    , d(other.d)
+{
+}
+
+AddresseeList::AddresseeList(const QVector<Addressee> &list)
+    : QVector<Addressee>(list)
+    , d(new Private)
+{
+}
+
+AddresseeList &AddresseeList::operator=(const AddresseeList &other)
+{
+    if (this != &other) {
+        QVector<Addressee>::operator=(other);
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString AddresseeList::toString() const
+{
+    QString str;
+
+    str += QLatin1String("AddresseeList {\n");
+    str += QStringLiteral("   Reverse Order: %1\n").arg(d->mReverseSorting ?
+            QStringLiteral("true") :
+            QStringLiteral("false"));
+
+    QString crit;
+    switch (d->mActiveSortingCriterion) {
+    case Uid:
+        crit = QStringLiteral("Uid");
+        break;
+    case Name:
+        crit = QStringLiteral("Name");
+        break;
+    case FormattedName:
+        crit = QStringLiteral("FormattedName");
+        break;
+    case FamilyName:
+        crit = QStringLiteral("FamilyName");
+        break;
+    case GivenName:
+        crit = QStringLiteral("GivenName");
+        break;
+    default:
+        crit = QStringLiteral("unknown -- update dump method");
+        break;
+    }
+
+    str += QStringLiteral("   Sorting criterion: %1\n").arg(crit);
+#if 0 //code commented in loop => comment it too
+    for (const_iterator it = begin(); it != end(); ++it) {
+//    str += (*it).toString();
+    }
+#endif
+
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+void AddresseeList::setReverseSorting(bool reverseSorting)
+{
+    d->mReverseSorting = reverseSorting;
+}
+
+bool AddresseeList::reverseSorting() const
+{
+    return d->mReverseSorting;
+}
+
+void AddresseeList::sortBy(SortingCriterion c)
+{
+    d->mActiveSortingCriterion = c;
+    if (Uid == c) {
+        sortByTrait<SortingTraits::Uid>();
+    } else if (Name == c) {
+        sortByTrait<SortingTraits::Name>();
+    } else if (FormattedName == c) {
+        sortByTrait<SortingTraits::FormattedName>();
+    } else if (FamilyName == c) {
+        sortByTrait<SortingTraits::FamilyName>();
+    } else if (GivenName == c) {
+        sortByTrait<SortingTraits::GivenName>();
+    } else {
+        qCCritical(KCONTACTS_LOG) << "AddresseeList sorting criterion passed for which a trait is not known."
+                                  << "No sorting done.";
+    }
+}
+
+void AddresseeList::sort()
+{
+    sortBy(d->mActiveSortingCriterion);
+}
+
+template<class Trait>
+void AddresseeList::sortByTrait()
+{
+    // FIXME: better sorting algorithm, bubblesort is not acceptable for larger lists.
+    //
+    // for i := 1 to n - 1
+    //   do for j := 1 to n - i
+    //     do if A[j] > A[j+1]
+    //       then temp :=  A[j]
+    //         A[j] := A[j + 1]
+    //         A[j + 1 ] := temp
+
+    iterator i1 = begin();
+    iterator endIt = end();
+    --endIt;
+    if (i1 == endIt) {   // don't need sorting
+        return;
+    }
+
+    iterator i2 = endIt;
+    while (i1 != endIt) {
+        iterator j1 = begin();
+        iterator j2 = j1;
+        ++j2;
+        while (j1 != i2) {
+            if ((!d->mReverseSorting && Trait::lt(*j2, *j1)) ||
+                    (d->mReverseSorting && Trait::lt(*j1, *j2))) {
+                qSwap(*j1, *j2);
+            }
+            ++j1;
+            ++j2;
+        }
+        ++i1;
+        --i2;
+    }
+}
+
+void AddresseeList::sortByField(Field *field)
+{
+    if (!field) {
+        qCWarning(KCONTACTS_LOG) << "sortByField called with no active sort field";
+        return;
+    }
+
+    sActiveField = field;
+
+    if (count() == 0) {
+        return;
+    }
+
+    KContacts::FieldSortMode *mode = new KContacts::FieldSortMode(sActiveField, !d->mReverseSorting);
+
+    KContacts::Addressee::setSortMode(mode);
+    qSort(*this);
+    KContacts::Addressee::setSortMode(Q_NULLPTR);
+
+    delete mode;
+}
+
+void AddresseeList::sortByMode(SortMode *mode)
+{
+    if (count() == 0) {
+        return;
+    }
+
+    KContacts::Addressee::setSortMode(mode);
+    qSort(*this);
+    KContacts::Addressee::setSortMode(Q_NULLPTR);
+}
+
+SortingCriterion AddresseeList::sortingCriterion() const
+{
+    return d->mActiveSortingCriterion;
+}
+
+Field *AddresseeList::sortingField() const
+{
+    return sActiveField;
+}
diff --git a/src/addresseelist.h b/src/addresseelist.h
new file mode 100644 (file)
index 0000000..acd7776
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Jost Schenck <jost@schenck.de>
+                  2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_ADDRESSEELIST_H
+#define KCONTACTS_ADDRESSEELIST_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QVector>
+
+namespace KContacts
+{
+
+class Field;
+class SortMode;
+class Addressee;
+
+/**
+ * Each trait must implement one static function for equality, one for "less
+ * than". Class name should be the field name. A trait does not necessarily
+ * have to stick to just one field: a trait sorting by family name can e.g.
+ * sort addressees with equal family name by given name.
+ *
+ * If you want to implement reverse sorting, you do not have to write another
+ * trait, as AddresseeList takes care of that.
+ */
+namespace SortingTraits
+{
+
+class KCONTACTS_EXPORT Uid
+{
+public:
+    /**
+     * Creates an instance.
+     */
+    Uid();
+
+    /**
+     * Destroys the instance.
+     */
+    ~Uid();
+
+    /**
+     * "Equal" compare method
+     *
+     * @return @c true if the first parameter is equal to the second
+     *         when comparing the uid attribute.
+     *
+     * @see Addressee::uid()
+     * @see QString::compare()
+     */
+    static bool eq(const Addressee &a1, const Addressee &a2);
+
+    /**
+     * "Less-Than" compare method
+     *
+     * @return @c true if the first parameter is "less-than" the second
+     *         when comparing the uid attribute.
+     *
+     * @see Addressee::uid()
+     * @see QString::compare()
+     */
+    static bool lt(const Addressee &a1, const Addressee &a2);
+
+private:
+    Q_DISABLE_COPY(Uid)
+    class Private;
+    Private *const d;
+};
+
+class KCONTACTS_EXPORT Name
+{
+public:
+    /**
+     * Creates an instance.
+     */
+    Name();
+
+    /**
+     * Destroys the instance.
+     */
+    ~Name();
+
+    /**
+     * "Equal" compare method
+     *
+     * @return @c true if the first parameter is equal to the second
+     *         when comparing the name attribute.
+     *
+     * @see Addressee::name()
+     * @see QString::localeAwareCompare()
+     */
+    static bool eq(const Addressee &a1, const Addressee &a2);
+
+    /**
+     * "Less-Than" compare method
+     *
+     * @return @c true if the first parameter is "less-than" the second
+     *         when comparing the name attribute.
+     *
+     * @see Addressee::name()
+     * @see QString::localeAwareCompare()
+     */
+    static bool lt(const Addressee &a1, const Addressee &a2);
+
+private:
+    Q_DISABLE_COPY(Name)
+    class Private;
+    Private *const d;
+};
+
+class KCONTACTS_EXPORT FormattedName
+{
+public:
+    /**
+     * Creates an instance.
+     */
+    FormattedName();
+
+    /**
+     * Destroys the instance.
+     */
+    ~FormattedName();
+
+    /**
+     * "Equal" compare method
+     *
+     * @return @c true if the first parameter is equal to the second
+     *         when comparing the formatted name attribute.
+     *
+     * @see Addressee::formattedName()
+     * @see QString::localeAwareCompare()
+     */
+    static bool eq(const Addressee &a1, const Addressee &a2);
+
+    /**
+     * "Less-Than" compare method
+     *
+     * @return @c true if the first parameter is "less-than" the second
+     *         when comparing the formatted name attribute.
+     *
+     * @see Addressee::formattedName()
+     * @see QString::localeAwareCompare()
+     */
+    static bool lt(const Addressee &a1, const Addressee &a2);
+
+private:
+    Q_DISABLE_COPY(FormattedName)
+    class Private;
+    Private *const d;
+};
+
+class KCONTACTS_EXPORT FamilyName // fallback to given name
+{
+public:
+    /**
+     * Creates an instance.
+     */
+    FamilyName();
+
+    /**
+     * Destroys the instance.
+     */
+    ~FamilyName();
+
+    /**
+     * "Equal" compare method
+     *
+     * @return @c true if the first parameter is equal to the second
+     *         when comparing the family name and given name attributes.
+     *
+     * @see Addressee::familyName()
+     * @see Addressee::givenName()
+     * @see QString::localeAwareCompare()
+     */
+    static bool eq(const Addressee &a1, const Addressee &a2);
+
+    /**
+     * "Less-Than" compare method
+     *
+     * Falls back to comparing given name if equal
+     *
+     * @return @c true if the first parameter is "less-than" the second
+     *         when comparing the family name attribute.
+     *
+     * @see Addressee::familyName()
+     * @see QString::localeAwareCompare()
+     */
+    static bool lt(const Addressee &a1, const Addressee &a2);
+
+private:
+    Q_DISABLE_COPY(FamilyName)
+    class Private;
+    Private *const d;
+};
+
+class KCONTACTS_EXPORT GivenName  // fallback to family name
+{
+public:
+    /**
+     * Creates an instance.
+     */
+    GivenName();
+
+    /**
+     * Destroys the instance.
+     */
+    ~GivenName();
+
+    /**
+     * "Equal" compare method
+     *
+     * @return @c true if the first parameter is equal to the second
+     *         when comparing the given name and family name attributes.
+     *
+     * @see Addressee::givenName()
+     * @see Addressee::familyName()
+     * @see QString::localeAwareCompare()
+     */
+    static bool eq(const Addressee &a1, const Addressee &a2);
+
+    /**
+     * "Less-Than" compare method
+     *
+     * Falls back to comparing family name if equal
+     *
+     * @return @c true if the first parameter is "less-than" the second
+     *         when comparing the given name attribute.
+     *
+     * @see Addressee::givenName()
+     * @see QString::localeAwareCompare()
+     */
+    static bool lt(const Addressee &a1, const Addressee &a2);
+
+private:
+    Q_DISABLE_COPY(GivenName)
+    class Private;
+    Private *const d;
+};
+
+}
+
+/**
+ * Addressee attribute used for sorting.
+ */
+typedef enum {
+    Uid,
+    Name,
+    FormattedName,
+    FamilyName,
+    GivenName
+} SortingCriterion;
+
+/**
+ * @short  a QValueList of Addressee, with sorting functionality
+ *
+ * This class extends the functionality of QValueList with
+ * sorting methods specific to the Addressee class. It can be used
+ * just like any other QValueList but is no template class.
+ *
+ * An AddresseeList does not automatically keep sorted when addressees
+ * are added or removed or the sorting order is changed, as this would
+ * slow down larger operations by sorting after every step. So after
+ * such operations you have to call {@link #sort} or {@link #sortBy} to
+ * create a defined order again.
+ *
+ * Iterator usage is inherited from QVector and extensively documented
+ * there. Please remember that the state of an iterator is undefined
+ * after any sorting operation.
+ *
+ * For the enumeration Type SortingCriterion, which specifies the
+ * field by the collection will be sorted, the following values exist:
+ * Uid, Name, FormattedName, FamilyName, GivenName.
+ *
+ * @author Jost Schenck jost@schenck.de
+ */
+class KCONTACTS_EXPORT AddresseeList : public QVector<Addressee>
+{
+public:
+    /**
+     * Creates a new addressee list.
+     */
+    AddresseeList();
+
+    /**
+     * Creates a new addressee list.
+     */
+    AddresseeList(const AddresseeList &other);
+
+    /**
+     * Creates a new addressee list.
+     */
+    AddresseeList(const QVector<Addressee> &list);
+
+    /**
+     * Destroys the addressee list.
+     */
+    ~AddresseeList();
+
+    /**
+     * Assignment operator.
+     *
+     * @param other the list to assign from
+     * @return a reference to @c this
+     */
+    AddresseeList &operator=(const AddresseeList &other);
+
+    /**
+     * Determines the direction of sorting. On change, the list
+     * will <em>not</em> automatically be resorted.
+     * @param reverseSorting <tt>true</tt> if sorting should be done reverse,
+     *                       <tt>false</tt> otherwise
+     */
+    void setReverseSorting(bool reverseSorting = true);
+
+    /**
+     * Returns the direction of sorting.
+     * @return    <tt>true</tt> if sorting is done reverse, <tt>false</tt> otherwise
+     */
+    bool reverseSorting() const;
+
+    /**
+     * Sorts this list by a specific criterion.
+     * @param c    the criterion by which should be sorted
+     */
+    void sortBy(SortingCriterion c);
+
+    /**
+     * Sorts this list by a specific field. If no parameter is given, the
+     * last used Field object will be used.
+     * @param field    pointer to the Field object to be sorted by
+     */
+    void sortByField(Field *field = Q_NULLPTR);
+
+    /**
+     * Sorts this list by a specific sorting mode.
+     * @param mode    pointer to the sorting mode object to be sorted by
+    */
+    void sortByMode(SortMode *mode = Q_NULLPTR);
+
+    /**
+     * Sorts this list by its active sorting criterion. This normally is the
+     * criterion of the last sortBy operation or <tt>FormattedName</tt> if up
+     * to now there has been no sortBy operation.
+     *
+     * Please note that the sorting trait of the last {@link #sortByTrait}
+     * method call is not remembered and thus the action can not be repeated
+     * by this method.
+     */
+    void sort();
+
+    /**
+     * Templated sort function. You normally will not want to use this but
+     * {@link #sortBy} and {@link #sort} instead as the existing sorting
+     * criteria completely suffice for most cases.
+     *
+     * However, if you do want to use some special sorting criterion, you can
+     * write a trait class that will be provided to this templated method.
+     * This trait class has to have a class declaration like the following:
+     * \code
+     * class MySortingTrait {
+     *   public:
+     *     // eq returns true if a1 and a2 are equal
+     *     static bool eq(KContacts::Addressee a1, KContacts::Addressee a2);
+     *     // lt returns true is a1 is "less than" a2
+     *     static bool lt(KContacts::Addressee a1, KContacts::Addressee a2);
+     * };
+     * \endcode
+     * You can then pass this class to the sortByTrait method like this:
+     * \code
+     * myAddresseelist.sortByTrait&lt;MySortingTrait&gt;();
+     * \endcode
+     * Please note that the {@link #sort} method can not be used to repeat the
+     * sorting of the last <tt>sortByTrait</tt> action.
+     *
+     * Right now this method uses the bubble sort algorithm. This should be
+     * replaced for a better one when I have time.
+     */
+    template<class Trait> void sortByTrait();
+
+    /**
+     * Returns the active sorting criterion, ie the sorting criterion that
+     * will be used by a {@link #sort} call.
+     */
+    SortingCriterion sortingCriterion() const;
+
+    /**
+     * Returns the active sorting field, ie a pointer to the Field object
+     * which was used for the last {@link #sortByField} operation.
+     * This function returns the last GLOBAL sorting field, not
+     * the class specific one.
+     * You're a lot better off by keeping track of this locally.
+     */
+    Field *sortingField() const;
+
+    /**
+     * Returns a string representation of the addressee list.
+     */
+    QString toString() const;
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+}
+
+#endif
diff --git a/src/calendarurl.cpp b/src/calendarurl.cpp
new file mode 100644 (file)
index 0000000..ba6938d
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "calendarurl.h"
+
+#include <QMap>
+#include <QUrl>
+#include <QDataStream>
+#include <qstringlist.h>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN CalendarUrl::Private : public QSharedData
+{
+public:
+    Private()
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        parameters = other.parameters;
+        type = other.type;
+        url = other.url;
+    }
+    static QString typeToString(CalendarUrl::CalendarType type);
+    QMap<QString, QStringList> parameters;
+    QUrl url;
+    CalendarUrl::CalendarType type;
+
+};
+
+QString CalendarUrl::Private::typeToString(CalendarUrl::CalendarType type)
+{
+    QString ret;
+    switch (type) {
+    case Unknown:
+    case EndCalendarType:
+        ret = QStringLiteral("Unknown");
+        break;
+    case FBUrl:
+        ret = QStringLiteral("FreeBusy");
+        break;
+    case CALUri:
+        ret = QStringLiteral("CalUri");
+        break;
+    case CALADRUri:
+        ret = QStringLiteral("Caladruri");
+        break;
+    }
+    return ret;
+}
+
+CalendarUrl::CalendarUrl()
+    : d(new Private)
+{
+    d->type = Unknown;
+}
+
+CalendarUrl::CalendarUrl(CalendarUrl::CalendarType type)
+    : d(new Private)
+{
+    d->type = type;
+}
+
+CalendarUrl::CalendarUrl(const CalendarUrl &other)
+    : d(other.d)
+{
+}
+
+CalendarUrl::~CalendarUrl()
+{
+
+}
+
+QMap<QString, QStringList> CalendarUrl::parameters() const
+{
+    return d->parameters;
+}
+
+bool CalendarUrl::operator==(const CalendarUrl &other) const
+{
+    return (d->parameters == other.parameters()) && (d->type == other.type()) && (d->url == other.url());
+}
+
+bool CalendarUrl::operator!=(const CalendarUrl &other) const
+{
+    return !(other == *this);
+}
+
+CalendarUrl &CalendarUrl::operator=(const CalendarUrl &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString CalendarUrl::toString() const
+{
+    QString str;
+    str += QLatin1String("CalendarUrl {\n");
+    str += QStringLiteral("    url: %1\n").arg(d->url.toString());
+    str += QStringLiteral("    type: %1\n").arg(CalendarUrl::Private::typeToString(d->type));
+    if (!d->parameters.isEmpty()) {
+        QMapIterator<QString, QStringList> i(d->parameters);
+        QString param;
+        while (i.hasNext()) {
+            i.next();
+            param += QStringLiteral("%1 %2").arg(i.key(), i.value().join(QStringLiteral(",")));
+        }
+        str += QStringLiteral("    parameters: %1\n").arg(param);
+    }
+    str += QLatin1String("}\n");
+    return str;
+}
+
+void CalendarUrl::setParameters(const QMap<QString, QStringList> &params)
+{
+    d->parameters = params;
+}
+
+bool CalendarUrl::isValid() const
+{
+    return (d->type != Unknown) && (d->url.isValid());
+}
+
+void CalendarUrl::setType(CalendarUrl::CalendarType type)
+{
+    d->type = type;
+}
+
+CalendarUrl::CalendarType CalendarUrl::type() const
+{
+    return d->type;
+}
+
+void CalendarUrl::setUrl(const QUrl &url)
+{
+    d->url = url;
+}
+
+QUrl CalendarUrl::url() const
+{
+    return d->url;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const CalendarUrl &calUrl)
+{
+    return s << calUrl.d->parameters << (uint)calUrl.d->type << calUrl.d->url;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, CalendarUrl &calUrl)
+{
+    uint type;
+    s >> calUrl.d->parameters >> type >> calUrl.d->url;
+    calUrl.d->type = static_cast<CalendarUrl::CalendarType>(type);
+    return s;
+}
+
diff --git a/src/calendarurl.h b/src/calendarurl.h
new file mode 100644 (file)
index 0000000..45e4698
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef CALENDARURL_H
+#define CALENDARURL_H
+#include "kcontacts_export.h"
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+class QUrl;
+/** @short Class that holds a Calendar Url (FBURL/CALADRURI/CALURI)
+ *  @since 4.14.6
+ */
+
+namespace KContacts
+{
+
+class KCONTACTS_EXPORT CalendarUrl
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const CalendarUrl &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, CalendarUrl &);
+public:
+    enum CalendarType {
+        Unknown = 0,
+        FBUrl,
+        CALUri,
+        CALADRUri,
+        EndCalendarType
+    };
+
+    CalendarUrl();
+    CalendarUrl(CalendarUrl::CalendarType type);
+    CalendarUrl(const CalendarUrl &other);
+
+    ~CalendarUrl();
+
+    typedef QVector<CalendarUrl> List;
+
+    bool isValid() const;
+
+    void setType(CalendarUrl::CalendarType type);
+    CalendarUrl::CalendarType type() const;
+
+    void setUrl(const QUrl &url);
+    QUrl url() const;
+
+    void setParameters(const QMap<QString, QStringList> &params);
+    QMap<QString, QStringList> parameters() const;
+
+    bool operator==(const CalendarUrl &other) const;
+    bool operator!=(const CalendarUrl &other) const;
+
+    CalendarUrl &operator=(const CalendarUrl &other);
+
+    QString toString() const;
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const CalendarUrl &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, CalendarUrl &object);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::CalendarUrl, Q_MOVABLE_TYPE);
+#endif // CALENDARURL_H
diff --git a/src/contactgroup.cpp b/src/contactgroup.cpp
new file mode 100644 (file)
index 0000000..eed4e97
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+  This file is part of the KContacts framework.
+  Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "contactgroup.h"
+
+#include <QtCore/QMap>
+#include <QtCore/QSharedData>
+#include <QtCore/QString>
+#include <QtCore/QUuid>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN ContactGroup::ContactReference::ContactReferencePrivate : public QSharedData
+{
+public:
+    ContactReferencePrivate()
+        : QSharedData()
+    {
+    }
+
+    ContactReferencePrivate(const ContactReferencePrivate &other)
+        : QSharedData(other)
+    {
+        mUid = other.mUid;
+        mPreferredEmail = other.mPreferredEmail;
+        mCustoms = other.mCustoms;
+    }
+
+    QString mUid;
+    QString mGid;
+    QString mPreferredEmail;
+    QMap<QString, QString> mCustoms;
+};
+
+ContactGroup::ContactReference::ContactReference()
+    : d(new ContactReferencePrivate)
+{
+}
+
+ContactGroup::ContactReference::ContactReference(const ContactReference &other)
+    : d(other.d)
+{
+}
+
+ContactGroup::ContactReference::ContactReference(const QString &uid)
+    : d(new ContactReferencePrivate)
+{
+    d->mUid = uid;
+}
+
+ContactGroup::ContactReference::~ContactReference()
+{
+}
+
+void ContactGroup::ContactReference::setUid(const QString &uid)
+{
+    d->mUid = uid;
+}
+
+QString ContactGroup::ContactReference::uid() const
+{
+    return d->mUid;
+}
+
+void ContactGroup::ContactReference::setGid(const QString &gid)
+{
+    d->mGid = gid;
+}
+
+QString ContactGroup::ContactReference::gid() const
+{
+    return d->mGid;
+}
+
+void ContactGroup::ContactReference::setPreferredEmail(const QString &email)
+{
+    d->mPreferredEmail = email;
+}
+
+QString ContactGroup::ContactReference::preferredEmail() const
+{
+    return d->mPreferredEmail;
+}
+
+void ContactGroup::ContactReference::insertCustom(const QString &key, const QString &value)
+{
+    d->mCustoms.insert(key, value);
+}
+
+void ContactGroup::ContactReference::removeCustom(const QString &key)
+{
+    d->mCustoms.remove(key);
+}
+
+QString ContactGroup::ContactReference::custom(const QString &key) const
+{
+    return d->mCustoms.value(key);
+}
+
+ContactGroup::ContactReference &ContactGroup::ContactReference::operator=(
+    const ContactGroup::ContactReference &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool ContactGroup::ContactReference::operator==(const ContactReference &other) const
+{
+    return d->mUid == other.d->mUid &&
+           d->mPreferredEmail == other.d->mPreferredEmail &&
+           d->mCustoms == other.d->mCustoms;
+}
+
+class Q_DECL_HIDDEN ContactGroup::ContactGroupReference::ContactGroupReferencePrivate : public QSharedData
+{
+public:
+    ContactGroupReferencePrivate()
+        : QSharedData()
+    {
+    }
+
+    ContactGroupReferencePrivate(const ContactGroupReferencePrivate &other)
+        : QSharedData(other)
+    {
+        mUid = other.mUid;
+        mCustoms = other.mCustoms;
+    }
+
+    QString mUid;
+    QMap<QString, QString> mCustoms;
+};
+
+ContactGroup::ContactGroupReference::ContactGroupReference()
+    : d(new ContactGroupReferencePrivate)
+{
+}
+
+ContactGroup::ContactGroupReference::ContactGroupReference(const ContactGroupReference &other)
+    : d(other.d)
+{
+}
+
+ContactGroup::ContactGroupReference::ContactGroupReference(const QString &uid)
+    : d(new ContactGroupReferencePrivate)
+{
+    d->mUid = uid;
+}
+
+ContactGroup::ContactGroupReference::~ContactGroupReference()
+{
+}
+
+void ContactGroup::ContactGroupReference::setUid(const QString &uid)
+{
+    d->mUid = uid;
+}
+
+QString ContactGroup::ContactGroupReference::uid() const
+{
+    return d->mUid;
+}
+
+void ContactGroup::ContactGroupReference::insertCustom(const QString &key, const QString &value)
+{
+    d->mCustoms.insert(key, value);
+}
+
+void ContactGroup::ContactGroupReference::removeCustom(const QString &key)
+{
+    d->mCustoms.remove(key);
+}
+
+QString ContactGroup::ContactGroupReference::custom(const QString &key) const
+{
+    return d->mCustoms.value(key);
+}
+
+ContactGroup::ContactGroupReference &ContactGroup::ContactGroupReference::operator=(
+    const ContactGroup::ContactGroupReference &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool ContactGroup::ContactGroupReference::operator==(const ContactGroupReference &other) const
+{
+    return d->mUid == other.d->mUid &&
+           d->mCustoms == other.d->mCustoms;
+}
+
+class Q_DECL_HIDDEN ContactGroup::Data::DataPrivate : public QSharedData
+{
+public:
+    DataPrivate()
+        : QSharedData()
+    {
+    }
+
+    DataPrivate(const DataPrivate &other)
+        : QSharedData(other)
+    {
+        mName = other.mName;
+        mEmail = other.mEmail;
+        mCustoms = other.mCustoms;
+    }
+
+    QString mName;
+    QString mEmail;
+    QMap<QString, QString> mCustoms;
+};
+
+ContactGroup::Data::Data()
+    : d(new DataPrivate)
+{
+}
+
+ContactGroup::Data::Data(const Data &other)
+    : d(other.d)
+{
+}
+
+ContactGroup::Data::Data(const QString &name, const QString &email)
+    : d(new DataPrivate)
+{
+    d->mName = name;
+    d->mEmail = email;
+}
+
+ContactGroup::Data::~Data()
+{
+}
+
+void ContactGroup::Data::setName(const QString &name)
+{
+    d->mName = name;
+}
+
+QString ContactGroup::Data::name() const
+{
+    return d->mName;
+}
+
+void ContactGroup::Data::setEmail(const QString &email)
+{
+    d->mEmail = email;
+}
+
+QString ContactGroup::Data::email() const
+{
+    return d->mEmail;
+}
+
+void ContactGroup::Data::insertCustom(const QString &key, const QString &value)
+{
+    d->mCustoms.insert(key, value);
+}
+
+void ContactGroup::Data::removeCustom(const QString &key)
+{
+    d->mCustoms.remove(key);
+}
+
+QString ContactGroup::Data::custom(const QString &key) const
+{
+    return d->mCustoms.value(key);
+}
+
+ContactGroup::Data &ContactGroup::Data::operator=(const ContactGroup::Data &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool ContactGroup::Data::operator==(const Data &other) const
+{
+    return d->mName == other.d->mName &&
+           d->mEmail == other.d->mEmail &&
+           d->mCustoms == other.d->mCustoms;
+}
+
+class Q_DECL_HIDDEN ContactGroup::Private : public QSharedData
+{
+public:
+    Private()
+        : QSharedData()
+        , mIdentifier(QUuid::createUuid().toString().mid(1, 36))  //We avoid the curly braces so the string is RFC4122 compliant and can be used as urn
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mIdentifier = other.mIdentifier;
+        mName = other.mName;
+        mContactReferences = other.mContactReferences;
+        mContactGroupReferences = other.mContactGroupReferences;
+        mDataObjects = other.mDataObjects;
+    }
+
+    QString mIdentifier;
+    QString mName;
+    ContactGroup::ContactReference::List mContactReferences;
+    ContactGroup::ContactGroupReference::List mContactGroupReferences;
+    ContactGroup::Data::List mDataObjects;
+};
+
+ContactGroup::ContactGroup()
+    : d(new Private)
+{
+}
+
+ContactGroup::ContactGroup(const ContactGroup &other)
+    : d(other.d)
+{
+}
+
+ContactGroup::ContactGroup(const QString &name)
+    : d(new Private)
+{
+    d->mName = name;
+}
+
+ContactGroup::~ContactGroup()
+{
+}
+
+void ContactGroup::setName(const QString &name)
+{
+    d->mName = name;
+}
+
+QString ContactGroup::name() const
+{
+    return d->mName;
+}
+
+void ContactGroup::setId(const QString &id)
+{
+    d->mIdentifier = id;
+}
+
+QString ContactGroup::id() const
+{
+    return d->mIdentifier;
+}
+
+unsigned int ContactGroup::count() const
+{
+    return d->mContactReferences.count() + d->mDataObjects.count();
+}
+
+unsigned int ContactGroup::contactReferenceCount() const
+{
+    return d->mContactReferences.count();
+}
+
+unsigned int ContactGroup::contactGroupReferenceCount() const
+{
+    return d->mContactGroupReferences.count();
+}
+
+unsigned int ContactGroup::dataCount() const
+{
+    return d->mDataObjects.count();
+}
+
+ContactGroup::ContactReference &ContactGroup::contactReference(unsigned int index)
+{
+    Q_ASSERT_X(index < (unsigned int)d->mContactReferences.count(),
+               "contactReference()", "index out of range");
+
+    return d->mContactReferences[index];
+}
+
+const ContactGroup::ContactReference &ContactGroup::contactReference(unsigned int index) const
+{
+    Q_ASSERT_X(index < (unsigned int)d->mContactReferences.count(),
+               "contactReference()", "index out of range");
+
+    return d->mContactReferences[index];
+}
+
+ContactGroup::ContactGroupReference &ContactGroup::contactGroupReference(unsigned int index)
+{
+    Q_ASSERT_X(index < (unsigned int)d->mContactGroupReferences.count(),
+               "contactGroupReference()", "index out of range");
+
+    return d->mContactGroupReferences[index];
+}
+
+const ContactGroup::ContactGroupReference &ContactGroup::contactGroupReference(
+    unsigned int index) const
+{
+    Q_ASSERT_X(index < (unsigned int)d->mContactGroupReferences.count(),
+               "contactGroupReference()", "index out of range");
+
+    return d->mContactGroupReferences[index];
+}
+
+ContactGroup::Data &ContactGroup::data(unsigned int index)
+{
+    Q_ASSERT_X(index < (unsigned int)d->mDataObjects.count(), "data()", "index out of range");
+
+    return d->mDataObjects[index];
+}
+
+const ContactGroup::Data &ContactGroup::data(unsigned int index) const
+{
+    Q_ASSERT_X(index < (unsigned int)d->mDataObjects.count(), "data()", "index out of range");
+
+    return d->mDataObjects[index];
+}
+
+void ContactGroup::append(const ContactReference &reference)
+{
+    d->mContactReferences.append(reference);
+}
+
+void ContactGroup::append(const ContactGroupReference &reference)
+{
+    d->mContactGroupReferences.append(reference);
+}
+
+void ContactGroup::append(const Data &data)
+{
+    d->mDataObjects.append(data);
+}
+
+void ContactGroup::remove(const ContactReference &reference)
+{
+    d->mContactReferences.removeOne(reference);
+}
+
+void ContactGroup::remove(const ContactGroupReference &reference)
+{
+    d->mContactGroupReferences.removeOne(reference);
+}
+
+void ContactGroup::remove(const Data &data)
+{
+    d->mDataObjects.removeOne(data);
+}
+
+void ContactGroup::removeAllContactReferences()
+{
+    d->mContactReferences.clear();
+}
+
+void ContactGroup::removeAllContactGroupReferences()
+{
+    d->mContactGroupReferences.clear();
+}
+
+void ContactGroup::removeAllContactData()
+{
+    d->mDataObjects.clear();
+}
+
+ContactGroup &ContactGroup::operator=(const ContactGroup &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool ContactGroup::operator==(const ContactGroup &other) const
+{
+    return d->mIdentifier == other.d->mIdentifier &&
+           d->mName == other.d->mName &&
+           d->mContactReferences == other.d->mContactReferences &&
+           d->mContactGroupReferences == other.d->mContactGroupReferences &&
+           d->mDataObjects == other.d->mDataObjects;
+}
+
+QString ContactGroup::mimeType()
+{
+    return QStringLiteral("application/x-vnd.kde.contactgroup");
+}
diff --git a/src/contactgroup.h b/src/contactgroup.h
new file mode 100644 (file)
index 0000000..b43c716
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+  This file is part of the KContacts framework.
+  Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_CONTACTGROUP_H
+#define KCONTACTS_CONTACTGROUP_H
+
+#include <QtCore/QVector>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QMetaType>
+
+#include "kcontacts_export.h"
+
+class QString;
+
+namespace KContacts
+{
+
+/**
+ * @short This class represents a group of contacts.
+ *
+ * It can contain two types of contacts, either a reference
+ * or data.
+ * The reference entry is just an unique identifier which
+ * identifies the real contact in the system.
+ * The data entry contains a name and an email address.
+ *
+ * @author Tobias Koenig <tokoe@kde.org>
+ * @since 4.3
+ */
+class KCONTACTS_EXPORT ContactGroup
+{
+public:
+
+    /**
+     * This class represents a contact reference
+     */
+    class KCONTACTS_EXPORT ContactReference
+    {
+    public:
+        /**
+         * A list of contact references.
+         */
+        typedef QVector<ContactReference> List;
+
+        /**
+         * Creates an empty contact reference.
+         */
+        ContactReference();
+
+        /**
+         * Creates a contact reference from an @p other reference.
+         */
+        ContactReference(const ContactReference &other);
+
+        /**
+         * Creates a contact reference for the given contact @p uid.
+         */
+        ContactReference(const QString &uid);
+
+        /**
+         * Destroys the contact reference.
+         */
+        ~ContactReference();
+
+        /**
+         * Sets the contact uid of the contact reference.
+         * @param uid identifier of the contact to reference
+         * @note That is the Akonadi Item ID of the contact that
+         *       is referenced here.
+         */
+        void setUid(const QString &uid);
+
+        /**
+         * Returns the contact uid of the contact reference.
+         *
+         * @note That is the Akonadi Item ID of the contact that
+         *       is referenced here.
+         */
+        QString uid() const;
+
+        /**
+         * Sets the contact gid of the contact reference.
+         * @param gid globally unique identifier of the contact to reference
+         * @since 4.12
+         */
+        void setGid(const QString &gid);
+
+        /**
+         * Returns the contact GID of the contact reference.
+         * @since 4.12
+         */
+        QString gid() const;
+
+        /**
+         * Sets the preferred email address.
+         */
+        void setPreferredEmail(const QString &email);
+
+        /**
+         * Returns the preferred email address, or an empty string
+         * if no preferred email address is set.
+         */
+        QString preferredEmail() const;
+
+        /**
+         * Inserts a custom entry.
+         * If an entry with the same @p key already exists, it is
+         * overwritten.
+         *
+         * @param key The unique key.
+         * @param value The value.
+         */
+        void insertCustom(const QString &key, const QString &value);
+
+        /**
+         * Removes the custom entry with the given @p key.
+         */
+        void removeCustom(const QString &key);
+
+        /**
+         * Returns the value for the given @p key, or an empty string
+         * if the entry for that key does not exists.
+         */
+        QString custom(const QString &key) const;
+
+        /**
+         * @internal
+         */
+        ContactReference &operator=(const ContactReference &other);
+
+        /**
+         * @internal
+         */
+        bool operator==(const ContactReference &other) const;
+
+    private:
+        class ContactReferencePrivate;
+        QSharedDataPointer<ContactReferencePrivate> d;
+    };
+
+    /**
+     * This class represents a contact group reference
+     */
+    class KCONTACTS_EXPORT ContactGroupReference
+    {
+    public:
+        /**
+         * A list of contact group references.
+         */
+        typedef QVector<ContactGroupReference> List;
+
+        /**
+         * Creates an empty contact group reference.
+         */
+        ContactGroupReference();
+
+        /**
+         * Creates a contact group reference from an @p other reference.
+         */
+        ContactGroupReference(const ContactGroupReference &other);
+
+        /**
+         * Creates a contact group reference for the given contact group @p uid.
+         */
+        ContactGroupReference(const QString &uid);
+
+        /**
+         * Destroys the contact group reference.
+         */
+        ~ContactGroupReference();
+
+        /**
+         * Sets the contact group uid of the contact group reference.
+         */
+        void setUid(const QString &uid);
+
+        /**
+         * Returns the contact group uid of the contact group reference.
+         */
+        QString uid() const;
+
+        /**
+         * Inserts a custom entry.
+         * If an entry with the same @p key already exists, it is
+         * overwritten.
+         *
+         * @param key The unique key.
+         * @param value The value.
+         */
+        void insertCustom(const QString &key, const QString &value);
+
+        /**
+         * Removes the custom entry with the given @p key.
+         */
+        void removeCustom(const QString &key);
+
+        /**
+         * Returns the value for the given @p key, or an empty string
+         * if the entry for that key does not exists.
+         */
+        QString custom(const QString &key) const;
+
+        /**
+         * @internal
+         */
+        ContactGroupReference &operator=(const ContactGroupReference &other);
+
+        /**
+         * @internal
+         */
+        bool operator==(const ContactGroupReference &other) const;
+
+    private:
+        class ContactGroupReferencePrivate;
+        QSharedDataPointer<ContactGroupReferencePrivate> d;
+    };
+
+    /**
+     * This class represents a contact data object
+     */
+    class KCONTACTS_EXPORT Data
+    {
+    public:
+        /**
+         * A list of contact data.
+         */
+        typedef QVector<Data> List;
+
+        /**
+         * Creates an empty contact data object.
+         */
+        Data();
+
+        /**
+         * Creates a contact data object from an @p other data object.
+         */
+        Data(const Data &other);
+
+        /**
+         * Creates a contact data object with the given @p name and @p email address.
+         */
+        Data(const QString &name, const QString &email);
+
+        /**
+         * Destroys the contact data object.
+         */
+        ~Data();
+
+        /**
+         * Sets the @p name of the contact data object.
+         */
+        void setName(const QString &name);
+
+        /**
+         * Returns the name of the contact data object.
+         */
+        QString name() const;
+
+        /**
+         * Sets the @p email address of the contact data object.
+         */
+        void setEmail(const QString &email);
+
+        /**
+         * Returns the email address of the contact data object.
+         */
+        QString email() const;
+
+        /**
+         * Inserts a custom entry.
+         * If an entry with the same @p key already exists, it is
+         * overwritten.
+         *
+         * @param key The unique key.
+         * @param value The value.
+         */
+        void insertCustom(const QString &key, const QString &value);
+
+        /**
+         * Removes the custom entry with the given @p key.
+         */
+        void removeCustom(const QString &key);
+
+        /**
+         * Returns the value for the given @p key, or an empty string
+         * if the entry for that key does not exists.
+         */
+        QString custom(const QString &key) const;
+
+        /**
+         * @internal
+         */
+        Data &operator=(const Data &other);
+
+        /**
+         * @internal
+         */
+        bool operator==(const Data &other) const;
+
+    private:
+        class DataPrivate;
+        QSharedDataPointer<DataPrivate> d;
+    };
+
+    /**
+     * A list of contact groups.
+     */
+    typedef QVector<ContactGroup> List;
+
+    /**
+     * Creates an empty contact group.
+     */
+    ContactGroup();
+
+    /**
+     * Creates a contact group from an @p other group.
+     */
+    ContactGroup(const ContactGroup &other);
+
+    /**
+     * Creates a contact group with the given name.
+     */
+    ContactGroup(const QString &name);
+
+    /**
+     * Destroys the contact group.
+     */
+    ~ContactGroup();
+
+    /**
+     * Sets the unique @p id of the contact group.
+     */
+    void setId(const QString &id);
+
+    /**
+     * Returns the unique id of the contact group.
+     */
+    QString id() const;
+
+    /**
+     * Sets the i18n'd @p name of the contact group.
+     */
+    void setName(const QString &name);
+
+    /**
+     * Returns the i18n'd name of the contact group.
+     */
+    QString name() const;
+
+    /**
+     * Returns the number of contacts in this group.
+     * That includes the contact references and contact data.
+     */
+    unsigned int count() const;
+
+    /**
+     * Returns the number of contact references in this group.
+     */
+    unsigned int contactReferenceCount() const;
+
+    /**
+     * Returns the number of group references in this group.
+     */
+    unsigned int contactGroupReferenceCount() const;
+
+    /**
+     * Returns the number of contact data objects in this group.
+     */
+    unsigned int dataCount() const;
+
+    /**
+     * Returns the contact reference at the given @p index.
+     */
+    ContactReference &contactReference(unsigned int index);
+
+    /**
+     * Returns the contact reference at the given @p index.
+     */
+    const ContactReference &contactReference(unsigned int index) const;
+
+    /**
+     * Returns the contact group reference at the given @p index.
+     */
+    ContactGroupReference &contactGroupReference(unsigned int index);
+
+    /**
+     * Returns the contact group reference at the given @p index.
+     */
+    const ContactGroupReference &contactGroupReference(unsigned int index) const;
+
+    /**
+     * Returns the contact data object at the given @p index.
+     */
+    Data &data(unsigned int index);
+
+    /**
+     * Returns the contact data object at the given @p index.
+     */
+    const Data &data(unsigned int index) const;
+
+    /**
+     * Appends a new contact @p reference to the contact group.
+     */
+    void append(const ContactReference &reference);
+
+    /**
+     * Appends a new contact group @p reference to the contact group.
+     */
+    void append(const ContactGroupReference &reference);
+
+    /**
+     * Appends a new contact @p data object to the contact group.
+     */
+    void append(const Data &data);
+
+    /**
+     * Removes the given contact @p reference from the contact group.
+     */
+    void remove(const ContactReference &reference);
+
+    /**
+     * Removes the given contact group @p reference from the contact group.
+     */
+    void remove(const ContactGroupReference &reference);
+
+    /**
+     * Removes the given contact @p data object from the contact group.
+     */
+    void remove(const Data &data);
+
+    /**
+     * Removes all contact references from the contact group.
+     */
+    void removeAllContactReferences();
+
+    /**
+     * Removes all contact group references from the contact group.
+     */
+    void removeAllContactGroupReferences();
+
+    /**
+     * Removes all contact data from the contact group.
+     */
+    void removeAllContactData();
+
+    /**
+     * @internal
+     */
+    ContactGroup &operator=(const ContactGroup &other);
+
+    /**
+     * @internal
+     */
+    bool operator==(const ContactGroup &other) const;
+
+    /**
+     * Returns the MIME type used for Contact Groups
+     */
+    static QString mimeType();
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+}
+Q_DECLARE_TYPEINFO(KContacts::ContactGroup::ContactGroupReference, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(KContacts::ContactGroup::ContactReference, Q_MOVABLE_TYPE);
+
+#define KCONTACTS_CONTACTGROUP_METATYPE_DEFINED
+Q_DECLARE_METATYPE(KContacts::ContactGroup)
+
+#endif
diff --git a/src/contactgroup.xsd b/src/contactgroup.xsd
new file mode 100644 (file)
index 0000000..ba486b6
--- /dev/null
@@ -0,0 +1,76 @@
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+<!--
+    Copyright (C) 2007 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Library General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or (at your
+    option) any later version.
+
+    This library is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+    License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to the
+    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301, USA.
+-->
+
+  <!-- The root element of every ContactGroup document -->
+  <xsd:element name="contactGroup">
+    <xsd:complexType>
+      <!-- A list of contacts or contact references -->
+      <xsd:choice minOccurs="0" maxOccurs="unbound">
+        <xsd:element name="contactData" type="contactDataType"/>
+        <xsd:element name="contactReference" type="contactReferenceType"/>
+      </xsd:choice>
+
+      <!-- The unique identifier of this contact group -->
+      <xsd:attribute name="uid" type="xsd:string" use="required" />
+
+      <!-- The i18n'ed name of this contact group -->
+      <xsd:attribute name="name" type="xsd:string" use="required" />
+    </xsd:complexType>
+  </xsd:element>
+
+  <!-- The type for inline contacts. -->
+  <xsd:complexType name="contactDataType">
+    <xsd:sequence>
+      <!-- The i18n'ed name of this contact -->
+      <xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1"/>
+
+      <!-- The plain email address (without the name) of this contact -->
+      <xsd:element name="email" type="xsd:string" minOccurs="1" maxOccurs="1"/>
+
+      <!-- A list of custom fields for this contact -->
+      <xsd:element name="custom" minOccurs="0" maxOccurs="unbound">
+        <xsd:complexType>
+          <xsd:attribute name="key" type="xsd:string" use="required" />
+          <xsd:attribute name="value" type="xsd:string" use="required" />
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:sequence>
+  </xsd:complexType>
+
+  <xsd:complexType name="contactReferenceType">
+    <xsd:sequence>
+      <!-- The uid of the contact this entry references -->
+      <xsd:element name="uid" type="xsd:string" minOccurs="1" maxOccurs="1"/>
+
+      <!-- The preferred email address which shall be used for the contact -->
+      <xsd:element name="preferredEmail" type="xsd:string" minOccurs="0" maxOccurs="1"/>
+
+      <!-- A list of custom fields for this contact -->
+      <xsd:element name="custom" minOccurs="0" maxOccurs="unbound">
+        <xsd:complexType>
+          <xsd:attribute name="key" type="xsd:string" use="required" />
+          <xsd:attribute name="value" type="xsd:string" use="required" />
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:sequence>
+  </xsd:complexType>
+
+</xsd:schema>
diff --git a/src/contactgrouptool.cpp b/src/contactgrouptool.cpp
new file mode 100644 (file)
index 0000000..0f70df9
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+  This file is part of the KContacts framework.
+  Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>
+  Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "contactgrouptool.h"
+#include "contactgroup.h"
+
+#include <QtCore/QIODevice>
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamWriter>
+
+using namespace KContacts;
+
+class XmlContactGroupWriter : public QXmlStreamWriter
+{
+public:
+    XmlContactGroupWriter();
+
+    void write(const ContactGroup &group, QIODevice *device);
+    void write(const QVector<ContactGroup> &groupLis, QIODevice *device);
+
+private:
+    void writeGroup(const ContactGroup &group);
+    void writeContactReference(const ContactGroup::ContactReference &reference);
+    void writeContactGroupReference(const ContactGroup::ContactGroupReference &reference);
+    void writeData(const ContactGroup::Data &data);
+};
+
+XmlContactGroupWriter::XmlContactGroupWriter()
+{
+    setAutoFormatting(true);
+}
+
+void XmlContactGroupWriter::write(const ContactGroup &group, QIODevice *device)
+{
+    setDevice(device);
+
+    writeStartDocument();
+
+    writeGroup(group);
+
+    writeEndDocument();
+}
+
+void XmlContactGroupWriter::write(const QVector<ContactGroup> &groupList, QIODevice *device)
+{
+    setDevice(device);
+
+    writeStartDocument();
+
+    writeStartElement(QStringLiteral("contactGroupList"));
+
+    foreach (const ContactGroup &group, groupList) {
+        writeGroup(group);
+    }
+
+    writeEndElement();
+
+    writeEndDocument();
+}
+
+void XmlContactGroupWriter::writeGroup(const ContactGroup &group)
+{
+    writeStartElement(QStringLiteral("contactGroup"));
+    writeAttribute(QStringLiteral("uid"), group.id());
+    writeAttribute(QStringLiteral("name"), group.name());
+
+    for (uint i = 0; i < group.contactReferenceCount(); ++i) {
+        writeContactReference(group.contactReference(i));
+    }
+
+    for (uint i = 0; i < group.contactGroupReferenceCount(); ++i) {
+        writeContactGroupReference(group.contactGroupReference(i));
+    }
+
+    for (uint i = 0; i < group.dataCount(); ++i) {
+        writeData(group.data(i));
+    }
+
+    writeEndElement();
+}
+
+void XmlContactGroupWriter::writeContactReference(const ContactGroup::ContactReference &reference)
+{
+    writeStartElement(QStringLiteral("contactReference"));
+    writeAttribute(QStringLiteral("uid"), reference.uid());
+    writeAttribute(QStringLiteral("gid"), reference.gid());
+    if (!reference.preferredEmail().isEmpty()) {
+        writeAttribute(QStringLiteral("preferredEmail"), reference.preferredEmail());
+    }
+
+    // TODO: customs
+
+    writeEndElement();
+}
+
+void XmlContactGroupWriter::writeContactGroupReference(
+    const ContactGroup::ContactGroupReference &reference)
+{
+    writeStartElement(QStringLiteral("contactGroupReference"));
+    writeAttribute(QStringLiteral("uid"), reference.uid());
+
+    // TODO: customs
+
+    writeEndElement();
+}
+
+void XmlContactGroupWriter::writeData(const ContactGroup::Data &data)
+{
+    writeStartElement(QStringLiteral("contactData"));
+    writeAttribute(QStringLiteral("name"), data.name());
+    writeAttribute(QStringLiteral("email"), data.email());
+
+    // TODO: customs
+
+    writeEndElement();
+}
+
+class XmlContactGroupReader : public QXmlStreamReader
+{
+public:
+    XmlContactGroupReader();
+
+    bool read(QIODevice *device, ContactGroup &group);
+    bool read(QIODevice *device, QVector<ContactGroup> &groupList);
+
+private:
+    bool readGroup(ContactGroup &group);
+    bool readContactReference(ContactGroup::ContactReference &reference);
+    bool readContactGroupReference(ContactGroup::ContactGroupReference &reference);
+    bool readData(ContactGroup::Data &data);
+};
+
+XmlContactGroupReader::XmlContactGroupReader()
+{
+}
+
+bool XmlContactGroupReader::read(QIODevice *device, ContactGroup &group)
+{
+    setDevice(device);
+
+    while (!atEnd()) {
+        readNext();
+        if (isStartElement()) {
+            if (name() == QLatin1String("contactGroup")) {
+                return readGroup(group);
+            } else {
+                raiseError(QStringLiteral("The document does not describe a ContactGroup"));
+            }
+        }
+    }
+
+    return error() == NoError;
+}
+
+bool XmlContactGroupReader::read(QIODevice *device, QVector<ContactGroup> &groupList)
+{
+    setDevice(device);
+
+    int depth = 0;
+
+    while (!atEnd()) {
+        readNext();
+        if (isStartElement()) {
+            ++depth;
+            if (depth == 1) {
+                if (name() == QLatin1String("contactGroupList")) {
+                    continue;
+                } else {
+                    raiseError(QStringLiteral("The document does not describe a list of ContactGroup"));
+                }
+            } else if (depth == 2) {
+                if (name() == QLatin1String("contactGroup")) {
+                    ContactGroup group;
+                    if (!readGroup(group)) {
+                        return false;
+                    }
+
+                    groupList.append(group);
+                } else {
+                    raiseError(QStringLiteral("The document does not describe a list of ContactGroup"));
+                }
+            }
+        }
+
+        if (isEndElement()) {
+            --depth;
+        }
+    }
+
+    return error() == NoError;
+}
+
+bool XmlContactGroupReader::readGroup(ContactGroup &group)
+{
+    const QXmlStreamAttributes elementAttributes = attributes();
+    const QStringRef uid = elementAttributes.value(QLatin1String("uid"));
+    if (uid.isEmpty()) {
+        raiseError(QStringLiteral("ContactGroup is missing a uid"));
+        return false;
+    }
+
+    const QStringRef groupName = elementAttributes.value(QLatin1String("name"));
+    if (groupName.isEmpty()) {
+        raiseError(QStringLiteral("ContactGroup is missing a name"));
+        return false;
+    }
+
+    group.setId(uid.toString());
+    group.setName(groupName.toString());
+
+    while (!atEnd()) {
+        readNext();
+        if (isStartElement()) {
+            if (name() == QLatin1String("contactData")) {
+                ContactGroup::Data data;
+                if (!readData(data)) {
+                    return false;
+                }
+                group.append(data);
+            } else if (name() == QLatin1String("contactReference")) {
+                ContactGroup::ContactReference reference;
+                if (!readContactReference(reference)) {
+                    return false;
+                }
+                group.append(reference);
+            } else if (name() == QLatin1String("contactGroupReference")) {
+                ContactGroup::ContactGroupReference reference;
+                if (!readContactGroupReference(reference)) {
+                    return false;
+                }
+                group.append(reference);
+            } else {
+                raiseError(QStringLiteral("The document does not describe a ContactGroup"));
+            }
+        }
+
+        if (isEndElement()) {
+            if (name() == QLatin1String("contactGroup")) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+bool XmlContactGroupReader::readData(ContactGroup::Data &data)
+{
+    const QXmlStreamAttributes elementAttributes = attributes();
+    const QStringRef email = elementAttributes.value(QLatin1String("email"));
+    if (email.isEmpty()) {
+        raiseError(QStringLiteral("ContactData is missing an email address"));
+        return false;
+    }
+
+    const QStringRef name = elementAttributes.value(QLatin1String("name"));
+
+    data.setName(name.toString());
+    data.setEmail(email.toString());
+
+    return true;
+}
+
+bool XmlContactGroupReader::readContactReference(ContactGroup::ContactReference &reference)
+{
+    const QXmlStreamAttributes elementAttributes = attributes();
+    const QStringRef uid = elementAttributes.value(QLatin1String("uid"));
+    const QStringRef gid = elementAttributes.value(QLatin1String("gid"));
+    if (uid.isEmpty() && gid.isEmpty()) {
+        raiseError(QStringLiteral("ContactReference is missing both uid and gid"));
+        return false;
+    }
+    const QStringRef preferredEmail = elementAttributes.value(QLatin1String("preferredEmail"));
+
+    reference.setUid(uid.toString());
+    reference.setGid(gid.toString());
+    reference.setPreferredEmail(preferredEmail.toString());
+
+    return true;
+}
+
+bool XmlContactGroupReader::readContactGroupReference(
+    ContactGroup::ContactGroupReference &reference)
+{
+    const QXmlStreamAttributes elementAttributes = attributes();
+    const QStringRef uid = elementAttributes.value(QLatin1String("uid"));
+    if (uid.isEmpty()) {
+        raiseError(QStringLiteral("ContactGroupReference is missing a uid"));
+        return false;
+    }
+
+    reference.setUid(uid.toString());
+
+    return true;
+}
+
+bool ContactGroupTool::convertFromXml(QIODevice *device, ContactGroup &group,
+                                      QString *errorMessage)
+{
+    Q_UNUSED(errorMessage);
+
+    XmlContactGroupReader reader;
+
+    bool ok = reader.read(device, group);
+
+    if (!ok && errorMessage != Q_NULLPTR) {
+        *errorMessage = reader.errorString();
+    }
+
+    return ok;
+}
+
+bool ContactGroupTool::convertToXml(const ContactGroup &group, QIODevice *device,
+                                    QString *errorMessage)
+{
+    Q_UNUSED(errorMessage);
+
+    XmlContactGroupWriter writer;
+    writer.write(group, device);
+
+    return true;
+}
+
+bool ContactGroupTool::convertFromXml(QIODevice *device, QVector<ContactGroup> &groupList,
+                                      QString *errorMessage)
+{
+    Q_UNUSED(errorMessage);
+
+    XmlContactGroupReader reader;
+
+    bool ok = reader.read(device, groupList);
+
+    if (!ok && errorMessage != Q_NULLPTR) {
+        *errorMessage = reader.errorString();
+    }
+
+    return ok;
+}
+
+bool ContactGroupTool::convertToXml(const QVector<ContactGroup> &groupList,
+                                    QIODevice *device, QString *errorMessage)
+{
+    Q_UNUSED(errorMessage);
+
+    XmlContactGroupWriter writer;
+    writer.write(groupList, device);
+
+    return true;
+}
diff --git a/src/contactgrouptool.h b/src/contactgrouptool.h
new file mode 100644 (file)
index 0000000..0b9a66b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+  This file is part of the KContacts framework.
+  Copyright (c) 2008 Tobias Koenig <tokoe@kde.org>
+  Copyright (c) 2008 Kevin Krammer <kevin.krammer@gmx.at>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_CONTACTGROUPTOOL_H
+#define KCONTACTS_CONTACTGROUPTOOL_H
+
+#include "kcontacts_export.h"
+#include <QString>
+class QIODevice;
+
+template <class T> class QList;
+
+namespace KContacts
+{
+
+class ContactGroup;
+
+/**
+ * Static methods for converting ContactGroup to XML format and vice versa.
+ *
+ * @author Kevin Krammer <kevin.krammer@gmx.at>
+ * @since 4.3
+ */
+namespace ContactGroupTool
+{
+
+/**
+ * Converts XML data coming from a @p device into a contact @p group.
+ * If an error occurs, @c false is returned and @p errorMessage is set.
+ */
+KCONTACTS_EXPORT bool convertFromXml(QIODevice *device, ContactGroup &group,
+                                     QString *errorMessage = Q_NULLPTR);
+
+/**
+ * Converts a contact @p group into XML data and writes them to a @p device.
+ * If an error occurs, @c false is returned and @p errorMessage is set.
+ */
+KCONTACTS_EXPORT bool convertToXml(const ContactGroup &group, QIODevice *device,
+                                   QString *errorMessage = Q_NULLPTR);
+
+/**
+ * Converts XML data coming from a @p device into a @p list of contact groups.
+ * If an error occurs, @c false is returned and @p errorMessage is set.
+ */
+KCONTACTS_EXPORT bool convertFromXml(QIODevice *device,
+                                     QVector<ContactGroup> &list,
+                                     QString *errorMessage = Q_NULLPTR);
+
+/**
+ * Converts a @p list of contact groups into XML data and writes them to a @p device.
+ * If an error occurs, @c false is returned and @p errorMessage is set.
+ */
+KCONTACTS_EXPORT bool convertToXml(const QVector<ContactGroup> &list,
+                                   QIODevice *device, QString *errorMessage = Q_NULLPTR);
+}
+
+}
+
+#endif
diff --git a/src/converter/CMakeLists.txt b/src/converter/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6d07969
--- /dev/null
@@ -0,0 +1,22 @@
+ecm_generate_headers(KContacts_CamelCase_Converter_HEADERS
+  HEADER_NAMES
+  LDIFConverter
+  VCardConverter
+  PREFIX KContacts
+  REQUIRED_HEADERS KContacts_Converter_HEADERS
+)
+
+install(FILES
+    ${KContacts_CamelCase_Converter_HEADERS}
+    DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KContacts/KContacts
+    COMPONENT Devel
+)
+
+
+install(FILES
+    ${KContacts_Converter_HEADERS}
+    DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KContacts/kcontacts
+    COMPONENT Devel
+)
+
+
diff --git a/src/converter/ldifconverter.cpp b/src/converter/ldifconverter.cpp
new file mode 100644 (file)
index 0000000..d1fee94
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003  Helge Deller <deller@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+/*
+    Useful links:
+        - http://tldp.org/HOWTO/LDAP-Implementation-HOWTO/schemas.html
+        - http://www.faqs.org/rfcs/rfc2849.html
+
+    Not yet handled items:
+        - objectclass microsoftaddressbook
+                - info,
+                - initials,
+                - otherfacsimiletelephonenumber,
+                - otherpager,
+                - physicaldeliveryofficename,
+*/
+
+#include "ldifconverter.h"
+#include "vcardconverter.h"
+#include "address.h"
+#include "addressee.h"
+#include "kcontacts_debug.h"
+
+#include "ldif_p.h"
+
+#include <klocalizedstring.h>
+
+#include <QtCore/QStringList>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+
+using namespace KContacts;
+
+/* internal functions - do not use !! */
+
+namespace KContacts
+{
+/**
+  @internal
+
+  Evaluates @p fieldname and sets the @p value at the addressee or the address
+  objects when appropriate.
+
+  @param a The addressee to store information into
+  @param homeAddr The home address to store respective information into
+  @param workAddr The work address to store respective information into
+  @param fieldname LDIF field name to evaluate
+  @param value The value of the field addressed by @p fieldname
+*/
+void evaluatePair(Addressee &a, Address &homeAddr,
+                  Address &workAddr,
+                  QString &fieldname, QString &value,
+                  int &birthday, int &birthmonth, int &birthyear, ContactGroup &contactGroup);
+}
+
+/* generate LDIF stream */
+
+static void ldif_out(QTextStream &t, const QString &formatStr,
+                     const QString &value)
+{
+    if (value.isEmpty()) {
+        return;
+    }
+
+    const QByteArray txt = Ldif::assembleLine(formatStr, value, 72);
+
+    // write the string
+    t << QString::fromUtf8(txt) << "\n";
+}
+
+bool LDIFConverter::addresseeAndContactGroupToLDIF(const AddresseeList &addrList, const ContactGroup::List &contactGroupList, QString &str)
+{
+    bool result = addresseeToLDIF(addrList, str);
+    if (!contactGroupList.isEmpty()) {
+        result = contactGroupToLDIF(contactGroupList, str);
+    }
+    return result;
+}
+
+bool LDIFConverter::contactGroupToLDIF(const ContactGroup &contactGroup, QString &str)
+{
+    if (contactGroup.dataCount() <= 0) {
+        return false;
+    }
+    QTextStream t(&str, QIODevice::WriteOnly | QIODevice::Append);
+    t.setCodec(QTextCodec::codecForName("UTF-8"));
+    t << "objectclass: top\n";
+    t << "objectclass: groupOfNames\n";
+
+    for (unsigned int i = 0; i < contactGroup.dataCount(); ++i) {
+        ContactGroup::Data data = contactGroup.data(i);
+        const QString value = QStringLiteral("cn=%1,mail=%2").arg(data.name(), data.email());
+        ldif_out(t, QStringLiteral("member"), value);
+    }
+
+    t << "\n";
+    return true;
+}
+
+bool LDIFConverter::contactGroupToLDIF(const ContactGroup::List &contactGroupList, QString &str)
+{
+    ContactGroup::List::ConstIterator it;
+    ContactGroup::List::ConstIterator end(contactGroupList.constEnd());
+    for (it = contactGroupList.constBegin(); it != end; ++it) {
+        contactGroupToLDIF(*it, str);
+    }
+    return true;
+}
+
+bool LDIFConverter::addresseeToLDIF(const AddresseeList &addrList, QString &str)
+{
+    AddresseeList::ConstIterator it;
+    AddresseeList::ConstIterator end(addrList.constEnd());
+    for (it = addrList.constBegin(); it != end; ++it) {
+        addresseeToLDIF(*it, str);
+    }
+    return true;
+}
+
+bool LDIFConverter::addresseeToLDIF(const Addressee &addr, QString &str)
+{
+    if (addr.isEmpty()) {
+        return false;
+    }
+
+    QTextStream t(&str, QIODevice::WriteOnly | QIODevice::Append);
+    t.setCodec(QTextCodec::codecForName("UTF-8"));
+
+    const Address homeAddr = addr.address(Address::Home);
+    const Address workAddr = addr.address(Address::Work);
+
+    ldif_out(t, QStringLiteral("dn"), QStringLiteral("cn=%1,mail=%2").
+             arg(addr.formattedName().simplified(), addr.preferredEmail()));
+    t << "objectclass: top\n";
+    t << "objectclass: person\n";
+    t << "objectclass: organizationalPerson\n";
+
+    ldif_out(t, QStringLiteral("givenname"), addr.givenName());
+    ldif_out(t, QStringLiteral("sn"), addr.familyName());
+    ldif_out(t, QStringLiteral("cn"), addr.formattedName().simplified());
+    ldif_out(t, QStringLiteral("uid"), addr.uid());
+    ldif_out(t, QStringLiteral("nickname"), addr.nickName());
+    ldif_out(t, QStringLiteral("xmozillanickname"), addr.nickName());
+    ldif_out(t, QStringLiteral("mozillanickname"), addr.nickName());
+
+    ldif_out(t, QStringLiteral("mail"), addr.preferredEmail());
+    const QStringList emails = addr.emails();
+    const int numEmails = emails.count();
+    for (int i = 1; i < numEmails; ++i) {
+        if (i == 0) {
+            //nothing
+        } else if (i == 1) {
+            ldif_out(t, QStringLiteral("mozillasecondemail"), emails[1]);
+        } else {
+            ldif_out(t, QStringLiteral("othermailbox"), emails[i]);
+        }
+    }
+    //ldif_out( t, "mozilla_AIMScreenName: %1\n", "screen_name" );
+
+    ldif_out(t, QStringLiteral("telephonenumber"),
+             addr.phoneNumber(PhoneNumber::Work).number());
+    ldif_out(t, QStringLiteral("facsimiletelephonenumber"),
+             addr.phoneNumber(PhoneNumber::Fax).number());
+    ldif_out(t, QStringLiteral("homephone"),
+             addr.phoneNumber(PhoneNumber::Home).number());
+    ldif_out(t, QStringLiteral("mobile"),
+             addr.phoneNumber(PhoneNumber::Cell).number());    // Netscape 7
+    ldif_out(t, QStringLiteral("cellphone"),
+             addr.phoneNumber(PhoneNumber::Cell).number());    // Netscape 4.x
+    ldif_out(t, QStringLiteral("pager"),
+             addr.phoneNumber(PhoneNumber::Pager).number());
+    ldif_out(t, QStringLiteral("pagerphone"),
+             addr.phoneNumber(PhoneNumber::Pager).number());
+
+    ldif_out(t, QStringLiteral("streethomeaddress"), homeAddr.street());
+    ldif_out(t, QStringLiteral("postalcode"), workAddr.postalCode());
+    ldif_out(t, QStringLiteral("postofficebox"), workAddr.postOfficeBox());
+
+    QStringList streets = homeAddr.street().split(QLatin1Char('\n'));
+    const int numberOfStreets(streets.count());
+    if (numberOfStreets > 0) {
+        ldif_out(t, QStringLiteral("homepostaladdress"), streets.at(0));     // Netscape 7
+    }
+    if (numberOfStreets > 1) {
+        ldif_out(t, QStringLiteral("mozillahomepostaladdress2"), streets.at(1));     // Netscape 7
+    }
+    ldif_out(t, QStringLiteral("mozillahomelocalityname"), homeAddr.locality());     // Netscape 7
+    ldif_out(t, QStringLiteral("mozillahomestate"), homeAddr.region());
+    ldif_out(t, QStringLiteral("mozillahomepostalcode"), homeAddr.postalCode());
+    ldif_out(t, QStringLiteral("mozillahomecountryname"),
+             Address::ISOtoCountry(homeAddr.country()));
+    ldif_out(t, QStringLiteral("locality"), workAddr.locality());
+    ldif_out(t, QStringLiteral("streetaddress"), workAddr.street());     // Netscape 4.x
+
+    streets = workAddr.street().split(QLatin1Char('\n'));
+    if (streets.count() > 0) {
+        ldif_out(t, QStringLiteral("street"), streets.at(0));
+    }
+    if (streets.count() > 1) {
+        ldif_out(t, QStringLiteral("mozillaworkstreet2"), streets.at(1));
+    }
+    ldif_out(t, QStringLiteral("countryname"), Address::ISOtoCountry(workAddr.country()));
+    ldif_out(t, QStringLiteral("l"), workAddr.locality());
+    ldif_out(t, QStringLiteral("c"), Address::ISOtoCountry(workAddr.country()));
+    ldif_out(t, QStringLiteral("st"), workAddr.region());
+
+    ldif_out(t, QStringLiteral("title"), addr.title());
+    ldif_out(t, QStringLiteral("vocation"), addr.prefix());
+    ldif_out(t, QStringLiteral("ou"), addr.role());
+    ldif_out(t, QStringLiteral("o"), addr.organization());
+    ldif_out(t, QStringLiteral("organization"), addr.organization());
+    ldif_out(t, QStringLiteral("organizationname"), addr.organization());
+
+    // Compatibility with older kabc versions.
+    if (!addr.department().isEmpty()) {
+        ldif_out(t, QStringLiteral("department"), addr.department());
+    } else {
+        ldif_out(t, QStringLiteral("department"), addr.custom(QStringLiteral("KADDRESSBOOK"),
+                 QStringLiteral("X-Department")));
+    }
+
+    ldif_out(t, QStringLiteral("workurl"), addr.url().url().toDisplayString());
+    ldif_out(t, QStringLiteral("homeurl"), addr.url().url().toDisplayString());
+    ldif_out(t, QStringLiteral("mozillahomeurl"), addr.url().url().toDisplayString());
+
+    ldif_out(t, QStringLiteral("description"), addr.note());
+    if (addr.revision().isValid()) {
+        ldif_out(t, QStringLiteral("modifytimestamp"), dateToVCardString(addr.revision()));
+    }
+
+    const QDateTime birthday = addr.birthday();
+    if (birthday.date().isValid()) {
+        const QDate date = birthday.date();
+        ldif_out(t, QStringLiteral("birthyear"), QString::number(date.year()));
+        ldif_out(t, QStringLiteral("birthmonth"), QString::number(date.month()));
+        ldif_out(t, QStringLiteral("birthday"), QString::number(date.day()));
+    }
+
+    t << "\n";
+
+    return true;
+}
+
+/* convert from LDIF stream */
+bool LDIFConverter::LDIFToAddressee(const QString &str, AddresseeList &addrList, ContactGroup::List &contactGroupList,
+                                    const QDateTime &dt)
+{
+    if (str.isEmpty()) {
+        return true;
+    }
+
+    bool endldif = false, end = false;
+    Ldif ldif;
+    Ldif::ParseValue ret;
+    Addressee a;
+    Address homeAddr, workAddr;
+    int birthday = -1;
+    int birthmonth = -1;
+    int birthyear = -1;
+    ContactGroup contactGroup;
+    ldif.setLdif(str.toLatin1());
+    QDateTime qdt = dt;
+    if (!qdt.isValid()) {
+        qdt = QDateTime::currentDateTime();
+    }
+    a.setRevision(qdt);
+    homeAddr = Address(Address::Home);
+    workAddr = Address(Address::Work);
+
+    do {
+        ret = ldif.nextItem();
+        switch (ret) {
+        case Ldif::Item: {
+            QString fieldname = ldif.attr().toLower();
+            QString value = QString::fromUtf8(ldif.value());
+            evaluatePair(a, homeAddr, workAddr, fieldname, value, birthday, birthmonth, birthyear, contactGroup);
+            break;
+        }
+        case Ldif::EndEntry: {
+            if (contactGroup.count() == 0) {
+                // if the new address is not empty, append it
+                QDateTime birthDate(QDate(birthyear, birthmonth, birthday));
+                if (birthDate.isValid()) {
+                    a.setBirthday(birthDate);
+                }
+
+                if (!a.formattedName().isEmpty() || !a.name().isEmpty() ||
+                        !a.familyName().isEmpty()) {
+                    if (!homeAddr.isEmpty()) {
+                        a.insertAddress(homeAddr);
+                    }
+                    if (!workAddr.isEmpty()) {
+                        a.insertAddress(workAddr);
+                    }
+                    addrList.append(a);
+                }
+            } else {
+                contactGroupList.append(contactGroup);
+            }
+            a = Addressee();
+            contactGroup = ContactGroup();
+            a.setRevision(qdt);
+            homeAddr = Address(Address::Home);
+            workAddr = Address(Address::Work);
+        }
+        break;
+        case Ldif::MoreData: {
+            if (endldif) {
+                end = true;
+            } else {
+                ldif.endLdif();
+                endldif = true;
+                break;
+            }
+        }
+        default:
+            break;
+        }
+    } while (!end);
+
+    return true;
+}
+
+void KContacts::evaluatePair(Addressee &a, Address &homeAddr,
+                             Address &workAddr,
+                             QString &fieldname, QString &value,
+                             int &birthday, int &birthmonth, int &birthyear, ContactGroup &contactGroup)
+{
+    if (fieldname == QLatin1String("dn")) {     // ignore
+        return;
+    }
+
+    if (fieldname.startsWith(QLatin1Char('#'))) {
+        return;
+    }
+
+    if (fieldname.isEmpty() && !a.note().isEmpty()) {
+        // some LDIF export filters are borken and add additional
+        // comments on stand-alone lines. Just add them to the notes for now.
+        a.setNote(a.note() + QLatin1Char('\n') + value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("givenname")) {
+        a.setGivenName(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("xmozillanickname") ||
+            fieldname == QLatin1String("nickname") ||
+            fieldname == QLatin1String("mozillanickname")) {
+        a.setNickName(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("sn")) {
+        a.setFamilyName(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("uid")) {
+        a.setUid(value);
+        return;
+    }
+    if (fieldname == QLatin1String("mail") ||
+            fieldname == QLatin1String("mozillasecondemail") /* mozilla */ ||
+            fieldname == QLatin1String("othermailbox")  /*TheBat!*/)  {
+        if (a.emails().indexOf(value) == -1) {
+            a.insertEmail(value);
+        }
+        return;
+    }
+
+    if (fieldname == QLatin1String("title")) {
+        a.setTitle(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("vocation")) {
+        a.setPrefix(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("cn")) {
+        a.setFormattedName(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("o") ||
+            fieldname == QLatin1String("organization") ||        // Exchange
+            fieldname == QLatin1String("organizationname")) {    // Exchange
+        a.setOrganization(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("description") ||
+            fieldname == QLatin1String("mozillacustom1") ||
+            fieldname == QLatin1String("mozillacustom2") ||
+            fieldname == QLatin1String("mozillacustom3") ||
+            fieldname == QLatin1String("mozillacustom4") ||
+            fieldname == QLatin1String("custom1") ||
+            fieldname == QLatin1String("custom2") ||
+            fieldname == QLatin1String("custom3") ||
+            fieldname == QLatin1String("custom4")) {
+        if (!a.note().isEmpty()) {
+            a.setNote(a.note() + QLatin1Char('\n'));
+        }
+        a.setNote(a.note() + value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("homeurl") ||
+            fieldname == QLatin1String("workurl") ||
+            fieldname == QLatin1String("mozillahomeurl")) {
+        if (a.url().url().isEmpty()) {
+            ResourceLocatorUrl url;
+            url.setUrl(QUrl(value));
+            a.setUrl(url);
+            return;
+        }
+        if (a.url().url().toDisplayString() == QUrl(value).toDisplayString()) {
+            return;
+        }
+        // TODO: current version of kabc only supports one URL.
+        // TODO: change this with KDE 4
+    }
+
+    if (fieldname == QLatin1String("homephone")) {
+        a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Home));
+        return;
+    }
+
+    if (fieldname == QLatin1String("telephonenumber")) {
+        a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Work));
+        return;
+    }
+    if (fieldname == QLatin1String("mobile") ||    /* mozilla/Netscape 7 */
+            fieldname == QLatin1String("cellphone")) {
+        a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Cell));
+        return;
+    }
+
+    if (fieldname == QLatin1String("pager")  ||          // mozilla
+            fieldname == QLatin1String("pagerphone")) {     // mozilla
+        a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Pager));
+        return;
+    }
+
+    if (fieldname == QLatin1String("facsimiletelephonenumber")) {
+        a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Fax));
+        return;
+    }
+
+    if (fieldname == QLatin1String("xmozillaanyphone")) {      // mozilla
+        a.insertPhoneNumber(PhoneNumber(value, PhoneNumber::Work));
+        return;
+    }
+
+    if (fieldname == QLatin1String("streethomeaddress") ||
+            fieldname == QLatin1String("mozillahomestreet")) {     // thunderbird
+        homeAddr.setStreet(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("street") ||
+            fieldname == QLatin1String("postaladdress")) {     // mozilla
+        workAddr.setStreet(value);
+        return;
+    }
+    if (fieldname == QLatin1String("mozillapostaladdress2") ||
+            fieldname == QLatin1String("mozillaworkstreet2")) {     // mozilla
+        workAddr.setStreet(workAddr.street() + QLatin1String("\n") + value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("postalcode")) {
+        workAddr.setPostalCode(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("postofficebox")) {
+        workAddr.setPostOfficeBox(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("homepostaladdress")) {      // Netscape 7
+        homeAddr.setStreet(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("mozillahomepostaladdress2")) {      // mozilla
+        homeAddr.setStreet(homeAddr.street() + QLatin1String("\n") + value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("mozillahomelocalityname")) {      // mozilla
+        homeAddr.setLocality(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("mozillahomestate")) {     // mozilla
+        homeAddr.setRegion(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("mozillahomepostalcode")) {      // mozilla
+        homeAddr.setPostalCode(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("mozillahomecountryname")) {     // mozilla
+        if (value.length() <= 2) {
+            value = Address::ISOtoCountry(value);
+        }
+        homeAddr.setCountry(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("locality")) {
+        workAddr.setLocality(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("streetaddress")) {     // Netscape 4.x
+        workAddr.setStreet(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("countryname") ||
+            fieldname == QLatin1String("c")) {     // mozilla
+        if (value.length() <= 2) {
+            value = Address::ISOtoCountry(value);
+        }
+        workAddr.setCountry(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("l")) {      // mozilla
+        workAddr.setLocality(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("st")) {
+        workAddr.setRegion(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("ou")) {
+        a.setRole(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("department")) {
+        a.setDepartment(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("member")) {
+        // this is a mozilla list member (cn=xxx, mail=yyy)
+        QStringList list = value.split(QLatin1Char(','));
+        QString name, email;
+
+        QStringList::ConstIterator it;
+        QStringList::ConstIterator end(list.constEnd());
+        for (it = list.constBegin(); it != end; ++it) {
+            if ((*it).startsWith(QLatin1String("cn="))) {
+                name = (*it).mid(3).trimmed();
+            }
+            if ((*it).startsWith(QLatin1String("mail="))) {
+                email = (*it).mid(5).trimmed();
+            }
+        }
+        if (!name.isEmpty() && !email.isEmpty()) {
+            email = QLatin1String(" <") + email + QLatin1Char('>');
+        }
+        ContactGroup::Data data;
+        data.setEmail(email);
+        data.setName(name);
+        contactGroup.append(data);
+        return;
+    }
+
+    if (fieldname == QLatin1String("modifytimestamp")) {
+        if (value == QLatin1String("0Z")) {     // ignore
+            return;
+        }
+        QDateTime dt = VCardStringToDate(value);
+        if (dt.isValid()) {
+            a.setRevision(dt);
+            return;
+        }
+    }
+
+    if (fieldname == QLatin1String("display-name")) {
+        contactGroup.setName(value);
+        return;
+    }
+
+    if (fieldname == QLatin1String("objectclass")) {     // ignore
+        return;
+    }
+
+    if (fieldname == QLatin1String("birthyear")) {
+        birthyear = value.toInt();
+        return;
+    }
+    if (fieldname == QLatin1String("birthmonth")) {
+        birthmonth = value.toInt();
+        return;
+    }
+    if (fieldname == QLatin1String("birthday")) {
+        birthday = value.toInt();
+        return;
+    }
+    if (fieldname == QLatin1String("xbatbirthday")) {
+        QDate dt = QDate::fromString(value, QStringLiteral("yyyyMMdd"));
+        if (dt.isValid()) {
+            a.setBirthday(QDateTime(dt));
+        }
+        return;
+    }
+    qCWarning(KCONTACTS_LOG) << QStringLiteral("LDIFConverter: Unknown field for '%1': '%2=%3'\n").
+                             arg(a.formattedName(), fieldname, value);
+
+    return;
+}
diff --git a/src/converter/ldifconverter.h b/src/converter/ldifconverter.h
new file mode 100644 (file)
index 0000000..d6ebd91
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Helge Deller <deller@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License version 2 only as published by the Free Software Foundation.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_LDIFCONVERTER_H
+#define KCONTACTS_LDIFCONVERTER_H
+
+#include "addressee.h"
+#include "contactgroup.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QString>
+
+namespace KContacts
+{
+
+/**
+  A set of functions to convert a string with LDIF information to addressees
+  and vice versa. It is useful for addressbook import- and exportfilters
+  and might be used to read and write Mozilla and Netscape addresssbooks.
+*/
+
+namespace LDIFConverter
+{
+
+/**
+  Converts a LDIF string to a list of addressees.
+
+  @param str         The vcard string.
+  @param addrList    The addresseelist.
+  @param contactGroupList the contactGroupList
+  @param dt          The date & time value of the last modification (e.g. file modification time).
+*/
+KCONTACTS_EXPORT bool LDIFToAddressee(const QString &str, AddresseeList &addrList, QVector<KContacts::ContactGroup> &contactGroupList,
+                                      const QDateTime &dt = QDateTime::currentDateTime());
+
+/**
+  Converts a list of addressees to a LDIF string.
+
+  @param addrList    The addresseelist.
+  @param str         The LDIF string.
+*/
+KCONTACTS_EXPORT bool addresseeToLDIF(const AddresseeList &addrList, QString &str);
+
+/**
+  Converts a list of addressees and contactgrouplist to a LDIF string.
+
+  @param addrList    The addresseelist.
+  @param contactGroupList The contact group list
+  @param str         The LDIF string.
+
+*/
+KCONTACTS_EXPORT bool addresseeAndContactGroupToLDIF(const AddresseeList &addrList, const QVector<KContacts::ContactGroup> &contactGroupList, QString &str);
+/**
+  Converts an addressee to a LDIF string.
+
+  @param addr    The addressee.
+  @param str     The LDIF string.
+*/
+KCONTACTS_EXPORT bool addresseeToLDIF(const Addressee &addr, QString &str);
+
+/**
+  Converts a list of contact group to a LDIF string.
+
+  @param contactGroupList    The contact group list
+  @param str         The LDIF string.
+*/
+KCONTACTS_EXPORT bool contactGroupToLDIF(const ContactGroup::List &contactGroupList, QString &str);
+
+/**
+  Converts a contact group to a LDIF string.
+
+  @param contactGroup    The contact group
+  @param str         The LDIF string.
+*/
+KCONTACTS_EXPORT bool contactGroupToLDIF(const ContactGroup &contactGroup, QString &str);
+
+}
+
+}
+#endif
diff --git a/src/converter/vcardconverter.cpp b/src/converter/vcardconverter.cpp
new file mode 100644 (file)
index 0000000..4353272
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "vcardconverter.h"
+#include "vcardtool.h"
+
+using namespace KContacts;
+
+VCardConverter::VCardConverter()
+    : d(Q_NULLPTR)
+{
+}
+
+VCardConverter::~VCardConverter()
+{
+}
+
+QByteArray VCardConverter::exportVCard(const Addressee &addr, Version version) const
+{
+    Addressee::List list;
+    list.append(addr);
+
+    return exportVCards(list, version);
+}
+
+QByteArray VCardConverter::exportVCards(const Addressee::List &list, Version version) const
+{
+    VCardTool tool;
+    QByteArray returnValue;
+    switch (version) {
+    case v2_1:
+        returnValue = tool.exportVCards(list, VCard::v2_1);
+        break;
+    case v3_0:
+        returnValue = tool.exportVCards(list, VCard::v3_0);
+        break;
+    case v4_0:
+        returnValue = tool.exportVCards(list, VCard::v4_0);
+        break;
+    }
+
+    return returnValue;
+}
+
+QByteArray VCardConverter::createVCard(const Addressee &addr, Version version) const
+{
+    Addressee::List list;
+    list.append(addr);
+
+    return createVCards(list, version);
+}
+
+QByteArray VCardConverter::createVCards(const Addressee::List &list, Version version) const
+{
+    VCardTool tool;
+    QByteArray returnValue;
+    switch (version) {
+    case v2_1:
+        returnValue = tool.createVCards(list, VCard::v2_1);
+        break;
+    case v3_0:
+        returnValue = tool.createVCards(list, VCard::v3_0);
+        break;
+    case v4_0:
+        returnValue = tool.createVCards(list, VCard::v4_0);
+        break;
+    }
+
+    return returnValue;
+}
+
+Addressee VCardConverter::parseVCard(const QByteArray &vcard) const
+{
+    Addressee::List list = parseVCards(vcard);
+
+    return list.isEmpty() ? Addressee() : list[ 0 ];
+}
+
+Addressee::List VCardConverter::parseVCards(const QByteArray &vcard) const
+{
+    VCardTool tool;
+
+    return tool.parseVCards(vcard);
+}
+
+/* Helper functions */
+
+QString KContacts::dateToVCardString(const QDateTime &dateTime)
+{
+    return dateTime.toString(QStringLiteral("yyyyMMddThhmmssZ"));
+}
+
+QString KContacts::dateToVCardString(QDate date)
+{
+    return date.toString(QStringLiteral("yyyyMMdd"));
+}
+
+QDateTime KContacts::VCardStringToDate(const QString &dateString)
+{
+    QDate date;
+    QTime time;
+    QString d(dateString);
+
+    d = d.remove(QLatin1Char('-')).remove(QLatin1Char(':'));
+
+    if (d.length() >= 8) {
+        date = QDate(d.midRef(0, 4).toUInt(), d.midRef(4, 2).toUInt(), d.midRef(6, 2).toUInt());
+    }
+
+    if (d.length() > 9 && d[ 8 ].toUpper() == QLatin1Char('T')) {
+        time = QTime(d.midRef(9, 2).toUInt(), d.midRef(11, 2).toUInt(), d.midRef(13, 2).toUInt());
+    }
+
+    return QDateTime(date, time);
+}
+
+void KContacts::adaptIMAttributes(QByteArray &data)
+{
+    data.replace("X-messaging/aim-All", ("X-AIM"));
+    data.replace("X-messaging/icq-All", ("X-ICQ"));
+    data.replace("X-messaging/xmpp-All", ("X-JABBER"));
+    data.replace("X-messaging/msn-All", ("X-MSN"));
+    data.replace("X-messaging/yahoo-All", ("X-YAHOO"));
+    data.replace("X-messaging/gadu-All", ("X-GADUGADU"));
+    data.replace("X-messaging/skype-All", ("X-SKYPE"));
+    data.replace("X-messaging/groupwise-All", ("X-GROUPWISE"));
+    data.replace("X-messaging/sms-All", ("X-SMS"));
+    data.replace("X-messaging/meanwhile-All", ("X-MEANWHILE"));
+    data.replace("X-messaging/irc-All", ("X-IRC"));
+    data.replace("X-messaging/googletalk-All", ("X-GTALK"));
+}
diff --git a/src/converter/vcardconverter.h b/src/converter/vcardconverter.h
new file mode 100644 (file)
index 0000000..46d3dba
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_VCARDCONVERTER_H
+#define KCONTACTS_VCARDCONVERTER_H
+
+#include "kcontacts_export.h"
+#include "kcontacts/addressee.h"
+#include <QtCore/QString>
+
+namespace KContacts
+{
+
+/**
+  @short Class to converting contact objects into vCard format and vice versa.
+
+  This class implements reading and writing of contact using from/to the
+  vCard format. Currently vCard version 2.1 and 3.0 is supported.
+
+  Example:
+
+  \code
+
+  QFile file( "myfile.vcf" );
+  file.open( QIODevice::ReadOnly );
+
+  QByteArray data = file.readAll();
+
+  VCardConverter converter;
+  Addressee::List list = converter.parseVCards( data );
+
+  // print formatted name of first contact
+  qDebug( "name=%s", list[ 0 ].formattedName().toLatin1() );
+
+  \endcode
+*/
+class KCONTACTS_EXPORT VCardConverter
+{
+public:
+
+    /**
+      @li v2_1 - VCard format version 2.1
+      @li v3_0 - VCard format version 3.0
+      @li v4_0 - VCard format version 4.0
+     */
+    enum Version {
+        v2_1,
+        v3_0,
+        v4_0
+    };
+
+    /**
+      Constructor.
+     */
+    VCardConverter();
+
+    /**
+      Destructor.
+     */
+    ~VCardConverter();
+
+    /**
+      Creates a string in vCard format which contains the given
+      contact.
+
+      @param addr The contact object
+      @param version The version of the generated vCard format
+     */
+    QByteArray createVCard(const Addressee &addr, Version version = v3_0) const;
+
+    /**
+      Creates a string in vCard format which contains the given
+      list of contact.
+
+      @param list The list of contact objects
+      @param version The version of the generated vCard format
+     */
+    // FIXME: Add error handling
+    QByteArray createVCards(const Addressee::List &list, Version version = v3_0) const;
+
+    /**
+     * @since 4.9.1
+     */
+    QByteArray exportVCard(const Addressee &addr, Version version) const;
+
+    /**
+     * @since 4.9.1
+     */
+    QByteArray exportVCards(const Addressee::List &list, Version version) const;
+
+    /**
+      Parses a string in vCard format and returns the first contact.
+     */
+    Addressee parseVCard(const QByteArray &vcard) const;
+
+    /**
+      Parses a string in vCard format and returns a list of contact objects.
+     */
+    // FIXME: Add error handling
+    Addressee::List parseVCards(const QByteArray &vcard) const;
+
+private:
+    Q_DISABLE_COPY(VCardConverter)
+    class VCardConverterPrivate;
+    VCardConverterPrivate *const d;
+};
+
+/**
+    Helper functions
+  */
+
+/**
+  * Converts a QDateTime to a date string as it is used in VCard and LDIF files.
+  * The return value is in the form "yyyyMMddThhmmssZ" (e.g. "20031201T120000Z")
+  * @param dateTime date and time to be converted
+  */
+KCONTACTS_EXPORT QString dateToVCardString(const QDateTime &dateTime);
+
+/**
+  * Converts a QDate to a short date string as it is used in VCard and LDIF files.
+  * The return value is in the form "yyyyMMdd" (e.g. "20031201")
+  * @param date date to be converted
+  */
+KCONTACTS_EXPORT QString dateToVCardString(QDate date);
+
+/**
+  * Converts a date string as it is used in VCard and LDIF files to a QDateTime value.
+  * If the date string does not contain a time value, it will be returned as 00:00:00.
+  * (e.g. "20031201T120000" will return a QDateTime for 2003-12-01 at 12:00)
+  * @param dateString string representing the date and time.
+  */
+KCONTACTS_EXPORT QDateTime VCardStringToDate(const QString &dateString);
+
+/**
+ * @brief adaptIMAttributes. Convert KAddressBook attribute to VCard IM Attribute
+ * @param data
+ */
+KCONTACTS_EXPORT void adaptIMAttributes(QByteArray &data);
+
+}
+#endif
diff --git a/src/countrytransl.map b/src/countrytransl.map
new file mode 100644 (file)
index 0000000..2810d36
--- /dev/null
@@ -0,0 +1,5685 @@
+Andorra        ad
+Андора   ad
+Андора   ad
+Andora ad
+ອີນເດີຍ  ad
+Andora ad
+Андорра ad
+安道尔      ad
+United Arab Emirates   ae
+Vereenigde Arabiese Emirate    ae
+الإمارات العربية المتحدة ae
+Злучаныя Арабскія Эміраты       ae
+Обединени Арабски Емирства     ae
+Ujedinjeni arapski emirati     ae
+Emirats Àrabs Units   ae
+Spojené arabské emiráty     ae
+Forenende Arabiske Emirater    ae
+Vereinigte Arabische Emirate   ae
+Ενωμένα Αραβικά Εμιράτα   ae
+Emiratos árabes unidos        ae
+Araabia Ühendemiraadid        ae
+Arabiar Emirato Batuak ae
+امارات متحده عربی       ae
+Yhdistyneet Arabiemiraatit     ae
+Émirats Arabes Unis   ae
+איחוד האמירויות הערביות   ae
+Ujedinjeni arapski emirati     ae
+Egyesült Arab Emirátusok     ae
+Emirati arabi uniti    ae
+アラブ首長国連邦       ae
+아랍 에미레이트 연합  ae
+ສະຫະລັດ  ae
+Jungtiniai Arabų Emiratai     ae
+Emirati Għarab Magħquda      ae
+De forente arabiske emirater   ae
+Verenigde Arabische Emiraten   ae
+Dei sameinte arabiske emirata  ae
+Di-Emirate tseo di Kopanego tsa Arab   ae
+Zjednoczone Emiraty Arabskie   ae
+Emiratos Árabes Unidos        ae
+Emirados Árabes       ae
+Emiratele Arabe Unite  ae
+Объединенные Арабские Эмираты       ae
+Spojené arabské emiráty     ae
+Združeni arabski Emirati      ae
+Förenade arabemiraten ae
+³ì¸¢Â «ÃÒ ¿¡Î¸û   ae
+Birleşik Arap Emirlikleri     ae
+Об'єднані Арабські Емірати      ae
+Mashango o tangananaho a Emirates      ae
+阿拉伯联合酋长国       ae
+阿拉伯聯合大公國       ae
+Izindawo zezinduna zase-United Arab    ae
+Afghanistan    af
+Афганістан   af
+Афганистан   af
+Afganistan     af
+Afghanistán   af
+Afganisztán   af
+ລີທົ່ວເນີຍ af
+Afeganistão   af
+Afganistan     af
+Afganistan     af
+Afganistan     af
+Афганістан   af
+阿富汗      af
+Antigua and Barbuda    ag
+Antigue en Barbuda     ag
+أنتيغوا و باربودا       ag
+Antigua və Barbuda    ag
+Антыгуа і Барбуда       ag
+Антигуа и Барбадос     ag
+Antigua ha Barbuda     ag
+Antigua i Barbuda      ag
+Antigua i Barbuda      ag
+Antigua a Barbuda      ag
+Antigua og Barbuda     ag
+Antigua und Barbuda    ag
+Antigua και Barbuda ag
+Antigvo kaj Barbudo    ag
+Antigua y Barbuda      ag
+Antigua ja Barbuda     ag
+Antigua eta Barbuda    ag
+آنتیگوا و باربودا       ag
+Antigua ja Barbados    ag
+Antigua og Barbuda     ag
+Antigua et Barbuda     ag
+Antiga e Barbuda       ag
+אנטיגואה וברבודה        ag
+Antigua i Barbuda      ag
+Antigua és Barbuda    ag
+Antigua dan Barbuda    ag
+Antigúa og Barbúda       ag
+Antigua e Barbuda      ag
+アンティグアバーブーダ      ag
+앤티가 바부다    ag
+Antikva ir Barbuda     ag
+Antigva un Barbudas    ag
+Antigwa u Barbuda      ag
+Antigua og Barbuda     ag
+Antigua en Barbuda     ag
+Antigua og Barbuda     ag
+Antigua le Barbuda     ag
+Antigua e Barbuda      ag
+Antigua i Barbuda      ag
+Antigua e Barbuda      ag
+Antigua e Barbuda      ag
+Antigua şi Barbuda    ag
+Антигуа и Барбадос     ag
+Antigua a Barbuda      ag
+Antigva in Barbuda     ag
+Antigua i Barbuda      ag
+I-Antigua kanye ne Barbuda     ag
+Antigua och Barbuda    ag
+¬ýÊÌÅ¡ & À¡÷Ò¼¡    ag
+Antigua ve Barbuda     ag
+Трінідад та Тобаго     ag
+Antigua và Barbuda    ag
+Antigua eyet Barbuda   ag
+Antigua ne Barbuda     ag
+安地瓜岛和巴布达岛    ag
+安地瓜島和巴布達島    ag
+Antigua kanye ne-Barbuda       ag
+Anguilla       ai
+Ангуила ai
+ແພນວິນ     ai
+Angvila        ai
+Ангілья ai
+安圭拉      ai
+Albania        al
+Альбанія       al
+Албания al
+Albanija       al
+Albanien       al
+Albánia       al
+ແອດແລນຕິກ    al
+Albânia       al
+Albánsko      al
+Albanija       al
+Albanien       al
+Албанія al
+阿尔巴尼亚        al
+Armenia        am
+Армэнія am
+Армения am
+Armenija       am
+Armenien       am
+Örményország        am
+ອາເຈນຕິນາ    am
+Armênia       am
+Arménsko      am
+Armenija       am
+Armenien       am
+Арменія am
+亚美尼亚   am
+Netherlands Antilles   an
+Холандски Антили        an
+Nizozemski Antili      an
+Nederlandske antiller  an
+Antillas holandesas    an
+Antilla Holandarrak    an
+Holland-Antillák      an
+ເນເທີແລນ       an
+Antilhas       an
+Holandské Antily      an
+Nizozemski Antili      an
+Nederländska Antillerna       an
+Голландські Антилли  an
+荷属安的列斯群岛       an
+Angola ao
+Ангола   ao
+Ангола   ao
+ບັນແກເລີຍ    ao
+Ангола   ao
+安哥拉      ao
+Argentina      ar
+Argentinië    ar
+الأرجنتين     ar
+Аргентына     ar
+Аржентина     ar
+Arc'hantina    ar
+Argentinien    ar
+Αργεντινή     ar
+Argentino      ar
+Argentiina     ar
+آرژانتین       ar
+Agentiina      ar
+Argentine      ar
+Arxentina      ar
+ארגנטינה       ar
+Argentína     ar
+Argentína   ar
+アルゼンチン     ar
+아르헨티나        ar
+ອາເຈນຕິນາ    ar
+Argentīna     ar
+Arġentina     ar
+Argentinië    ar
+Argentyna      ar
+Аргентина     ar
+Argentína     ar
+I-Argentina    ar
+¬÷¦º\83ýÊÉ¡     ar
+อาร์เจนตินา      ar
+Arjantin       ar
+Аргентина     ar
+Agenthina      ar
+Årdjintene    ar
+阿根廷      ar
+阿根廷      ar
+American Samoa as
+Амэрыканскае Самоа    as
+Американска Самоа      as
+Američka Samoa        as
+Samoa (USA)    as
+Samoa americana        as
+Amerikako Samoa        as
+Amerikai Szamoa        as
+ອາເມລິກາເຫນືອ        as
+Samoa Americana        as
+Americká Samoa        as
+Ameriška Samoa        as
+Amerikanska Samoa      as
+Американське Самоа    as
+美属萨摩亚群岛  as
+Austria        at
+Oostenryk      at
+النمسا   at
+Avstriya       at
+Аўстрыя at
+Австрия at
+Aostria        at
+Austrija       at
+Àustria       at
+Rakousko       at
+Østrig        at
+Österreich    at
+Αυστρία at
+Aŭstrio       at
+اتریش     at
+Itävalta      at
+Eysturríki    at
+Autriche       at
+אוסטריה at
+Austrija       at
+Ausztria       at
+Austurríki  at
+オーストリア     at
+오스트리아        at
+ອອດສະເຕເລີຍ      at
+Austrija       at
+Austrija       at
+Awtrija        at
+Østerrike     at
+Oostenrijk     at
+Austerrike     at
+Áustria       at
+Áustria       at
+Австрия at
+Rakúsko       at
+Avstrija       at
+Austrija       at
+I-Austria      at
+Österrike     at
+¬Íò¾¢Ã¢Â¡     at
+ออสเตรีย       at
+Avusturya      at
+Австрія at
+Ositiria       at
+A'o    at
+Ôtriche       at
+奥地利      at
+奧地利      at
+Australia      au
+Australië     au
+أستراليا       au
+Avustralya     au
+Аўстралія     au
+Австралия     au
+Aostralia      au
+Australija     au
+Austràlia     au
+Austrálie     au
+Australien     au
+Australien     au
+Αυστραλία     au
+Aŭstralio     au
+Austraalia     au
+استرالیا       au
+Australie      au
+אוסטרליה       au
+Australija     au
+Ausztrália    au
\81stralía au
+オーストラリア  au
+오스트레일리아  au
+ອອດສະເຕເລີຍ      au
+Australija     au
+Austrālija    au
+Awstralja      au
+Australië     au
+Austrália     au
+Austrália     au
+Австралия     au
+Austrália     au
+Avstralija     au
+Australija     au
+I-Australia    au
+Australien     au
\8aÍò¾¢§ÃĢ¡       au
+ออสเตรเลีย au
+Avusturalya    au
+Австралія     au
+Ositiralia     au
+U'c    au
+Ôstraleye     au
+澳大利亚   au
+澳大利亞   au
+Aruba  aw
+Аруба     aw
+ເກມໄັພ່  aw
+Аруба     aw
+阿鲁巴岛   aw
+Azerbaijan     az
+أذربيجان       az
+Azərbaycan    az
+Азэрбайджан az
+Азарбайджан az
+Azerbejdžan   az
+Azerbaitjan    az
+Ázerbajdžánský     az
+Azerbajdjan    az
+Aserbaidschan  az
+Αζερμπαϊτζάν       az
+Azerbajĝana   az
+Azerbaiján    az
+Aserbaidžaan  az
+آذربایجان     az
+Azerbaidzan    az
+Aserbadsjan    az
+אזרביג'ן        az
+Azerbejdžan   az
+Azerbajdzsán  az
+Azerbaigian    az
+アゼルバイジャン       az
+아제르바이잔     az
+ອາເຊີໄບຈັນ az
+Azerbaidžanas az
+Azerbaidžāņu        az
+Ażerbajġan   az
+Aserbajdsjan   az
+Azerbeidjan    az
+Aserbajdsjan   az
+Azerbejdżan   az
+Azerbaijão    az
+Turco  az
+Azerbadjan     az
+Азербайджан az
+Ázerbajdžánsky      az
+Azerbajdžan   az
+Azerbejdžan   az
+I-Azerbaijan   az
+«º÷¨Àº¡ý       az
+อาร์เซอร์ไบจัน     az
+Azerice        az
+Азербайджан az
+Azerbaydjan    az
+阿塞拜疆   az
+亞塞拜然   az
+Bosnia and Herzegovina ba
+Bosnië en Herzegovina ba
+البوسنا و الهرسك ba
+Босьнія і Герцагавіна       ba
+Боснена и Херцеговина       ba
+Bosna i Hercegovina    ba
+Bòsnia i Hercegovina  ba
+Bosna a Herzegovina    ba
+Bosnien-Herzegovina    ba
+Bosnien und Herzegowina        ba
+Βοσνία και Ερζεγοβίνη       ba
+Bosnio kaj Hercegovino ba
+Bosnia y Herzegovina   ba
+Bosnia ja Hertsegovina ba
+Bosnia eta Herzegovina ba
+بوسنی و هرزگوین   ba
+Bosnia ja Herzegovina  ba
+Bosnia-Herzegovina     ba
+Bosnie herzégovine    ba
+בוסניה הרצגובינה        ba
+Bosna i Hercegovina    ba
+Bosznia-Hercegovina    ba
+Bosnia e Erzegovina    ba
+ボスニアヘルツェゴビナ      ba
+보스니아어와 헤르체고비나  ba
+ບອສເນີຍ ແລະ ເຫີເຊີໂກວິນາ   ba
+Bosnija ir Hercegovina ba
+Bosnija un Hercogovina ba
+Bożnia u Ħerżegovina        ba
+Bosnia-Hercegovina     ba
+Bosnië en Herzegovina ba
+Bosnia-Hercegovina     ba
+Bosnia le Herzegovina  ba
+Bośnia i Hercegowina  ba
+Bósnia e Herzegovina  ba
+Bósnia Herzegóvina   ba
+Bosnia şi Herţegovina        ba
+Босния и Герцеговина ba
+Bosna a Hercegovina    ba
+Bosna in Hercegovina   ba
+I-Bosnia kanye ne Herzegovina  ba
+Bosnien och Herzegovina        ba
+¦À¡Íɢ¡       ba
+บอสเนีย และ เฮอร์เซโกวินา        ba
+Bosna Hersek   ba
+Боснія та Герцеговина       ba
+Mubosinia na Muhezegovina      ba
+Bosneye        ba
+Bosnia ne Herzegovina  ba
+波斯尼亚和黑塞哥维那 ba
+波士尼亞與赫塞哥維納 ba
+Bosnia kanye ne-Herzegovina    ba
+Barbados       bb
+بربادوس bb
+Барбадос       bb
+Барбадос       bb
+Μπαρμπάντος bb
+Babadoso       bb
+باربادوس       bb
+Barbade        bb
+ברבדוס   bb
+バルバドス        bb
+바르바도스        bb
+ບາລບາດອດສ    bb
+Barbadosas     bb
+Barbadosa      bb
+Барбадос       bb
+I-Barbados     bb
+À¡÷§À¼¡Í       bb
+บาร์บาดอส    bb
+Барбадос       bb
+Barbades       bb
+巴巴多斯   bb
+巴貝多      bb
+Bangladesh     bd
+بنغلاديش       bd
+Banqladeş     bd
+Банглядэш     bd
+Бангладеш     bd
+Bangladeš     bd
+Bangladéš    bd
+Bangladesch    bd
+Μπαγκλαντές bd
+Bangladeŝo    bd
+بنگلادش bd
+בנגלדש   bd
+Bangladeš     bd
+Banglades      bd
+バングラデシュ  bd
+방글라데시        bd
+ບັງຄະລາເທດ bd
+Bangladešas   bd
+Bangladeša    bd
+Bangladexx     bd
+Bangladesz     bd
+Banglade       bd
+Бангладеш     bd
+Bangladéš    bd
+Bangladeš     bd
+I-Bangladesh   bd
+Àí¸Ç¡§¾Í       bd
+บังคลาเทศ    bd
+Bangladeş     bd
+Бангладеш     bd
+孟加拉      bd
+孟加拉      bd
+Belgium        be
+België        be
+بلجيكا   be
+Belçika       be
+Бэльгія be
+Белгия   be
+Belgia be
+Belgija        be
+Bèlgica       be
+Belgie be
+Belgien        be
+Belgien        be
+Βέλγιο   be
+Belgio be
+Bélgica       be
+Belgia be
+Belgika        be
+بلژیک     be
+Belgia be
+Belgia be
+Belgique       be
+Bélxica       be
+בלגיה     be
+Belgija        be
+Belgia be
+Belgía      be
+Belgio be
+ベルギー   be
+벨기에      be
+ເບລຢ່ງມ  be
+Belgija        be
+Beļģija      be
+Belġju        be
+Belgia be
+België        be
+Belgia be
+Bèlgica       be
+Belgia be
+Bélgica       be
+Bélgica       be
+Belgia be
+Бельгия be
+Belgicko       be
+Belgija        be
+Belgija        be
+I-Belgium      be
+Belgien        be
+¦Àøº¢Âõ be
+เบลเยียม       be
+Belçika       be
+Бельгія be
+Beljike        be
+比利时      be
+比利時      be
+Burkina Faso   bf
+Буркіна Фасо        bf
+Буркина Фаско      bf
+ຕຸລະກີ     bf
+Буркіна Фасо        bf
+布基纳法索        bf
+Bulgaria       bg
+Bulgarye       bg
+بلغاريا bg
+Bolgarıstan   bg
+Баўгарыя       bg
+България       bg
+Bugarska       bg
+Bulgària      bg
+Bulharsko      bg
+Bulgarien      bg
+Bulgarien      bg
+Βουλγαρία     bg
+Bulgario       bg
+Bulgaaria      bg
+بلغارستان     bg
+Bulgarie       bg
+בולגריה bg
+Bugarska       bg
+Bulgária      bg
+Búlgaría bg
+ブルガリア        bg
+불가리아   bg
+ບັນແກເລີຍ    bg
+Bulgarija      bg
+Bulgārija     bg
+Bulgarija      bg
+Bulgarije      bg
+Bułgaria      bg
+Bulgária      bg
+Bulgária      bg
+Болгария       bg
+Bulharsko      bg
+Bolgarija      bg
+Bugarska       bg
+I-Bulgaria     bg
+Bulgarien      bg
+Àø§¸Ã¢Â¡       bg
+บัลแกเรีย    bg
+Bulgaristan    bg
+Болгарія       bg
+Baligaria      bg
+Bulgåreye     bg
+保加利亚   bg
+保加利亞   bg
+Bahrain        bh
+البحرين bh
+Бахрэйн bh
+Бахрейн bh
+Bahrein        bh
+Bahrajn        bh
+Μπαχρέιν       bh
+Bahrein        bh
+Bahrein        bh
+بحرین     bh
+בחריין   bh
+バーレーン        bh
+바레인      bh
+ຖັກກ່ງວ  bh
+Bahreinas      bh
+Baħrain       bh
+Baghrein       bh
+Bahrajn        bh
+Bahamas        bh
+Bahrein        bh
+Бахрейн bh
+Bahrajn        bh
+Bahrajn        bh
+I-Bahrain      bh
+Bahrein        bh
+À‹¨Ãý    bh
+Bahreyn        bh
+Бахрейн bh
+巴林 bh
+巴林 bh
+Burundi        bi
+Бурундзі       bi
+Бурунди bi
+ເຄອຣດ        bi
+Бурунді bi
+布隆迪      bi
+Benin  bj
+Бэнін     bj
+Бенин     bj
+ບອສເນີຍ  bj
+Бенін     bj
+贝宁 bj
+Bermuda        bm
+Бэрмуды bm
+Бермуда bm
+ເຍລລະມັນ       bm
+Bermudy        bm
+Bermudi        bm
+Бермуди bm
+百慕大      bm
+Brunei Darussalam      bn
+Брунэй   bn
+Бруней   bn
+Brunei bn
+Brunei Szultánság    bn
+ເບລາລັສເຊີຍ      bn
+Brunei bn
+Brunei Darusalam       bn
+Бруней Даруссалам      bn
+Bolivia        bo
+Bolivië       bo
+بوليفيا bo
+Boliviya       bo
+Балівія bo
+Боливия bo
+Bolivija       bo
+Bolívia       bo
+Bolívie       bo
+Bolivien       bo
+Βολιβία bo
+Bolivio        bo
+Boliivia       bo
+بولیوی   bo
+Bolivie        bo
+בוליביה bo
+Bolivija       bo
+Bolívia       bo
+Bólivía  bo
+ボリビア   bo
+볼리비아   bo
+ໂບລີເວີຍ       bo
+Bolivija       bo
+Bolīvija      bo
+Bolivja        bo
+Boliwia        bo
+Bolívia       bo
+Bolívia       bo
+Боливия bo
+Bolívia       bo
+Bolivija       bo
+Bolivija       bo
+I-Bolivia      bo
+¦À¡Ä¢Å¢Â¡     bo
+โบลิเวีย       bo
+Bolivya        bo
+Болівія bo
+Boliveye       bo
+波利维亚   bo
+玻利維亞   bo
+Brazil br
+Brazilië      br
+البرازيل       br
+Braziliya      br
+Бразылія       br
+Бразилия       br
+Brasil br
+Brazílie      br
+Brasilien      br
+Brasilien      br
+Βραζιλία       br
+Brazilo        br
+Brasil br
+Brasiilia      br
+Brasil br
+برزیل     br
+Brasilia       br
+Brésil        br
+Brasil br
+ברזיל     br
+Brazília      br
+Brasilía    br
+Brasile        br
+ブラジル   br
+브라질      br
+ບາຊີລ        br
+Brazilija      br
+Brazīlija     br
+Brażil        br
+Brasil br
+Brazilië      br
+Brasil br
+Brasil br
+Brazylia       br
+Brasil br
+Brasil br
+Brazilia       br
+Бразилия       br
+Brazília      br
+Brazilija      br
+I-Brazil       br
+Brasilien      br
+À¢§Ãº¢ø br
+บราซิล     br
+Brezilya       br
+Бразилія       br
+Burazili       br
+Braezi br
+巴西 br
+巴西 br
+Bahamas        bs
+Багамы   bs
+Бахами   bs
+Bahami bs
+Bahamák       bs
+ປານາມາ     bs
+Bahamy bs
+Bahami bs
+Багами   bs
+巴哈马      bs
+Bhutan bt
+Бутан     bt
+Бутан     bt
+Butan  bt
+Bhután        bt
+Bhután        bt
+ຖັກກ່ງວ  bt
+Butan  bt
+Бутан     bt
+不丹 bt
+Botswana       bw
+Батсвана       bw
+Боцвана bw
+Bocvana        bw
+ບອດສເນີຍ       bw
+Botsvana       bw
+Ботсвана       bw
+博茨瓦纳   bw
+Belarus        by
+روسيا البيضاء      by
+Беларусь       by
+Беларус by
+Bjelorusija    by
+Bělorusko     by
+Hviderusland   by
+Weißrussland  by
+Bjelorusio     by
+Valgevene      by
+بلاروس   by
+Valkovenäjä  by
+Hvítarusland  by
+Bélarus       by
+בלרוס     by
+Bjelorusija    by
+Fehéroroszország     by
+Hvíta-Rússland   by
+Bielorussia    by
+ベラルーシ        by
+벨라루스   by
+ເບລາລັສ  by
+Baltarusija    by
+Baltkrievu     by
+Hviterussland  by
+Wit-Rusland    by
+Kviterussland  by
+Białoruś     by
+Bielorússia   by
+Bielorusso     by
+Беларусь       by
+Bielorusko     by
+Belorusija     by
+I-Belarus      by
+Vitryssland    by
+¦ÀÄ¡åÍ   by
+เบลารัส  by
+Білорусія     by
+Belaruss       by
+白俄罗斯   by
+白俄羅斯   by
+Belize bz
+Бэлізе   bz
+Белиз     bz
+ເບລຍ່ງມ  bz
+Беліз     bz
+伯利兹      bz
+Default        C
+Verstek        C
+افتراضي C
+Əsas  C
+Па ўмаўчаньні      C
+По подразбиране  C
+Dre ziouer     C
+Omissió       C
+Výchozí      C
+Standard       C
+Standard       C
+Προκαθορισμένο   C
+Apriora        C
+Predeterminado C
+Vaikimisi      C
+Aurremugatua   C
+پیش‌فرض        C
+Oletus C
+Forsettur      C
+Par défaut    C
+Por Omisión   C
+ברירת מחדל    C
+Uobičajeno    C
+Alapértelmezett       C
+Standar        C
+Sjálfgefið       C
+Predefinito    C
+標準 C
+기본 C
+ຄ່າປະລິຍາຍ C
+Nutylima       C
+Noklusētais   C
+Normali        C
+Standard       C
+Standaard      C
+Standard       C
+Thuso ya Tshoganetso   C
+Omission       C
+Domyślnie     C
+Por Omissão   C
+Padrão        C
+Implicit       C
+По умолчанию        C
+Štandardný   C
+Privzeto       C
+Predefinisano  C
+Förval        C
+¦¸¡¼¡¿¢¨Ä     C
+ค่าปริยาย    C
+Öntanımlı   C
+Типовий C
+Mặc định  C
+Prémetou      C
+Okwendalo      C
+默认 C
+預設 C
+Okwendalo      C
+Canada ca
+Kanada ca
+كندا       ca
+Kanada ca
+Канада   ca
+Канада   ca
+Kanada ca
+Kanada ca
+Canadà        ca
+Kanada ca
+Kanada ca
+Καναδάς ca
+Kanado ca
+Canadá        ca
+Kanada ca
+Kanada ca
+کانادا   ca
+Kanada ca
+Kanada ca
+Canadá        ca
+קנדה       ca
+Kanada ca
+Kanada ca
+Kanada ca
+Kanada ca
+カナダ      ca
+캐나다      ca
+ແຄນາດາ     ca
+Kanada ca
+Kanāda        ca
+Kanada ca
+Kanada ca
+Canadá        ca
+Canadá        ca
+Канада   ca
+Kanada ca
+Kanada ca
+Kanada ca
+I-Canada       ca
+Kanada ca
+¸É¼¡       ca
+แคนาดา     ca
+Kanada ca
+Канада   ca
+加拿大      ca
+加拿大      ca
+Cocos (Keeling) Islands        cc
+Кокосови Острови        cc
+Kokosovo (Keeling) ostrvo      cc
+Islas Cocos (Keeling)  cc
+Koko Irlak     cc
+Kókusz-szigetek (Keeling)     cc
+Ilhas Cocos    cc
+Kokosove Ostrovy       cc
+Kokosovi (Keelingovi) otoki    cc
+Kokosöarna    cc
+Кокосові острови        cc
+Congo, the democratic republic of the  cd
+Дэмакратычная Рэспубліка Конга     cd
+Конго     cd
+Kongo, demokratska republika   cd
+Congo, den demokratiske republik       cd
+Congo, república democrática del     cd
+Kongo, errepublika demokratikoa        cd
+Kongói Demokratikus Köztársaság    cd
+República Democrática do Congo       cd
+Demokratická Republika Kongo  cd
+Kongo, demokratična republika cd
+Demokratiska republiken Kongo  cd
+刚果民主共和国  cd
+Central African Republic       cf
+ЦАР cf
+Centralnoafrička Republika    cf
+Central-afrikanske Republik    cf
+República Centroafricana      cf
+Afrika Erdiko Errepublika      cf
+Közép-Afrikai Köztársaság cf
+ໂດມິນິກັນ    cf
+República da África Central  cf
+Stredoafrická Republika       cf
+Centralnoafriška republika    cf
+Centralafrikanska Republiken   cf
+Центральноафриканська республіка        cf
+中非共和国        cf
+Congo  cg
+Конга     cg
+Конго     cg
+Kongo  cg
+Kongo  cg
+Kongó cg
+ຄອນໂໍຊລ  cg
+Kongo  cg
+Kongo  cg
+Kongo  cg
+Конго     cg
+刚果 cg
+Switzerland    ch
+Switserland    ch
+سويسرا   ch
+İsveçrə     ch
+Швэйцарыя     ch
+Швейцария     ch
+Suis   ch
+Švicarska     ch
+Suïssa        ch
+Švýcarsko    ch
+Schweiz        ch
+Schweiz        ch
+Ελβετία ch
+Svislando      ch
+Suiza  ch
+Šveits        ch
+Suitza ch
+سوییس     ch
+Sveitsi        ch
+Suisse ch
+Suíza ch
+שוייץ     ch
+Švicarska     ch
+Svájc ch
+Swiss  ch
+Sviss  ch
+Svizzera       ch
+スイス      ch
+스위스      ch
+ສະວິສເຊີແລນ      ch
+Šveicarija    ch
+Šveice        ch
+Svizzera       ch
+Sveits ch
+Zwitserland    ch
+Sveits ch
+Suissa ch
+Szwajcaria     ch
+Suíça        ch
+Suíça        ch
+Elveţia       ch
+Швейцария     ch
+Švajčiarsko  ch
+Švica ch
+Švajcarska    ch
+I-Switzerland  ch
+Schweiz        ch
+ÍÅ¢ðº÷Ä¡óР  ch
+สวิสเซอร์แลนด์     ch
+İsviçre      ch
+Швейцарія     ch
+Thuỵ Sĩ     ch
+Swisse ch
+瑞士 ch
+瑞士 ch
+Cote d'ivoire  ci
+Бераг Слановай Косьці       ci
+Кот'Дивоар    ci
+Obala Slonovače       ci
+Elfenbenskysten        ci
+Costa de Marfil        ci
+Elefántcsontpart      ci
+ປ່ອຍຫມາກກະລອກ        ci
+Slonokoščena obala   ci
+Elfenbenskusten        ci
+Кот д'Івуар   ci
+Cook islands   ck
+Kukova ostrva  ck
+Cook-øerne    ck
+Islas Cook     ck
+Cook Irlak     ck
+Cook-szigetek  ck
+ຄຸກກີ້     ck
+Ilhas Cook     ck
+Cookove ostrovy        ck
+Cookovi otoki  ck
+Cooköarna     ck
+Острови Кука        ck
+库克群岛   ck
+Chile  cl
+Chilië        cl
+تشيلي     cl
+Şili  cl
+Чылі       cl
+Чили       cl
+Čile  cl
+Xile   cl
+Χιλή       cl
+Ĉilio cl
+Tšiili        cl
+Txile  cl
+شیلی       cl
+Chili  cl
+צ'ילה      cl
+Čile  cl
+Chili  cl
+Cile   cl
+チリ cl
+칠레 cl
+ຊີລີ   cl
+Čilė cl
+Čīle cl
+Ċile  cl
+Chili  cl
+Cile   cl
+Чили       cl
+Čile  cl
+Čile  cl
+Čile  cl
+I-Chile        cl
+º¢Ä¢       cl
+ชิลี   cl
+Şili  cl
+Чилі       cl
+Chi lê        cl
+Tchili cl
+智利 cl
+智利 cl
+Cameroon       cm
+Камэрун cm
+Камерун cm
+Kamerun        cm
+Cameroun       cm
+Camerún       cm
+Kamerun        cm
+Kamerun        cm
+ຕາລາງງານ - K   cm
+Camarões      cm
+Komerun        cm
+Kamerun        cm
+Kamerun        cm
+Камерун cm
+喀麦隆      cm
+China  cn
+Sjina  cn
+الصين     cn
+Çin   cn
+Кітай     cn
+Китай     cn
+Sina   cn
+Kina   cn
+Xina   cn
+Čína cn
+Kina   cn
+Κίνα       cn
+Ĉinujo        cn
+Hiina  cn
+Txina  cn
+چین cn
+Kiina  cn
+Kina   cn
+Chine  cn
+סין cn
+Kina   cn
+Kína  cn
+Cina   cn
+Kína        cn
+Cina   cn
+中国 cn
+중국 cn
+ຈີນ      cn
+Kinija cn
+Ķīna cn
+Ċina  cn
+Kina   cn
+Kina   cn
+Xina   cn
+Chiny  cn
+Китай     cn
+Čína cn
+Kitajska       cn
+Kina   cn
+I-China        cn
+Kina   cn
+º£É¡       cn
+จีน      cn
+Çin   cn
+Китай     cn
+Trung Quốc   cn
+Chine  cn
+中国 cn
+中國 cn
+Colombia       co
+Colombië      co
+كولمبيا co
+Калюмбія       co
+Колумбия       co
+Kolumbija      co
+Colòmbia      co
+Kolumbie       co
+Kolumbien      co
+Κολομβία       co
+Kolumbio       co
+Kolumbia       co
+Kolonbia       co
+کلمبیا   co
+Kolumbia       co
+Colombie       co
+קולומביה       co
+Kolumbija      co
+Kolumbia       co
+コロンビア        co
+콜롬비아   co
+ໂຄລຳເບີຍ       co
+Kolumbija      co
+Kolumbija      co
+Kolumbja       co
+Columbia       co
+Kolumbia       co
+Colômbia      co
+Colômbia      co
+Columbia       co
+Колумбия       co
+Kolumbia       co
+Kolumbija      co
+I-Colombia     co
+¦¸¡ÄõÀ¢Â¡     co
+โคลัมเบีย    co
+Kolombiya      co
+Колумбія       co
+Colombeye      co
+Columbia       co
+哥伦比亚   co
+哥倫比亞   co
+Costa Rica     cr
+كوستاريكا     cr
+Коста Рыка    cr
+Коста Рика    cr
+Kostarika      cr
+Kostarika      cr
+Κόστα Ρίκα    cr
+کاستاریکا     cr
+Kosta Rika     cr
+קוסטה ריקה    cr
+Costa rica     cr
+コスタリカ        cr
+코스타 리카       cr
+ໂຄເອເທີຍ       cr
+Kosta Rika     cr
+Kostaryka      cr
+Коста-Рика    cr
+Kostarika      cr
+Kostarika      cr
+I-Costa Rica   cr
+§¸¡Š¼¡ Ã¢¸¡  cr
+Kosta Rika     cr
+Коста-Ріка    cr
+哥斯达黎加        cr
+哥斯大黎加        cr
+Cuba   cu
+Kuba   cu
+كوبا       cu
+Куба       cu
+Куба       cu
+Kuba   cu
+Kuba   cu
+Kuba   cu
+Κούβα     cu
+Kuuba  cu
+Kuba   cu
+کوبا       cu
+Kuuba  cu
+Kuba   cu
+קובה       cu
+Kuba   cu
+キューバ   cu
+쿠바 cu
+ເກມໄພ່     cu
+Kuba   cu
+Kuba   cu
+Kuba   cu
+Куба       cu
+Kuba   cu
+Kuba   cu
+I-Cuba cu
+Kuba   cu
+¸¢ÔÀ¡     cu
+Küba  cu
+Куба       cu
+古巴 cu
+古巴 cu
+Cape Verde     cv
+Капе Верде    cv
+Zelenortska ostrva     cv
+Kapverdiske øer       cv
+Cabo Verde     cv
+Cabo Verde     cv
+Zöldfoki-szigetek     cv
+ເກມໄພ່     cv
+Cabo Verde     cv
+Kap Verde      cv
+佛得角      cv
+Christmas Island       cx
+Božično ostrvo       cx
+Juleøen       cx
+Islas Christmas        cx
+Eguberri Irla  cx
+Karácsony-szigetek    cx
+ຄິດສະຕອລ       cx
+Ilhas do Natal cx
+Vianočné Ostrovy     cx
+Božični otok cx
+Julön cx
+Острів Різдва      cx
+圣诞岛      cx
+Cyprus cy
+Кіпр       cy
+Кипър     cy
+Kipar  cy
+Cypern cy
+Chipre cy
+Txipre cy
+Ciprus cy
+ບີບອັດ     cy
+Chipre cy
+Ciper  cy
+Cypern cy
+Кипр       cy
+塞浦路斯   cy
+Czechia        cz
+Czechië       cz
+التشيك   cz
+Çex Respublikası     cz
+Чэхія     cz
+Чехия     cz
+Tchekia        cz
+Češka        cz
+Txèquia       cz
+Česko cz
+Tjekkiet       cz
+Tschechien     cz
+Τσεχία   cz
+Ĉeĥio        cz
+República Checa       cz
+Tšehhi        cz
+Txekia cz
+چک   cz
+Tsekki cz
+République tchèque   cz
+Chequia        cz
+צ'כיה      cz
+Češka        cz
+Csehország    cz
+Tékkland    cz
+Repubblica Ceca        cz
+チェコ      cz
+체코 cz
+Čekija        cz
+Čehija        cz
+Cżekia        cz
+Tsjekkia       cz
+Tsjechië      cz
+Tsjekkia       cz
+Chèquia       cz
+Czechy cz
+República Checa       cz
+República Tcheca      cz
+Cehia  cz
+Чехия     cz
+Česko cz
+Češka        cz
+Češka        cz
+I-Czechia      cz
+Tjeckien       cz
+¦ºì¡     cz
+Çek Cumhuriyeti       cz
+Чехія     cz
+Séc   cz
+Tchekeye       cz
+捷克 cz
+捷克 cz
+Germany        de
+Duitsland      de
+ألمانيا de
+Almaniya       de
+Нямеччына     de
+Германия       de
+Alamagn        de
+Njemačka      de
+Alemanya       de
+Německo       de
+Tyskland       de
+Deutschland    de
+Γερμανία       de
+Germanio       de
+Alemania       de
+Saksamaa       de
+Alemania       de
+ آلمان    de
+Saksa  de
+Týskland      de
+Allemagne      de
+Alemaña       de
+גרמניה   de
+Njemačka      de
+Németország  de
+Jerman de
\9eýskaland        de
+Germania       de
+ドイツ      de
+독일 de
+ເຍລລະມັນນີ de
+Vokietija      de
+Vācija        de
+Ġermanja      de
+Tyskland       de
+Duitsland      de
+Tyskland       de
+Alemanya       de
+Niemcy de
+Alemanha       de
+Alemanha       de
+Germania       de
+Германия       de
+Nemecko        de
+Nemčija       de
+Nemačka       de
+I-Germany      de
+Tyskland       de
+§º÷ÁÉ¢   de
+เยอรมันนี    de
+Almanya        de
+Німеччина     de
+Đức         de
+Almagne        de
+德国 de
+德國 de
+IJalimani      de
+Djibouti       dj
+جيبوتي   dj
+Джыбуці dj
+Джибути dj
+Đibuti        dj
+Džibuti       dj
+Dschibuti      dj
+Τζιμπουτί     dj
+جیبوتی   dj
+ג'יבוטי  dj
+Džibuti       dj
+Dzsibuti       dj
+ジブティ   dj
+지부티      dj
+ພັດພາ        dj
+Džibuti       dj
+Dġibuti       dj
+Dżibuti       dj
+Djibuti        dj
+Djibuti        dj
+Джибути dj
+Džibuty       dj
+Džibuti       dj
+I-Djibouti     dj
+Ê¢ƒ¢¦À¡Ê       dj
+Cibuti dj
+Джібуті dj
+吉布提      dj
+吉布地      dj
+Denmark        dk
+Denemarke      dk
+الدنمارك       dk
+Danimarka      dk
+Данія     dk
+Дания     dk
+Danmark        dk
+Danska dk
+Dinamarca      dk
+Dánsko        dk
+Danmark        dk
+Dänemark      dk
+Δανία     dk
+Danlando       dk
+Dinamarca      dk
+Taani  dk
+Danimarka      dk
+دانمارک dk
+Tanska dk
+Danmark        dk
+Danemark       dk
+Dinamarca      dk
+דנמרק     dk
+Danska dk
+Dánia dk
+Danmörk     dk
+Danimarca      dk
+デンマーク        dk
+덴마크      dk
+ເດນມາກ     dk
+Danija dk
+Dānija        dk
+Danimarka      dk
+Danmark        dk
+Denemarken     dk
+Danmark        dk
+Dinamarca      dk
+Dania  dk
+Dinamarca      dk
+Dinamarca      dk
+Danemarca      dk
+Дания     dk
+Dánsko        dk
+Danska dk
+Danska dk
+I-Denmark      dk
+Danmark        dk
+¦¼ýÁ¡÷ì dk
+เดนมาร์ก       dk
+Danimarka      dk
+Данія     dk
+Đan Mạch    dk
+Daenmåtche    dk
+丹麦 dk
+丹麥 dk
+Dominica       dm
+Дамініка       dm
+Доминика       dm
+Dominika       dm
+Dominika       dm
+ໂລມາເນີຍ       dm
+Dominicana     dm
+Dominikánsko  dm
+Dominikanska republika dm
+Домініка       dm
+多米尼加   dm
+Dominican Republic     do
+Dominikiese Republiek  do
+جمهورية الدومينيكان  do
+Dominik Respublikası  do
+Дамініканская Рэспубліка        do
+Доминиканска република    do
+Republik Dominikan     do
+Dominikanska Republika do
+República Dominicana  do
+Dominikánská republika       do
+Dominikanske Republik  do
+Dominikanische Republik        do
+Δομινικανή Δημοκρατία      do
+Dominika Respubliko    do
+República Dominicana  do
+Dominikaani Vabariik   do
+Dominikar Errepublika  do
+جمهوری دامینیکن  do
+Dominikaaninen tasavalta       do
+République dominicaine        do
+República Dominicana  do
+הרפובליקה הדומיניקנית      do
+Dominikanska Republika do
+Dominikai Köztársaság       do
+Republik Dominika      do
+Dóminíska Lýðveldið     do
+Repubblica Dominicana  do
+ドミニカ共和国  do
+도미니카 공화국 do
+ໂດມິນີກັນ    do
+Dominikos Respublika   do
+Dominikas Republika    do
+Repubblika Dominikana  do
+Den dominikanske republikk     do
+Dominicaanse Republiek do
+Den dominikanske republikken   do
+Republica Dominicana   do
+Dominikana     do
+República Dominicana  do
+República Dominicana  do
+Republica Dominicană  do
+Доминиканская республика        do
+Dominikánska republika        do
+Dominikanska republika do
+Dominikanska republika do
+I-Dominican Republic   do
+Dominikanska republiken        do
+¦¼¡Á¢É¢ì¸ý ÌÊÂÃÍ        do
+โดมินิกัน    do
+Dominik Cumhuriyeti    do
+Домініканська республіка        do
+Muvhuso wa Dominican   do
+Cộng hoà Dominican  do
+Republike Dominikinne  do
+IRepublic yeDominican  do
+多米尼加共和国  do
+多明尼加共和國  do
+Algeria        dz
+Algerië       dz
+الجزائر dz
+Альжыр   dz
+Алжир     dz
+Alžir dz
+Algèria       dz
+Alžírsko     dz
+Algeriet       dz
+Algerien       dz
+Αλγερία dz
+Algerio        dz
+Argelia        dz
+Alžeeria      dz
+ الجزیره        dz
+Algérie       dz
+אלג'יריה        dz
+Alžir dz
+Algéria       dz
+アルジェリア     dz
+알제리      dz
+ບັນກາເລີຍ    dz
+Alžyras       dz
+Alġerija      dz
+Algerie        dz
+Algerije       dz
+Algerie        dz
+Algieria       dz
+Argélia       dz
+Argélia       dz
+Алжир     dz
+Alžírsko     dz
+Alžirija      dz
+I-Algeria      dz
+Algeriet       dz
+«øƒ¢Ã¢Â¡       dz
+Алжир     dz
+Aldjereye      dz
+阿尔及利亚        dz
+阿爾及利亞        dz
+Equador        ec
+Ewenaar        ec
+الإكوادور     ec
+Ekvator        ec
+Эквадор ec
+Еквадор ec
+Ecuador        ec
+Ekvador        ec
+Ekvádor       ec
+Ecuador        ec
+Ισημερινός   ec
+Ekvadoro       ec
+Ecuador        ec
+Ekvador        ec
+Ekuador        ec
+اکوادور ec
+Équateur      ec
+Ecuador        ec
+אקוודור ec
+Ekvador        ec
+Ecuador        ec
+Ekvador        ec
+Ecuador        ec
+エクアドル        ec
+에콰도르   ec
+ເອກໍດໍ     ec
+Ekvadoras      ec
+Ekvadora       ec
+Ekwador        ec
+Ecuador        ec
+Ecuador        ec
+Ekwador        ec
+Ecuador        ec
+Эквадор ec
+Ekvádor       ec
+Ekvador        ec
+Ekvador        ec
+I-Equador      ec
+®ì¦Å§¼¡÷       ec
+เอกวาดอร์    ec
+Ekvator        ec
+Еквадор ec
+Ecwåteur      ec
+厄瓜多尔   ec
+厄瓜多      ec
+Estonia        ee
+Estlandies     ee
+استونيا ee
+Estoniya       ee
+Эстонія ee
+Естония ee
+Estonija       ee
+Estònia       ee
+Estonsko       ee
+Estland        ee
+Estland        ee
+Εσθονία ee
+Estlando       ee
+Eesti  ee
+استونی   ee
+Eesti  ee
+Estonie        ee
+אסטוניה ee
+Estonija       ee
+Észtország   ee
+Eistland       ee
+エストニア        ee
+에스토니아        ee
+ເອໂທເນີຍ       ee
+Estija ee
+Igaunija       ee
+Estonja        ee
+Estland        ee
+Estland        ee
+Estland        ee
+Estònia       ee
+Estónia       ee
+Estônia       ee
+Эстония ee
+Estónsko      ee
+Estonija       ee
+Estonija       ee
+I-Estonia      ee
+Estland        ee
+±Í§¼¡É¢Â¡     ee
+เอสโธเนีย    ee
+Estonya        ee
+Естонія ee
+Estoneye       ee
+爱沙尼亚   ee
+愛沙尼亞   ee
+Egypt  eg
+Egipte eg
+مصر eg
+Misir  eg
+Эгіпэт   eg
+Египет   eg
+Egipat eg
+Egipte eg
+Egypten        eg
+Ägypten       eg
+Αίγυπτος       eg
+Egiptujo       eg
+Egipto eg
+Egiptus        eg
+Egypto eg
+مصر eg
+Egypti eg
+Egyptaland     eg
+Égypte        eg
+מצרים     eg
+Egipat eg
+Egyiptom       eg
+Egyptaland     eg
+Egitto eg
+エジプト   eg
+이집트      eg
+ອີຢີບ        eg
+Egiptas        eg
+Ēģipte       eg
+Eġittu        eg
+Egypte eg
+Egepeta        eg
+Egipt  eg
+Egipto eg
+Egito  eg
+Egipt  eg
+Египет   eg
+Egipt  eg
+I-Egypt        eg
+Egypten        eg
+±¸¢ôР    eg
+อียิปต์  eg
+Mısır        eg
+Єгипет   eg
+Edjipe eg
+埃及 eg
+埃及 eg
+Igibhithe      eg
+Western Sahara eh
+Западна Сахара    eh
+Zapadna Sahara eh
+Vestsahara     eh
+Sahara occidental      eh
+Mendebaldeko Sahara    eh
+Nyugat-Szahara eh
+ພື້ນທີ່ທຳງານ   eh
+Sahara eh
+Západna Sahara        eh
+Zahodna Sahara eh
+Västsahara    eh
+Західна Сахара    eh
+西撒哈拉   eh
+Eritrea        er
+Эрытрэя er
+Еритрея er
+Eritreja       er
+ແກ້ໄຂແຟ້ມທຳງານ     er
+Eritreja       er
+Еритрея er
+厄立特里亚        er
+Spain  es
+Spanje es
+أسبانيا es
+İspaniya      es
+Гішпанія       es
+Испания es
+Spagn  es
+Španija       es
+Espanya        es
+Španělsko    es
+Spanien        es
+Spanien        es
+Ισπανία es
+Hispanio       es
+España        es
+Hispaania      es
+Espainia       es
+اسپانیا es
+Espanja        es
+Spania es
+Espagne        es
+España        es
+ספרד       es
+Španjolska    es
+Spanyolország es
+Spanyol        es
+Spánn       es
+Spagna es
+スペイン   es
+스페인      es
+ສະເປັນ     es
+Ispanija       es
+Spānija       es
+Spanja es
+Spania es
+Spanje es
+Spania es
+Espanha        es
+Hiszpania      es
+Espanha        es
+Espanha        es
+Spania es
+Испания es
+Španielsko    es
+Španija       es
+Španija       es
+I-Spain        es
+Spanien        es
+ͦÀ¢ý   es
+สเปน   es
+İspanya       es
+Іспанія es
+Tây Ban Nha   es
+Sipagne        es
+西班牙      es
+西班牙      es
+Ethiopia       et
+Этыёпія et
+Етиопия et
+Etiopija       et
+Ethiopien      et
+Etiopía       et
+Etiopia        et
+Etiópia       et
+ເອໂທເນີຍ       et
+Etiópia       et
+Etiópia       et
+Etiopija       et
+Etiopien       et
+Ефіопія et
+埃塞俄比亚        et
+Finland        fi
+فنلندا   fi
+Finlandiya     fi
+Фінляндыя     fi
+Финландия     fi
+Finska fi
+Finlàndia     fi
+Finsko fi
+Finnland       fi
+Φινλανδία     fi
+Finlando       fi
+Finlandia      fi
+Soome  fi
+Finlandia      fi
+فنلاند   fi
+Suomi  fi
+Finnland       fi
+Finlande       fi
+Finlandia      fi
+פינלנד   fi
+Finska fi
+Finnország    fi
+Finlandia      fi
+Finnland       fi
+Finlandia      fi
+フィンランド     fi
+핀란드      fi
+ຟີນແລນ     fi
+Suomija        fi
+Somija fi
+Finlandja      fi
+Finlandia      fi
+Finlandia      fi
+Finlândia     fi
+Finlândia     fi
+Finlanda       fi
+Финляндия     fi
+Fínsko        fi
+Finska fi
+Finska fi
+I-Finland      fi
+À¢ýÄ¡óРfi
+ฟินแลนด์       fi
+Finlandiya     fi
+Фінляндія     fi
+Phần Lan     fi
+Finlande       fi
+芬兰 fi
+芬蘭 fi
+Fiji   fj
+Фіджы     fj
+Фиджи     fj
+Fidži fj
+Fidzsi fj
+ມີດີ   fj
+Ilhas Fiji     fj
+Fidži fj
+Fidži fj
+Фіджі     fj
+斐济 fj
+Falkland Islands (Malvinas)    fk
+Фолклендски Острови  fk
+Foklandska ostrva (Malvini)    fk
+Falkland-øerne        fk
+Islas Falkland (Malvinas)      fk
+Falkland Irlak (Malvinak)      fk
+Falkland-szigetek      fk
+Ilhas Malvinas fk
+Falklandské Ostrovy (Malviny) fk
+Falklandski otoki (Malvini)    fk
+Falklandsöarna        fk
+福克兰岛 (马尔维纳斯) fk
+Micronesia, Federated states of        fm
+Федэрацыя Мiкранэзіі fm
+Микронезия   fm
+Mikronezija, Federalne države fm
+Mikronesien, de forenede stater af     fm
+Micronesia, Estados federados de       fm
+Mikronesia, Estatu Federatuak  fm
+Mikronézia    fm
+Estados Federados da Micronésia       fm
+Spjoené štáty Mikronézie   fm
+Mikronezija, Združene države fm
+Mikronesiska federationen      fm
+密克罗尼西亚联邦       fm
+Faroe Islands  fo
+Фареорски Острови      fo
+Farska ostrva  fo
+Færøerne     fo
+islas Faroe    fo
+Faroe Irlak    fo
+Faroe-szigetek fo
+ໄອແລນ        fo
+Ilhas Faroe    fo
+Ostrovy Faroe  fo
+Otoki Faroe    fo
+Färöarna     fo
+Фарерські острови      fo
+France fr
+Frankryk       fr
+فرنسا     fr
+Fransa fr
+Францыя fr
+Франция fr
+Frañs       fr
+Francuska      fr
+França        fr
+Francie        fr
+Frankrig       fr
+Frankreich     fr
+Γαλλία   fr
+Francio        fr
+Francia        fr
+Prantsusmaa    fr
+Frantzia       fr
+فرانسه   fr
+Ranska fr
+Frakland       fr
+Francia        fr
+צרפת       fr
+Francuska      fr
+Franciaország fr
+Prancis        fr
+Frakkland      fr
+Francia        fr
+フランス   fr
+프랑스      fr
+ຝຣັ່ງ        fr
+Prancūzija    fr
+Francija       fr
+Franza fr
+Frankrike      fr
+Frankrijk      fr
+Frankrike      fr
+Fora   fr
+França        fr
+Francja        fr
+França        fr
+França        fr
+Franţa        fr
+Франция fr
+Francúzsko    fr
+Francija       fr
+Francuska      fr
+I-France       fr
+Frankrike      fr
+À¢Ã¡ýÍ   fr
+ฝรั่งเศส       fr
+Fransa fr
+Франція fr
+Fura   fr
+Pháp  fr
+Fransi fr
+法国 fr
+法國 fr
+Gabon  ga
+Габон     ga
+Габон     ga
+Gabón ga
+ແກລ່ງນ     ga
+Gabão ga
+Габон     ga
+加蓬 ga
+United Kingdom gb
+Vereenigde Koninkryk   gb
+المملكة المتحدة  gb
+Birləşmiş Krallıq  gb
+Злучанае Каралеўства        gb
+Великобритания   gb
+Rouantelezh Unanet     gb
+Velika Britanija       gb
+Regne Unit     gb
+Spojené království  gb
+Storbritannien gb
+Großbritannien        gb
+Ηνωμένο Βασίλειο        gb
+Britio gb
+Reino Unido    gb
+Ühendatud Kuningriigid        gb
+Erreinu Batua  gb
+بریتانیا       gb
+Iso-Britannia  gb
+Stórabretland gb
+Royaume Uni    gb
+Reino Unido    gb
+בריטניה gb
+Ujedinjeno Kraljevstvo gb
+Egyesült Királyság  gb
+Inggris        gb
+Stóra Bretland      gb
+Regno Unito    gb
+イギリス   gb
+영국 gb
+ສະຫະລາດສະອານາຈັກ       gb
+Jungtinė Karalystė   gb
+Apvienotā Karaliste   gb
+Renju Unit     gb
+Storbritannia  gb
+Verenigd Koninkrijk    gb
+Storbritannia  gb
+Regne Unit     gb
+Wielka Brytania        gb
+Reino Unido    gb
+Reino Unido    gb
+Anglia gb
+Великобритания   gb
+Anglicko       gb
+Združeno kraljestvo   gb
+Velika Britanija       gb
+I-United Kingdom       gb
+Storbritannien gb
+³ì¸¢Â ­Ã¡îº¢Âõ    gb
+สหราชอาณาจักร        gb
+Birleşik Krallık     gb
+Великобританія   gb
+Anh    gb
+United Kingdom         gb
+联合王国   gb
+聯合王國   gb
+United Kingdom         gb
+Grenada        gd
+غرينادا gd
+Qrenada        gd
+Грэнада gd
+Гренада gd
+Granada        gd
+Γρενάδα gd
+Grenado        gd
+Granada        gd
+گرانادا gd
+Grenade        gd
+Granada        gd
+גרנדה     gd
+Grænhöfðaeyjar        gd
+Granada        gd
+グラナダ   gd
+그러네이다        gd
+ເກນາດາ     gd
+Grenāda       gd
+Granada        gd
+Granada        gd
+Granada        gd
+Гренада gd
+I-Grenada      gd
+¸¢¦Ãɼ¡ gd
+เกรนาดา  gd
+Гренада gd
+格林纳达   gd
+格瑞那達   gd
+Georgia        ge
+Грузія   ge
+Грузия   ge
+Gruzija        ge
+Georgien       ge
+Grúzia        ge
+ເຊີເບີຍ  ge
+Geórgia       ge
+Gruzija        ge
+Georgien       ge
+Грузія   ge
+格鲁吉亚   ge
+Ghana  gh
+غانا       gh
+Гана       gh
+Гана       gh
+Gana   gh
+Γκάνα     gh
+غنا gh
+גאנה       gh
+ガーナ      gh
+ຈີນ      gh
+Gana   gh
+Gana   gh
+Gana   gh
+Гана       gh
+Gana   gh
+I-Ghana        gh
+¸¡É¡       gh
+Гана       gh
+加纳 gh
+迦納 gh
+Gibraltar      gi
+Гибралтар     gi
+Gibraltár     gi
+ມອລຕາ        gi
+Гібралтар     gi
+直布罗陀   gi
+Gambia gm
+Гамбія   gm
+Гамбия   gm
+Gambija        gm
+ແກມມາ        gm
+Gâmbia        gm
+Gambija        gm
+Гамбія   gm
+冈比亚      gm
+Guinea gn
+Гвінэя   gn
+Гвинея   gn
+Gvineja        gn
+ເຖາວັນ     gn
+Guiné gn
+Gvineja        gn
+Гвінея   gn
+几内亚      gn
+Guadeloupe     gp
+Гвадалупа     gp
+Gvadalupe      gp
+Guadalupe      gp
+ເດີລຸກ     gp
+Guadalupe      gp
+Гваделупа     gp
+瓜德罗普岛        gp
+Equatorial Guinea      gq
+Экватарыяльная Гвінэя      gq
+Екваториялна Гвинея  gq
+Ekvatorijalna Gvineja  gq
+Ækvatorial Guinea     gq
+Guinea equatorial      gq
+Ginea Ekuatoriala      gq
+Egyenlítői Guinea    gq
+ການສອນ     gq
+Guiné Equatorial      gq
+Rovníkova Guinea      gq
+Ekvatorialna Gvineja   gq
+Ekvatorialguinea       gq
+Екваторіальна Гвінея        gq
+赤道几内亚        gq
+Greece gr
+Griekeland     gr
+اليونان gr
+Yunanıstan    gr
+Грэцыя   gr
+Гърция   gr
+Gres   gr
+Grčka gr
+Grècia        gr
+Řecko gr
+Grækenland    gr
+Griechenland   gr
+Ελλάδα   gr
+Grekujo        gr
+Grecia gr
+Kreeka gr
+Grezia gr
+یونان     gr
+Kreikka        gr
+Grikkaland     gr
+Grèce gr
+Grecia gr
+יוון       gr
+Grčka gr
+Görögország gr
+Grikkland      gr
+Grecia gr
+ギリシャ   gr
+그리스      gr
+ກີຊ      gr
+Graikija       gr
+Grieķija      gr
+Greċja        gr
+Hellas gr
+Griekenland    gr
+Hellas gr
+Grèça        gr
+Grecja gr
+Grécia        gr
+Grécia        gr
+Grecia gr
+Греция   gr
+Grécko        gr
+Grčija        gr
+Grčka gr
+I-Greece       gr
+Grekland       gr
+¸¢Ã£Í     gr
+กรีซ   gr
+Yunanistan     gr
+Греція   gr
+Hy Lạp       gr
+Grece  gr
+希腊 gr
+希臘 gr
+Guatemala      gt
+Gautemala      gt
+غواتيمالا     gt
+Quatemala      gt
+Гватэмала     gt
+Гватемала     gt
+Gvatemala      gt
+Γουατεμάλα   gt
+Gvatemalo      gt
+Guatemaala     gt
+گواتمالا       gt
+גואטמלה gt
+Gvatemala      gt
+カタロニア        gt
+과테말라   gt
+ກັວເຕມາລາ    gt
+Gvatemala      gt
+Gvatemala      gt
+Gwatemala      gt
+Gwatemala      gt
+Гватемала     gt
+Gvatemala      gt
+Gvatemala      gt
+I-Guatemala    gt
+ÌÅ¡ò¾Á¡Ä¡     gt
+กัวเตมาลา    gt
+Гватемала     gt
+Gwatemala      gt
+瓜地马拉   gt
+瓜地馬拉   gt
+Guam   gu
+Гуам       gu
+ແກມມາ        gu
+Гуам       gu
+关岛 gu
+Guinea-Bissau  gw
+Гвінэя-Бісаў        gw
+Гвинея-Бисау        gw
+Gvineja-Bisau  gw
+Ginea-Bissau   gw
+Bissau-Guinea  gw
+ລັດເຊີຍ  gw
+Guiné-Bissau  gw
+Гвінея-Біссау      gw
+几内亚比绍        gw
+Guyana gy
+Гвіяна   gy
+Гуана     gy
+Gvajana        gy
+ຈີນ      gy
+Guiana gy
+Gvajana        gy
+Гаяна     gy
+圭亚那      gy
+Hong Kong      hk
+Ганконг hk
+Хонг Конг      hk
+Hongkong       hk
+ບໍ່ຮູ້ຈັກ    hk
+Гонконг hk
+香港 hk
+Honduras       hn
+هندوراس hn
+Гандурас       hn
+Хондурас       hn
+Hondures       hn
+Ονδούρα hn
+Honduraso      hn
+Honduuras      hn
+هندوراس hn
+הונדורס hn
+ホンデュラス     hn
+온두라스   hn
+ຫອນດູລັດ       hn
+Hondūras      hn
+Hondurasa      hn
+Ħonduras      hn
+Hondures       hn
+Гондурас       hn
+I-Honduras     hn
+¬ñÎá\8aÍ hn
+ฮอนดูรัส       hn
+Гондурас       hn
+洪都拉斯   hn
+宏都拉斯   hn
+Croatia        hr
+Kroatië       hr
+كرواتيا hr
+Xırvatıstan  hr
+Харватыя       hr
+Хърватска     hr
+Kroatia        hr
+Hrvatska       hr
+Croàcia       hr
+Chorvatsko     hr
+Kroatien       hr
+Kroatien       hr
+Κροατία hr
+Kroatio        hr
+Croacia        hr
+Horvaatia      hr
+Kroazia        hr
+کرواسی   hr
+Kroatia        hr
+Kroatia        hr
+Croatie        hr
+Croacia        hr
+קרואטיה hr
+Hrvatska       hr
+Horvátország hr
+Kroasia        hr
+Króatía  hr
+Croazia        hr
+クロアチア        hr
+크로아티아        hr
+ໂຄເອເທີຍ       hr
+Kroatija       hr
+Horvātija     hr
+Kroazja        hr
+Kroatia        hr
+Kroatië       hr
+Kroatia        hr
+Croacia        hr
+Chorwacja      hr
+Croácia       hr
+Croácia       hr
+Croaţia       hr
+Хорватия       hr
+Chorvátsko    hr
+Hrvaška       hr
+Hrvatska       hr
+I-Croatia      hr
+Kroatien       hr
+̦á§Åº¢Â¡   hr
+โครเอเธีย    hr
+Hırvatistan   hr
+Хорватія       hr
+Crowåceye     hr
+克罗地亚   hr
+克羅埃西亞        hr
+Haiti  ht
+Гаіці     ht
+Хаити     ht
+Haití ht
+ວາດຮູບ - K ht
+Гаїті     ht
+海地岛      ht
+Hungary        hu
+Hongarye       hu
+هنغاريا hu
+Macarıstan    hu
+Вугоршчына   hu
+Унгария hu
+Hungaria       hu
+Mađarska      hu
+Hongria        hu
+Maďarsko      hu
+Ungarn hu
+Ungarn hu
+Ουγγαρία       hu
+Hungario       hu
+Hungría       hu
+Ungari hu
+Hungaria       hu
+مجارستان       hu
+Unkari hu
+Ungarn hu
+Hongrie        hu
+Hungría       hu
+הונגריה hu
+Mađjarska     hu
+Magyarország  hu
+Hungaria       hu
+Ungverjaland   hu
+Ungheria       hu
+ハンガリー        hu
+헝가리      hu
+ຫັງກາລີ  hu
+Vengrija       hu
+Ungārija      hu
+Ungerija       hu
+Ungarn hu
+Hongarije      hu
+Ungarn hu
+Hongria        hu
+Węgry hu
+Hungria        hu
+Hungria        hu
+Ungaria        hu
+Венгрия hu
+Maďarsko      hu
+Madžarska     hu
+Mađarska      hu
+I-Hungary      hu
+Ungern hu
+¬í§¸Ã¢   hu
+ฮังการี  hu
+Macaristan     hu
+Угорщина       hu
+Hongreye       hu
+匈牙利      hu
+匈牙利      hu
+Indonesia      id
+Indonesië     id
+إندونيسيا     id
+İndoneziya    id
+Інданэзія     id
+Индонезия     id
+Indonezija     id
+Indonèsia     id
+Indonésie     id
+Indonesien     id
+Indonesien     id
+Ινδονησία     id
+Indonezio      id
+Indoneesia     id
+اندونزی id
+Indonésie     id
+אינדונזיה     id
+Indonezija     id
+Indonézia     id
+Indónesía        id
+インドネシア     id
+인도네시아        id
+ອີຍໂດນີເຊີຍ      id
+Indonezija     id
+Indonēzija    id
+Indoneżja     id
+Indonesië     id
+Indonezja      id
+Indonésia     id
+Indonésia     id
+Indonezia      id
+Индонезия     id
+Indonézia     id
+Indonezija     id
+I-Indonesia    id
+Indonesien     id
+­ó§¾¡É£º¢Â¡ id
+อินโดนีเซีย      id
+İndonezya     id
+Індонезія     id
+Indoneseye     id
+印度尼西亚        id
+印尼 id
+Ireland        ie
+Ierland        ie
+أيرلندا ie
+İrlandiya     ie
+Ірляндыя       ie
+Ирландия       ie
+Iwerzhon       ie
+Irska  ie
+Irlanda        ie
+Irsko  ie
+Irland ie
+Irland ie
+Ιρλανδία       ie
+Islando        ie
+Irlanda        ie
+Island ie
+Irlanda        ie
+ایرلند   ie
+Irlanti        ie
+Írland        ie
+Irlande        ie
+Irlanda        ie
+אירלנד   ie
+Irska  ie
+Írország     ie
+Irlandia       ie
\8drland      ie
+Irlanda        ie
+アイスランド     ie
+아일랜드   ie
+ໄອແລນ        ie
+Airija ie
+Īrija ie
+Irlanda        ie
+Irland ie
+Ierland        ie
+Irland ie
+Irlanda        ie
+Irlandia       ie
+Irlanda        ie
+Irlanda        ie
+Irlanda        ie
+Ирландия       ie
+Írsko ie
+Irska  ie
+Irska  ie
+I-Ireland      ie
+Irland ie
+«Â÷Ä¡óРie
+ไอร์แลนด์    ie
+İrlanda       ie
+Ірландія       ie
+Irlande        ie
+爱尔兰      ie
+愛爾蘭      ie
+Israel il
+اسرائيل il
+İzrail        il
+Ізраіль il
+Израел   il
+Izrael il
+Izrael il
+Ισραήλ   il
+Israelo        il
+Iisrael        il
+اسراییل il
+Ísrael        il
+Israël        il
+ישראל     il
+Izrael il
+Izrael il
\8dsrael      il
+Israele        il
+イスラエル        il
+이스라엘   il
+ອິດສະລະເອລ il
+Izraelis       il
+Izraēla       il
+Iżrael        il
+Izrael il
+Израиль il
+Izrael il
+Izrael il
+Izrael il
+I-Israel       il
+­Í§Ãø     il
+อิสราเอล       il
+İsrail        il
+Ізраїль il
+Israyel        il
+USirayeli      il
+以色列      il
+以色列      il
+India  in
+Indië in
+الهند     in
+Hindistan      in
+Індыя     in
+Индия     in
+Indija in
+Índia in
+Indie  in
+Indien in
+Indien in
+Ινδία     in
+Hindujo        in
+هندوستان       in
+Intia  in
+Inde   in
+הודו       in
+Indija in
+Indland        in
+インド      in
+인도 in
+ອິນເດີຍ  in
+Indija in
+Indija in
+Indja  in
+Indie  in
+Índia in
+Índia in
+Индия     in
+Indija in
+I-India        in
+Indien in
+­ó¾¢Â¡   in
+อินเดีย  in
+Hindistan      in
+Індія     in
+Inde   in
+印度 in
+印度 in
+Endiya in
+Iraq   iq
+Irak   iq
+العراق   iq
+İraq  iq
+Ірак       iq
+Ирак       iq
+Irak   iq
+Irák  iq
+Irak   iq
+Irak   iq
+Ιράκ       iq
+Irako  iq
+Irak   iq
+Iraak  iq
+عراق       iq
+Irak   iq
+Irak   iq
+Irak   iq
+עירק       iq
+Irak   iq
+Irak   iq
\8drak        iq
+Irak   iq
+イラク      iq
+이라크      iq
+ອີລັກ        iq
+Irakas iq
+Irāka iq
+Irak   iq
+Irak   iq
+Irak   iq
+Irak   iq
+Iraque iq
+Iraque iq
+Irak   iq
+Ирак       iq
+Irák  iq
+Irak   iq
+I-Iraq         iq
+Irak   iq
+®Ã¡ì       iq
+อิรัค        iq
+Irak   iq
+Ірак       iq
+Irak   iq
+伊拉克      iq
+伊拉克      iq
+Iran   ir
+أيران     ir
+Іран       ir
+Иран       ir
+Írán ir
+Ιράν       ir
+Iraan  ir
+ایران     ir
+אירן       ir
+Irán  ir
+イラン      ir
+이란 ir
+ອີລັກ        ir
+Iranas ir
+Irão  ir
+Irã   ir
+Иран       ir
+Irán  ir
+I-Iran ir
+®Ã¡ý       ir
+İran  ir
+Іран       ir
+伊朗 ir
+伊朗 ir
+Iceland        is
+Ysland is
+أيسلندا is
+İslandiya     is
+Ісьляндыя     is
+Исландия       is
+Island is
+Island is
+Islàndia      is
+Island is
+Island is
+Island is
+Ισλανδία       is
+Islando        is
+Islandia       is
+Island is
+Islandia       is
+ایسلند   is
+Islanti        is
+Ísland        is
+Islande        is
+Islandia       is
+איסלנד   is
+Island is
+Izland is
+Islandia       is
\8dsland      is
+Islanda        is
+アイスランド     is
+아이슬란드        is
+ໄອຊແລນ     is
+Islandija      is
+Islande        is
+Islandja       is
+Island is
+IJsland        is
+Island is
+Islandia       is
+Islandia       is
+Islândia      is
+Islândia      is
+Islanda        is
+Исландия       is
+Island is
+Islandija      is
+Island is
+I-Iceland      is
+Island is
+³ÍÄ¡óР  is
+ไอซ์แลนด์    is
+İzlanda       is
+Ісландія       is
+Izlande        is
+冰岛 is
+冰島 is
+Icelandi       is
+Italy  it
+Italië        it
+ايطاليا it
+İtalyia       it
+Італія   it
+Италия   it
+Italia it
+Italija        it
+Itàlia        it
+Itálie        it
+Italien        it
+Italien        it
+Ιταλία   it
+Italio it
+Italia it
+Itaalia        it
+Italia it
+ایتالیا it
+Italia it
+Italia it
+Italie it
+Italia it
+איטליה   it
+Italija        it
+Olaszország   it
+Italia it
\8dtalía   it
+Italia it
+イタリア   it
+이탈리아   it
+ອີຕາລີ     it
+Italija        it
+Itālija       it
+Italja it
+Italia it
+Italië        it
+Italia it
+Italia it
+Włochy        it
+Itália        it
+Itália        it
+Italia it
+Италия   it
+Taliansko      it
+Italija        it
+Italija        it
+I-Italy        it
+Italien        it
+­ò¾¡Ä¢   it
+อิตาลี     it
+İtalya        it
+Італія   it
+Itåleye       it
+Ithali it
+意大利      it
+義大利      it
+Jamaica        jm
+Jamaika        jm
+جامايكا jm
+Yamayka        jm
+Ямайка   jm
+Ямайка   jm
+Jamaika        jm
+Jamajka        jm
+Jamajka        jm
+Jamaika        jm
+Τζαμάικα       jm
+Ĵamaiko       jm
+Jamaika        jm
+Jamaika        jm
+جاماییکا       jm
+Jamaika        jm
+Jamaïque      jm
+Xamaica        jm
+ג'מייקה  jm
+Jamajka        jm
+Jamaika        jm
+Jamaika        jm
+Giamaica       jm
+ジャマイカ        jm
+자메이카   jm
+ຈາໄມກາ     jm
+Jamaika        jm
+Jamaika        jm
+Ġamajka       jm
+Jamajka        jm
+Ямайка   jm
+Jamajka        jm
+Jamajka        jm
+Jamajka        jm
+I-Jamaica      jm
+\83º¦Áö측       jm
+จาไมกา     jm
+Jamaika        jm
+Ямайка   jm
+Djamayike      jm
+牙买加      jm
+牙買加      jm
+Jordan jo
+Jordaan        jo
+الأردن   jo
+İordaniya     jo
+Ярданія jo
+Йордания       jo
+Jordania       jo
+Jordán        jo
+Jordanien      jo
+Ιορδανία       jo
+Jordanio       jo
+Jordania       jo
+اردن       jo
+Jordania       jo
+Jordanie       jo
+ירדן       jo
+Jordánia      jo
+Jórdanía jo
+Giordania      jo
+ヨルダン   jo
+요르단      jo
+ຈໍແດນ        jo
+Jordanija      jo
+Jordāna       jo
+Ġordan        jo
+Jordanië      jo
+Jordania       jo
+Jordânia      jo
+Jordânia      jo
+Iordania       jo
+Иордания       jo
+Jordánsko     jo
+Jordanija      jo
+I-Jordan       jo
+Jordanien      jo
+§Â¡÷¾¡ý jo
+จอร์แดน  jo
+Ürdün        jo
+Йорданія       jo
+Djordaneye     jo
+约旦 jo
+約旦 jo
+Ijolidani      jo
+Japan  jp
+اليابان jp
+Yaponiya       jp
+Японія   jp
+Япония   jp
+Japó  jp
+Japonsko       jp
+Ιαπωνία jp
+Japanio        jp
+Japón jp
+Jaapan jp
+Japonia        jp
+ژاپن       jp
+Japani jp
+Japon  jp
+Xapón jp
+יפן jp
+Japán jp
+Jepang jp
+Giappone       jp
+日本 jp
+일본 jp
+ຍີ່ປຸ່ນ  jp
+Japonija       jp
+Japāna        jp
+Ġappun        jp
+Japon  jp
+Japonia        jp
+Japão jp
+Japão jp
+Japonia        jp
+Япония   jp
+Japonsko       jp
+Japonska       jp
+I-Japan        jp
+ºôÀ¡ý     jp
+ญี่ปุ่น  jp
+Japonya        jp
+Японія   jp
+Nhật bản   jp
+Djapon jp
+日本 jp
+日本 jp
+Kenya  ke
+Кенія     ke
+Кения     ke
+Kenija ke
+Kenia  ke
+ເວນດາ        ke
+Quênia        ke
+Keňa  ke
+Kenija ke
+Кенія     ke
+肯尼亚      ke
+Kyrgyzstan     kg
+Кыргызстан   kg
+Киргистан     kg
+Kirgistan      kg
+Kirgizistan    kg
+Kyrgyzstán    kg
+Kirgizisztán  kg
+ຄສິຕັລ     kg
+Kirgizstan     kg
+Kirgizistan    kg
+Киргизстан   kg
+吉尔吉斯坦        kg
+Cambodia       kh
+Камбоджа       kh
+Камбоджа       kh
+Kambođa       kh
+Kambodzsa      kh
+ໂຄລຳເບີຍ       kh
+Cambodja       kh
+Kambodža      kh
+Kambodža      kh
+Kambodja       kh
+Камбоджа       kh
+柬埔寨      kh
+Kiribati       ki
+Кiрыбацi ki
+Кирибати       ki
+ແຟຄທັລ - K ki
+Кірібаті       ki
+基里巴斯   ki
+Comoros        km
+Каморы   km
+Коморски       km
+Komori km
+Comorerne      km
+ສີ km
+Komori km
+Komorerna      km
+Комори   km
+科摩罗群岛        km
+St. Kitts and Nevis    kn
+St. Kitts en Nevis     kn
+سانت كيتس و نيفيس        kn
+St. Kitts və Nevis    kn
+Св. Кристоф и Невис     kn
+S. Kitts ha Nevis      kn
+St. Kitts i Nevis      kn
+Sv. Kitts a Nevis      kn
+St. Kitts-Nevis        kn
+St. Kitts und Nevis    kn
+St. Kitts και Nevis kn
+St. Kitts kaj Nevis    kn
+St. Kitts y Nevis      kn
+St. Kitts ja Nevis     kn
+St. Kitts eta Nevis    kn
+سن کیتس و نویس      kn
+St. Kitts ja Nevis     kn
+St Kitts et Nevis      kn
+Saint Kitts e Nevis    kn
+סנט קיטס ונביס     kn
+St. Kitts és Nevis    kn
+St. Kitts dan Nevis    kn
+Ss. Kitts e Nevis      kn
+セントキッツネヴィス kn
+세인트 키츠 네비스     kn
+Šv. Kitts ir Nevis    kn
+St. Kitts un Nevis     kn
+St. Kitts u Nevis      kn
+St. Kitts og Nevis     kn
+St. Kitts en Nevis     kn
+St. Kitts og Nevis     kn
+St. Kitts le Nevis     kn
+St. Kitts e Nevis      kn
+St. Kitts e Nevis      kn
+St Kitts e Nevis       kn
+Sf. Kitts şi Nevis    kn
+о. Св. Кристофа и Невиса     kn
+St. Kitts a Nevis      kn
+St. Kitts in Nevis     kn
+St. Kitts i Nevis      kn
+I-St. Kitts and Nevis  kn
+St. Kitts och Nevis    kn
+¦ºÂ¢ý𠸢ðÍ & ¦¿Å¢Í     kn
+St. Kitts ve Nevis     kn
+Федерація Сент-Кітс і Невіс     kn
+St. Kitts na Nevis     kn
+St. Kitts neNevis      kn
+圣基特和里维斯  kn
+聖克理斯多福及尼維斯 kn
+St. Kitts kanye no-Nevis       kn
+North Korea    kp
+Noord Korea    kp
+كوريا الشمالية    kp
+Şimali Koreya kp
+Паўночная Карэя  kp
+Северна Корея      kp
+Norzh-Korea    kp
+Sjeverna Koreja        kp
+Corea del Nord kp
+Severní Korea kp
+Nordkorea      kp
+Nord-Korea     kp
+Βόρεια Κορέα        kp
+Nordkoreo      kp
+Corea del Norte        kp
+Põhja-Korea   kp
+Ipar Korea     kp
+کره شمالی      kp
+Pohjois-Korea  kp
+Norðurkorea   kp
+Corée du nord kp
+Corea do Norte kp
+צפון קוריאה  kp
+Sjeverna Koreja        kp
+Észak-Korea   kp
+Korea Utara    kp
+Kórea - Norðurkórea   kp
+Corea del Nord kp
+北朝鮮      kp
+조선민주주의 인민공화국     kp
+ເກົາລີເຫນືອ      kp
+Šiaurės Korėja      kp
+ZiemeļKoreja  kp
+Korea ta' Fuq  kp
+Nord-Korea     kp
+Noord-Korea    kp
+Nord-Korea     kp
+Lebowa la Korea        kp
+Corea dèu Nord        kp
+Korea Północna       kp
+Coreia do Norte        kp
+Coréia do Norte       kp
+Coreea de Nord kp
+Северная Корея    kp
+severná Kórea        kp
+Severna Koreja kp
+Severna Koreja kp
+I-North Korea  kp
+Nordkorea      kp
+ż ¦¸¡Ã¢Â¡    kp
+เกาหลีเหนือ      kp
+Kuzey Kore     kp
+Північна Корея    kp
+Devhula ha Korea       kp
+Bắc Triều Tiên    kp
+Bijhe Coreye   kp
+Umntla Korea   kp
+朝鲜 kp
+北韓 kp
+Enyakatho ne-Korea     kp
+South Korea    kr
+Suid Korea     kr
+كوريا الجنوبية    kr
+Cənubi Koreya kr
+Паўднёвая Карэя  kr
+Южна Корея    kr
+Su-Korea       kr
+Južna Koreja  kr
+Corea del Sud  kr
+Jižní Korea  kr
+Sydkorea       kr
+Süd-Korea     kr
+Νότια Κορέα  kr
+Sudkoreo       kr
+Corea del Sur  kr
+Lõuna-Korea   kr
+Hego Korea     kr
+کره جنوبی      kr
+Etelä-Korea   kr
+Suðurkorea    kr
+Corée du sud  kr
+Corea do Sur   kr
+דרום קוריאה  kr
+Južna Koreja  kr
+Dél-Korea     kr
+Korea Selatan  kr
+Kórea - Suðurkórea    kr
+Corea del Sud  kr
+韓国 kr
+대한민국   kr
+ເກົາລີໃຕ້    kr
+Pietų Korėja kr
+DievidKoreja   kr
+Korea t'Isfel  kr
+Sør-Korea     kr
+Zuid-Korea     kr
+Sør-Korea     kr
+Borwa bja Korea        kr
+Corea dèu Sud kr
+Korea Południowa      kr
+Coreia do Sul  kr
+Coréia do Sul kr
+Coreea de Sud  kr
+Южная Корея  kr
+Južná Kórea kr
+Južna Koreja  kr
+Južna Koreja  kr
+I-South Korea  kr
+Sydkorea       kr
+¦¾ý ¦¸¡Ã¢Â¡  kr
+เกาหลีใต้    kr
+Güney Kore    kr
+Південна Корея    kr
+Korea tshipembe        kr
+Hàn Quốc    kr
+Nonne Coreye   kr
+Umzantsi Korea kr
+韩国 kr
+南韓 kr
+Emzansi Korea  kr
+Kuwait kw
+Kuwaït        kw
+الكويت   kw
+Кувэйт   kw
+Кувейт   kw
+Kuvajt kw
+Kuvajt kw
+Κουβέιτ kw
+Kuveit kw
+کویت       kw
+Kuvait kw
+Kowait kw
+כווית     kw
+Kuvajt kw
+Kuvait kw
+クェート   kw
+쿠웨이트   kw
+ແຕ້ມຮູບ- K       kw
+Kuveitas       kw
+Koeweit        kw
+Koweit kw
+Kuveit kw
+Кувейт   kw
+Kuvajt kw
+Kuvajt kw
+I-Kuwait       kw
+̨Åò       kw
+KКувейт  kw
+科威特      kw
+科威特      kw
+Cayman Islands ky
+Кайманови Острови      ky
+Kajmanska ostrva       ky
+Cayman-øerne  ky
+Islas Caimán  ky
+Kaiman Irlak   ky
+Kajmán-szigetek       ky
+ຄາຕາລັນ  ky
+Ilhas Cayman   ky
+Kajmanske Ostrovy      ky
+Kajmanski otoki        ky
+Caymanöarna   ky
+Кайманські острови    ky
+开曼群岛   ky
+Kazakhstan     kz
+Казахстан     kz
+Казахстан     kz
+Kazahstan      kz
+Kazakhstán    kz
+Kazahsztán    kz
+ແກແລກຕິກ - K   kz
+Kazaquistão   kz
+Kazachstan     kz
+Kazahstan      kz
+Kazakstan      kz
+Казахстан     kz
+哈萨克斯坦        kz
+Laos   la
+Лаос       la
+Лаос       la
+Laosz  la
+ລາວ      la
+老挝 la
+Lebanon        lb
+Libanon        lb
+لبنان     lb
+Ліван     lb
+Ливан     lb
+Liban  lb
+Libanon        lb
+Libanon        lb
+Libanon        lb
+Λίβανος lb
+Lebanono       lb
+Líbano        lb
+Liibanon       lb
+لبنان     lb
+Libanon        lb
+Libanon        lb
+Liban  lb
+לבנון     lb
+Libanon        lb
+Libanon        lb
+Libano lb
+レバノン   lb
+레바논      lb
+ເດນ່ງນ     lb
+Libanas        lb
+Libanu lb
+Libanon        lb
+Libanon        lb
+Libanon        lb
+Liban  lb
+Líbano        lb
+Líbano        lb
+Liban  lb
+Ливан     lb
+Libanon        lb
+Libanon        lb
+I-Lebanon      lb
+Libanon        lb
+¦ÄÀÉ¡ý   lb
+Ліван     lb
+Liban  lb
+黎巴嫩      lb
+黎巴嫩      lb
+St. Lucia      lc
+سانت لوسيا    lc
+Св. Люсиа       lc
+S. Lucia       lc
+Svatá Lucie   lc
+Σάντα Λουτσία      lc
+St. Lucio      lc
+Santa Lucía   lc
+سن لوسیا        lc
+Sankta Lusia   lc
+Sainte Lucie   lc
+Santa Lucía   lc
+סנטה לוסיה    lc
+Santa Lucia    lc
+セントルキア     lc
+세인트 루시아    lc
+ເຊັນລູເຊີຍ lc
+Šv Liucija    lc
+Sv. Lūcija    lc
+St. Luċija    lc
+Santa Lúcia   lc
+Santa Lúcia   lc
+Sf. Lucia      lc
+о. Св. Люсии   lc
+Sv. Júlia     lc
+Sv. Lucija     lc
+I-St. Lucia    lc
+¦ºýð æº¢Â¡    lc
+เซนต์ลูเซีย      lc
+Санта Лучія  lc
+圣路西亚   lc
+聖露西亞   lc
+Liechtenstein  li
+Ліхтэнштэйн li
+Лихтенщайн   li
+Lihtenštajn   li
+Liechtestein   li
+ຟ້າແມບ     li
+列支敦士登        li
+Sri Lanka      lk
+Шры-Ланка      lk
+Шри Ланка      lk
+Šri Lanka     lk
+ເຊີເບີຍ  lk
+Šri Lanka     lk
+斯里兰卡   lk
+Liberia        lr
+Лібэрыя lr
+Либеря   lr
+Liberija       lr
+Libéria       lr
+ລິຊາ   lr
+Libéria       lr
+Lýbia lr
+Liberija       lr
+利比里亚   lr
+Lesotho        ls
+Лесота   ls
+Лесото   ls
+Lesoto ls
+Lesoto ls
+ທົດສອບ     ls
+Lesoto ls
+莱索托      ls
+Lithuania      lt
+Lithuanië     lt
+ليتوانيا       lt
+Litvaniya      lt
+Літва     lt
+Литва     lt
+Litvanija      lt
+Lituània      lt
+Litva  lt
+Litauen        lt
+Litauen        lt
+Λιθουανία     lt
+Litovio        lt
+Lituania       lt
+Leedu  lt
+Lituania       lt
+لیتوانی lt
+Liettua        lt
+Lituanie       lt
+ליטא       lt
+Litva  lt
+Litvánia      lt
+Litháenska  lt
+Lituania       lt
+リトアニア        lt
+리 투아니아       lt
+ລິທົ່ວເນີຍ lt
+Lietuva        lt
+Lietuva        lt
+Litwanja       lt
+Litauen        lt
+Litouwen       lt
+Litauen        lt
+Litwa  lt
+Lituânia      lt
+Lituânia      lt
+Lituania       lt
+Литва     lt
+Litva  lt
+Litva  lt
+Litvanija      lt
+I-Lithuania    lt
+Litauen        lt
+Ä¢òЧÅɢ¡   lt
+ลิธัวเนีย    lt
+Litvanya       lt
+Литва     lt
+立陶宛      lt
+立陶宛      lt
+Luxembourg     lu
+Luxenburg      lu
+لوكسمبورغ     lu
+Lüksemburq    lu
+Люксэмбург   lu
+Люксембург   lu
+Luksemburg     lu
+Luxemburg      lu
+Lucembursko    lu
+Luxemburg      lu
+Λουξεμβούργο       lu
+Luksemburgo    lu
+Luxemburgo     lu
+Luksemburg     lu
+Luxemburg      lu
+لوگزامبورگ   lu
+Luxemburg      lu
+Luksemborg     lu
+לוקסמבורג     lu
+Luksemburg     lu
+Luxemburg      lu
+Lúxemborg   lu
+Lussemburgo    lu
+ルクセンブルグ  lu
+룩셈부르크        lu
+ລັກແຊມເບີກ lu
+Liuksemburgas  lu
+Luksemburga    lu
+Lussemburgu    lu
+Luxemburg      lu
+Luksemburg     lu
+Luxemburgo     lu
+Luxemburgo     lu
+Luxemburg      lu
+Люксембург   lu
+Luxemburg      lu
+Luksemburg     lu
+Luksemburg     lu
+I-Luxembourg   lu
+Luxemburg      lu
+Äìºõ§À¡÷ì     lu
+ลักเซมเบอร์ก   lu
+Lüksemburg    lu
+Люксембург   lu
+Lucsimbork     lu
+卢森堡      lu
+盧森堡      lu
+Latvia lv
+لاتفيا   lv
+Latviya        lv
+Латвія   lv
+Латвия   lv
+Latvija        lv
+Lotyšsko      lv
+Letland        lv
+Lettland       lv
+Λιθουανία     lv
+Latvio lv
+Letonia        lv
+Läti  lv
+لاتویا   lv
+Lettonie       lv
+לטביה     lv
+Latvija        lv
+Lettország    lv
+Léttland    lv
+Lettonia       lv
+ラトビア   lv
+라트비아   lv
+ລັດເວີຍ  lv
+Latvija        lv
+Latvija        lv
+Latvja lv
+Letland        lv
+Łotwa lv
+Letónia       lv
+Латвия   lv
+Lotyšsko      lv
+Latvija        lv
+Latvija        lv
+I-Latvia       lv
+Lettland       lv
+ÄðŢ¡   lv
+ลัธเวีย  lv
+Litvanya       lv
+Латвія   lv
+Lativia        lv
+拉脱维亚   lv
+拉脫維亞   lv
+Libya  ly
+Libië ly
+ليبيا     ly
+Лівія     ly
+Либия     ly
+Libija ly
+Líbia ly
+Lýbie ly
+Libyen ly
+Libyen ly
+Λιβύη     ly
+Libia  ly
+Liibüa        ly
+Libia  ly
+لیبی       ly
+Lybie  ly
+לוב ly
+Líbia ly
+Libia  ly
+リビア      ly
+ລິຊາ   ly
+Libija ly
+Libië ly
+Libia  ly
+Líbia ly
+Líbia ly
+Ливия     ly
+Lýbia ly
+Libija ly
+I-Libya        ly
+Libyen ly
+Ä¢À¢Â¡   ly
+Лівія     ly
+利比亚      ly
+利比亞      ly
+Morocco        ma
+Morokko        ma
+المغرب   ma
+Марока   ma
+Мароко   ma
+Maroko ma
+Marroc ma
+Maroko ma
+Marokko        ma
+Marokko        ma
+Μαρόκο   ma
+Moroko ma
+Marruecos      ma
+Maroko ma
+Maroko ma
+مراکش     ma
+Marokko        ma
+Marokko        ma
+Maroc  ma
+מרוקו     ma
+Maroko ma
+Marokkó       ma
+Marocco        ma
+モロッコ   ma
+모로코      ma
+ເມົາລິ     ma
+Marokas        ma
+Marokk ma
+Marokko        ma
+Marokko        ma
+Marokko        ma
+Maroko ma
+Marrocos       ma
+Marrocos       ma
+Maroc  ma
+Марокко ma
+Maroko ma
+Maroko ma
+I-Morocco      ma
+Marocko        ma
+§Á¡Ã¡§¸¡       ma
+Марокко ma
+Marok  ma
+摩洛哥      ma
+摩洛哥      ma
+Monaco mc
+Манака   mc
+Монако   mc
+Monako mc
+Mónaco        mc
+ເມົາລິ     mc
+Mônaco        mc
+Monako mc
+Monako mc
+摩纳哥      mc
+Moldova        md
+Малдова md
+Молдова md
+Moldavia       md
+Moldavia       md
+ສະໂລວັກ  md
+Moldávsko     md
+Moldavien      md
+摩尔多瓦   md
+Madagascar     mg
+Мадагаскар   mg
+Мадагаскар   mg
+Madagaskar     mg
+Madagaszkár   mg
+ຄາສະບາລ - K      mg
+Madagaskar     mg
+Madagaskar     mg
+Madagaskar     mg
+马达加斯加        mg
+Marshall Islands       mh
+Маршаллавы астравы    mh
+Маршалови Острови      mh
+Maršalova ostrva      mh
+Marshall-øerne        mh
+Islas Marshall mh
+Marshall Irlak mh
+Marshall-szigetek      mh
+ລາດສະອານາຈັກໄທຍ  mh
+Ilhas Marshall mh
+Maršálove ostrovy    mh
+Marshallovi otoki      mh
+Marshallöarna mh
+马绍尔群岛        mh
+Macedonia      mk
+Makedoniese    mk
+مقدونيا mk
+Makedonya      mk
+Македонія     mk
+Македония     mk
+Makedonia      mk
+Makedonija     mk
+Macedònia     mk
+Makedonie      mk
+Makedonien     mk
+Makedonien     mk
+Μακεδονία     mk
+Macedonio      mk
+Makedoonia     mk
+Mazedonia      mk
+مقدونیه mk
+Makedonia      mk
+Macédoine     mk
+מקדוניה mk
+Makedonija     mk
+Macedónia     mk
+Masedonia      mk
+Makedónía        mk
+マケドニア語     mk
+마케도니아        mk
+ມາເຊໂດເນີຍ mk
+Makedonija     mk
+Maķedonija    mk
+Maċedonja     mk
+Makedonia      mk
+Macedonië     mk
+Makedonia      mk
+Macedònian    mk
+Macedónia     mk
+Macedônia     mk
+Македония     mk
+Macedónsky    mk
+Makedonija     mk
+Makedonija     mk
+I-Macedonia    mk
+Makedonien     mk
+Á¡º¢§¼¡É¢Â¡ mk
+มาเซโดเนีย mk
+Makedonya      mk
+Македонія     mk
+Masedonia      mk
+马其顿      mk
+馬其頓      mk
+Mali   ml
+Малі       ml
+Мали       ml
+ຈົດຫມາຍ  ml
+马里 ml
+Myanmar        mm
+М'янма    mm
+Майнмар mm
+Mjanmar        mm
+Burma  mm
+Birmania       mm
+ຕົວຮງກພີື້ນທີ່ທຳງານ - K  mm
+Mjanmar        mm
+缅甸 mm
+Mongolia       mn
+Манголія       mn
+Монголия       mn
+Mongolija      mn
+Mongoliet      mn
+Mongólia      mn
+ລອກອິນ     mn
+Mongólia      mn
+Mongolsko      mn
+Mongolija      mn
+Mongoliet      mn
+蒙古 mn
+Macau  mo
+Макао     mo
+Makau  mo
+Macao  mo
+Makaó mo
+ມອລຕາ        mo
+Makao  mo
+Macao  mo
+澳门 mo
+Martinique     mq
+Мартиника     mq
+Martinik       mq
+Martinica      mq
+ເມົາລິ     mq
+Martinik       mq
+马提尼克岛        mq
+Mauritania     mr
+Маўрытанія   mr
+Мавритания   mr
+Mauritanija    mr
+Mauretanien    mr
+Mauritánia    mr
+ລິທົວເນີຍ    mr
+Mauritânia    mr
+Mavretanija    mr
+Mauretanien    mr
+毛里塔尼亚        mr
+Montserrat     ms
+Монсерат       ms
+Monsera        ms
+ຈໍພາບ        ms
+蒙特塞拉特岛     ms
+Malta  mt
+مالطة     mt
+Мальта   mt
+Малта     mt
+Μάλτα     mt
+Malto  mt
+مالت       mt
+Malte  mt
+מלטה       mt
+Málta mt
+マルタ      mt
+왈타 mt
+ມອລຕາ        mt
+Мальта   mt
+I-Malta        mt
+Á¡ø¼¡     mt
+มอลตา        mt
+Мальта   mt
+Male   mt
+马耳他      mt
+馬爾他      mt
+Mauritius      mu
+Маўрыцы mu
+Маурици mu
+Mauricijus     mu
+Mauricio       mu
+ພາທິຊັ້ນ       mu
+Maurícius     mu
+Mavricij       mu
+毛里求斯   mu
+Maldives       mv
+Мальдывы       mv
+Малдиви mv
+Maldivi        mv
+Maldiverne     mv
+Maldivas       mv
+Maldív-szigetek       mv
+ມັລດິສ     mv
+Maldivas       mv
+Maldiv mv
+Maldiverna     mv
+马尔代夫   mv
+Malawi mw
+Малаві   mw
+Малави   mw
+Malavi mw
+ມອລຕາ        mw
+马拉维      mw
+Mexico mx
+Meksiko        mx
+المكسيك mx
+Meksika        mx
+Мэксыка mx
+Мексико mx
+Mec'hiko       mx
+Meksiko        mx
+Mèxic mx
+Mexiko mx
+Mexiko mx
+Μεξικό   mx
+Meksikujo      mx
+México        mx
+Mehhiko        mx
+Mexiko mx
+مکزیک     mx
+Meksiko        mx
+Meksiko        mx
+Mexique        mx
+México        mx
+מקסיקו   mx
+Meksiko        mx
+Mexikó        mx
+Meksiko        mx
+Mexíkó   mx
+Messico        mx
+メキシコ   mx
+멕시코      mx
+ເມັກຊີໂກ       mx
+Meksika        mx
+Meksika        mx
+Messiku        mx
+Mèxic mx
+Meksyk mx
+México        mx
+México        mx
+Mexic  mx
+Мексика mx
+Mexiko mx
+Mehika mx
+Meksiko        mx
+I-Mexico       mx
+¦Á캢§¸¡       mx
+เม็กซิโก       mx
+Meksika        mx
+Мексика mx
+Mê hi cô     mx
+Mecsike        mx
+墨西哥      mx
+墨西哥      mx
+Malaysia       my
+Малайзыя       my
+Малайзия       my
+Malezija       my
+Malasia        my
+Malasia        my
+Malajzia       my
+ມອລຕາ        my
+Malásia       my
+Malajzia       my
+Malezija       my
+马来西亚   my
+Mozambique     mz
+Мазамбік       mz
+Мозамбик       mz
+Mozambik       mz
+Mozambik       mz
+ຫນ່ວຍຄວາມຈຳ      mz
+Moçambique    mz
+Mozambik       mz
+Mozambik       mz
+Moçambique    mz
+莫桑比克   mz
+Namibia        na
+Намібія na
+Намибия na
+Namibija       na
+Namíbia       na
+ຈາໄມກາ     na
+Namíbia       na
+Namíbia       na
+Namibija       na
+纳米比亚   na
+New Caledonia  nc
+Нова Каледония    nc
+Nova Kaledonija        nc
+Ny Caledonien  nc
+Nueva Caledonia        nc
+Kaledonia Berria       nc
+Új-Kaledónia nc
+ມາເຊໂດເນີຍ nc
+Nova Caledônia        nc
+Nová Kaledónia       nc
+Nova Kaledonija        nc
+Nya Caledonien nc
+新喀里多尼亚     nc
+Niger  ne
+Нігер     ne
+Нигер     ne
+ຕົວຮງກພື້ນທີ່ທຳງານ ne
+Nigéria       ne
+Nigéria       ne
+尼日尔      ne
+Norfolk Island nf
+Остров Норфолк    nf
+Norfolk ostrvo nf
+Norfolk-øerne (Australien)    nf
+Isla Norfolk   nf
+Norfok Irla    nf
+Norfolk-szigetek       nf
+ໂປແລນ        nf
+Ilhas Norfolk  nf
+Ostrov Norfolk nf
+Otok Norfolk   nf
+Norfolkön     nf
+诺福克岛   nf
+Nigeria        ng
+Нігерыя ng
+Нигерия ng
+Nigerija       ng
+Nigéria       ng
+ບັນແກເລີຍ    ng
+Nigéria       ng
+Nigéria       ng
+Nigerija       ng
+尼日利亚   ng
+Nicaragua      ni
+Nikaragua      ni
+نيكاراغوا     ni
+Нікарагуа     ni
+Никарагуа     ni
+Nikaragva      ni
+Nikaragua      ni
+Νικαράγουα   ni
+Nikaraagua     ni
+نیکاراگویه   ni
+Nikaragua      ni
+Nikaragua      ni
+ניקרגווה       ni
+ニカラグア        ni
+니카라과   ni
+ປາລາກກວຍ       ni
+Nikaragua      ni
+Nikaragwa      ni
+Nikaragua      ni
+Nicarágua     ni
+Nicarágua     ni
+Никарагуа     ni
+Nikaragua      ni
+Nikaragva      ni
+I-Nicaragua    ni
+¿¢¸Ã¡Ì§Å       ni
+Nikaragua      ni
+Нікарагуа     ni
+尼加拉瓜   ni
+尼加拉瓜   ni
+Netherlands    nl
+Nederland      nl
+هولندا   nl
+Hollandiya     nl
+Галяндыя       nl
+Холандия       nl
+Izelvroioù  nl
+Nizozemska     nl
+Holanda        nl
+Nizozemí      nl
+Holland        nl
+Niederlande    nl
+Κάτω Χώρες    nl
+Nederlando     nl
+Países Bajos  nl
+Holland        nl
+Holanda        nl
+هلند       nl
+Alankomaat     nl
+Háland        nl
+Pays bas       nl
+Países Baixos nl
+הולנד     nl
+Nizozemska     nl
+Hollandia      nl
+Belanda        nl
+Holland        nl
+Paesi Bassi    nl
+オランダ   nl
+네덜란드   nl
+ເນເທີແລນ์    nl
+Olandija       nl
+Nīderlande    nl
+Nederland      nl
+Nederland      nl
+Nederland      nl
+Holanda        nl
+Holandia       nl
+Países Baixos nl
+Holanda        nl
+Olanda nl
+Нидерланды   nl
+Holandsko      nl
+Nizozemska     nl
+Holandija      nl
+I-Netherlands  nl
+Nederländerna nl
+¦¿¾÷Ä¡óР      nl
+เนเธอร์แลนด์   nl
+Hollanda       nl
+Голландія     nl
+Hà Lan        nl
+荷兰 nl
+荷蘭 nl
+Norway no
+Noorweë       no
+النرويج no
+Norveç        no
+Нарвэгія       no
+Норвегия       no
+Norvegia       no
+Norveška      no
+Noruega        no
+Norsko no
+Norge  no
+Norwegen       no
+Νορβηγία       no
+Norvegio       no
+Noruega        no
+Norra  no
+Norvegia       no
+نروژ       no
+Norja  no
+Norra  no
+Norvège       no
+Noruega        no
+נורבגיה no
+Norveška      no
+Norvégia      no
+Norwegia       no
+Noregur        no
+Norvegia       no
+ノルウェー        no
+노르웨이   no
+ນໍເວ   no
+Norvegija      no
+Norvēģija    no
+Norveġja      no
+Norge  no
+Noorwegen      no
+Noreg  no
+Noruega        no
+Norwegia       no
+Noruega        no
+Noruega        no
+Norvegia       no
+Норвегия       no
+Nórsko        no
+Norveška      no
+Norveška      no
+I-Norway       no
+Norge  no
+¿¡÷§Å     no
+นอร์เวย์       no
+Norveç        no
+Норвегія       no
+Na uy  no
+挪威 no
+挪威 no
+Nepal  np
+Нэпал     np
+Непал     np
+Nepál np
+ເວນດາ        np
+尼泊尔      np
+Nauru  nr
+Науру     nr
+Науру     nr
+Naurú nr
+ປາລາກກວຍ       nr
+瑙鲁 nr
+Niue   nu
+Ниуе       nu
+ເນ໊ຕ   nu
+纽埃岛      nu
+New Zealand    nz
+Nuwe Seeland   nz
+نيوزيلاندا   nz
+Yeni Zellandiya        nz
+Новая Зэляндыя    nz
+Нова Зеландия      nz
+Zeland nevez   nz
+Novi Zeland    nz
+Nova Zelanda   nz
+Nový Zéland  nz
+Neuseeland     nz
+Νέα Ζηλανδία        nz
+Novzelando     nz
+Nueva Zelanda  nz
+Uus-Meremaa    nz
+Zelanda Berria nz
+زلاندنو nz
+Uusi-Seelanti  nz
+Nýsæland     nz
+Nouvelle Zélande      nz
+Nova Celandia  nz
+ניו זילנד      nz
+Novi Zeland    nz
+Új-Zéland    nz
+Selandia Baru  nz
+Nýja Sjáland     nz
+Nuova Zelanda  nz
+ニュージーランド       nz
+뉴질랜드   nz
+ນີວຊີແລນ       nz
+Naujoji Zelandija      nz
+JaunZēlande   nz
+Nieuw Zeeland  nz
+Navera Zelanda nz
+Nowa Zelandia  nz
+Nova Zelândia nz
+Nova Zelândia nz
+Noua Zeelandă nz
+Новая Зеландия    nz
+Nový Zéland  nz
+Nova Zelandija nz
+Novi Zeland    nz
+I-New Zealand  nz
+Nya Zeeland    nz
+¿¢äº¢Ä¡óР    nz
+นิวซีแลนด์ nz
+Yeni Zelanda   nz
+Нова Зеландія      nz
+Nouve Zelande  nz
+新西兰      nz
+紐西蘭      nz
+Oman   om
+عُمان     om
+Аман       om
+Оман       om
+Omán  om
+Ομάν       om
+Omano  om
+Omán  om
+Omaan  om
+عمان       om
+עומן       om
+Omán  om
+オマーン   om
+오만 om
+ເຍີຍລະມັນ    om
+Omanas om
+Omã   om
+Оман       om
+Omán  om
+I-Oman om
+µÁý om
+Umman  om
+Оман       om
+ Oman  om
+阿曼 om
+阿曼 om
+Panama pa
+بنما       pa
+Панама   pa
+Панама   pa
+Panamà        pa
+Παναμάς pa
+Panamo pa
+Panamá        pa
+پاناما   pa
+Panamá        pa
+פנמה       pa
+パナマ      pa
+파나마      pa
+ປານາມາ     pa
+Panamá        pa
+Panamá        pa
+Панама   pa
+I-Panama       pa
+ÀÉ¡Á¡     pa
+ปานามา     pa
+Панама   pa
+巴拿马      pa
+巴拿馬      pa
+Peru   pe
+البيرو   pe
+Пэру       pe
+Перу       pe
+Perou  pe
+Perú  pe
+Περού     pe
+Peruo  pe
+Perú  pe
+Peruu  pe
+پرو pe
+Pérou pe
+Perú  pe
+פרו pe
+Perú        pe
+Perù  pe
+ペルー      pe
+페루 pe
+ເປລູ   pe
+Perū  pe
+Pero   pe
+Perú  pe
+Перу       pe
+I-Peru pe
+¦ÀÕ pe
+เปรู   pe
+Перу       pe
+Perou  pe
+秘鲁 pe
+秘魯 pe
+French Polynesia       pf
+Француская Палінэзія        pf
+Френска Полинезия      pf
+Francuska Polinezija   pf
+Fransk Polynesien      pf
+Polinesia francesa     pf
+Polinesia Frantziarra  pf
+Francia-Polinézia     pf
+ຝຣັ່ງເສດ       pf
+Polinésia Francesa    pf
+Francúzska Polynézia pf
+Francoska Polinezija   pf
+Franska Polynesien     pf
+法属波利尼西亚  pf
+Papua New Guinea       pg
+Папуа–Новая Гвінэя   pg
+Папуа и Нова Гвинея    pg
+Papua Nova Gvineja     pg
+Papua Nueva Guinea     pg
+Papua Ginea Berria     pg
+Pápua Új-Guinea      pg
+ເທົາອ່ອນ       pg
+Nova Guiné Papua      pg
+Papua Nová Guinea     pg
+Papua Nova Gvineja     pg
+Papua Nya Guinea       pg
+巴布亚新几内亚  pg
+Philippines    ph
+Філіпіны       ph
+Филипини       ph
+Filipini       ph
+Filippinerne   ph
+Filipinas      ph
+Filipinak      ph
+Fülöp-szigetek       ph
+ອາລະປະໂຫຍດ ph
+Filipinas      ph
+Filipíny      ph
+Filipini       ph
+Filippinerna   ph
+菲律宾      ph
+Pakistan       pk
+Пакістан       pk
+Пакистан       pk
+Pakisztán     pk
+ລງບ      pk
+Paquistão     pk
+巴基斯坦   pk
+Poland pl
+Poolland       pl
+بولندا   pl
+Polşa pl
+Польшча pl
+Полша     pl
+Polonia        pl
+Poljska        pl
+Polònia       pl
+Polsko pl
+Polen  pl
+Polen  pl
+Πολωνία pl
+Pollando       pl
+Polonia        pl
+Poola  pl
+Polonia        pl
+لهستان   pl
+Puola  pl
+Pólland       pl
+Pologne        pl
+Polonia        pl
+פולין     pl
+Poljska        pl
+Lengyelország pl
+Polandia       pl
+Pólland     pl
+Polonia        pl
+ポーランド        pl
+폴란드      pl
+ໂປແລນ        pl
+Lenkija        pl
+Polija pl
+Polonja        pl
+Polen  pl
+Polen  pl
+Polen  pl
+Polònia       pl
+Polska pl
+Polónia       pl
+Polônia       pl
+Polonia        pl
+Польша   pl
+Poľsko        pl
+Poljska        pl
+Poljska        pl
+I-Poland       pl
+Polen  pl
+§À¡Ä¡óРpl
+โปแลนด์  pl
+Polonya        pl
+Польща   pl
+Pholandi       pl
+Ba Lan         pl
+Pologne        pl
+波兰 pl
+波蘭 pl
+Saint Pierre and Miquelon      pm
+Sveti Pjer i Migelon   pm
+Saint Pierre og Miquelon       pm
+Saint Pierre y Miquelon        pm
+Saint Pierre eta Miquelon      pm
+Saint Pierre és Miquelon      pm
+Saint Pierre e Miquelon        pm
+Saint Pierre a Miquelon        pm
+Sveti Pierre in Miquelon       pm
+Saint Pierre och Miquelon      pm
+圣皮埃尔和密克隆岛    pm
+Pitcairn       pn
+Pitkern        pn
+ລງບ      pn
+Puerto Rico    pr
+Пуерто Рико  pr
+Portoriko      pr
+ໂປຣໂຕຄອນ       pr
+Porto Rico     pr
+Portoriko      pr
+波多尼各   pr
+Palestinian Territory  ps
+Palesteinse Gebied     ps
+السلطة الفلسطينية      ps
+Палестынская тэрыторыя    ps
+Палестина     ps
+Palestinska teritorija ps
+Territori Palestí     ps
+Palestinské území   ps
+Palæstinensiske selvstyreområder     ps
+Palästinensisches Gebiet      ps
+Παλαιστίνη   ps
+Palestinaj teritorioj  ps
+Territorio palestino   ps
+Palestiina territoorium        ps
+Palestina      ps
+فلسطین   ps
+Palestiinalaisalue     ps
+Palestinensiska økið ps
+Territoire palestinien ps
+השטחים הפלסטיניים      ps
+Palestinski teritorij  ps
+Palesztin területek   ps
+Territori palestinesi  ps
+パレスチナ自治区       ps
+팔레스타인 자치구      ps
+ການພິມຜິດພາດ   ps
+Palestinos teritorija  ps
+Palestina      ps
+Palestinske territorier        ps
+Palestijns territorium ps
+Palestinske territorium        ps
+Bohwa bja Palestina    ps
+Palestyna      ps
+Território Palestiniano       ps
+Território Palestino  ps
+Teritoriul Palestinian ps
+Палестинские территории  ps
+Palestínske územia   ps
+Palestinski teritorij  ps
+I-Palestinian Territory        ps
+Palestina      ps
+À¡Äо£É ¬¨½Âõ      ps
+Filistin Bölgesi      ps
+Палестинська територія    ps
+Mukano wa maphalesitina        ps
+Palestene      ps
+Umhlaba wePalestina    ps
+巴勒斯坦地区     ps
+巴勒斯坦領地     ps
+Indawo yama-Phalesitina        ps
+Portugal       pt
+البرتغال       pt
+Portuqaliya    pt
+Партугалія   pt
+Португалия   pt
+Portugalsko    pt
+Πορτογαλία   pt
+Portugalo      pt
+پرتغال   pt
+Portugali      pt
+פורטוגל pt
+Portugália    pt
+Portúgal    pt
+Portogallo     pt
+ポルトガル        pt
+포르투갈   pt
+ໂປຣຕຸເກສ       pt
+Portugalija    pt
+Portugāle     pt
+Portugall      pt
+Portugalia     pt
+Portugalia     pt
+Португалия   pt
+Portugalsko    pt
+Portugalska    pt
+I-Portugal     pt
+§À¡÷òиø       pt
+โปรตุเกส       pt
+Portekiz       pt
+Португалія   pt
+BềEĐào Nha         pt
+葡萄牙      pt
+葡萄牙      pt
+Palau  pw
+Палау     pw
+Палау     pw
+ມອລຕາ        pw
+帕劳群岛   pw
+Paraguay       py
+Paraguaai      py
+باراغواي       py
+Paraqvay       py
+Парагвай       py
+Парагвай       py
+Paragvaj       py
+Paraguai       py
+Παραγουάη     py
+Paragvajo      py
+Paraguai       py
+پاراگویه       py
+Paraguai       py
+פרגואי   py
+Paragvaj       py
+パラグアイ        py
+파라과이   py
+ປາລາກກວຍ       py
+Paragvajus     py
+Paragvaja      py
+Paragwaj       py
+Paraguai       py
+Paragwaj       py
+Paraguai       py
+Paraguai       py
+Paraguai       py
+Парагвай       py
+Portugalsko    py
+Paragvaj       py
+Paragvaj       py
+I-Paraguay     py
+Àá̧Š  py
+ปารากวัย       py
+Парагвай       py
+Paragway       py
+巴拉圭      py
+巴拉圭      py
+Qatar  qa
+قطر qa
+Катар     qa
+Катар     qa
+Katar  qa
+Katar  qa
+Katar  qa
+Κατάρ     qa
+Kataro qa
+Katar  qa
+قطر qa
+Katar  qa
+קטר qa
+Katar  qa
+Katar  qa
+カタール   qa
+카타르      qa
+ມອລຕາ        qa
+Katar  qa
+Катар     qa
+Katar  qa
+Katar  qa
+I-Qatar        qa
+¸¾¡÷       qa
+Katar  qa
+Катар     qa
+卡塔尔      qa
+卡達 qa
+Romania        ro
+Romenië       ro
+رومانيا ro
+Rumıniya      ro
+Румынія ro
+Румъния ro
+Roumania       ro
+Rumunija       ro
+Rumunsko       ro
+Rumænien      ro
+Rumänien      ro
+Ρουμανία       ro
+Rumanio        ro
+Rumanía       ro
+Rumeenia       ro
+Errumania      ro
+رومانی   ro
+Rumenia        ro
+Roumanie       ro
+Romanía       ro
+רומניה   ro
+Rumunjska      ro
+Románia       ro
+Rumania        ro
+Rúmenía  ro
+ルーマニア        ro
+루마니아   ro
+ໂລມາເນີຍ       ro
+Rumunija       ro
+Rumānija      ro
+Rumanija       ro
+Roemenië      ro
+Rumunia        ro
+Roménia       ro
+Romênia       ro
+Румыния ro
+Rumunsko       ro
+Romunija       ro
+Rumunija       ro
+I-Romania      ro
+Rumänien      ro
+Õ§Áɢ¡ ro
+โรมาเนีย       ro
+Romanya        ro
+Румунія ro
+Ru ma ni       ro
+Roumaneye      ro
+罗马尼亚   ro
+羅馬尼亞   ro
+Russia ru
+Rusland        ru
+روسيا     ru
+Rusiya ru
+Расея     ru
+Русия     ru
+Rusia  ru
+Rusija ru
+Rússia        ru
+Rusko  ru
+Rusland        ru
+Russland       ru
+Ρωσία     ru
+Ruslando       ru
+Rusia  ru
+Venemaa        ru
+Errusia        ru
+روسیه     ru
+Venäjä       ru
+Russland       ru
+Russie ru
+Rusia  ru
+רוסיה     ru
+Rusija ru
+Oroszország   ru
+Rusia  ru
+Rússland    ru
+ロシア      ru
+러시아      ru
+ລັດເຊີຍ  ru
+Rusija ru
+Krievija       ru
+Russja ru
+Russland       ru
+Rusland        ru
+Russland       ru
+Rosja  ru
+Rússia        ru
+Rússia        ru
+Rusia  ru
+Россия   ru
+Rusko  ru
+Rusija ru
+Rusija ru
+I-Russia       ru
+Ryssland       ru
+ú¢Â¡     ru
+รัสเซีย  ru
+Rusya  ru
+Росія     ru
+Rashia ru
+Nga    ru
+Russeye        ru
+Rashiya        ru
+俄罗斯      ru
+俄羅斯      ru
+Rwanda rw
+Руанда   rw
+Руанда   rw
+Ruanda rw
+Ruanda rw
+Ruanda rw
+ແພນດ້າ     rw
+Ruanda rw
+Ruanda rw
+卢旺达      rw
+Saudi Arabia   sa
+Saudi Arabië  sa
+السعودية       sa
+Саудаўская Арабія      sa
+Саудитска Арабия        sa
+Saudijska Arabija      sa
+Aràbia Saudí sa
+Saúdská Arábie      sa
+Saudi Arabien  sa
+Saudi-Arabien  sa
+Σαουδική Αραβία  sa
+Saŭdi-Arabio  sa
+Arabia Saudí  sa
+Saudi Araabia  sa
+عربستان سعودی      sa
+Saudi-Arabia   sa
+Arabie Saoudite        sa
+ערב הסעודית  sa
+Saudijska Arabija      sa
+Szaúd-Arábia sa
+Arabia saudita sa
+サウジアラビア  sa
+사우디 아라비아 sa
+ອາລະບິກ  sa
+Saudi Arabija  sa
+Għarabja Sawdita      sa
+Saudi-Arabia   sa
+Saudi-Arabië  sa
+Saudi-Arabia   sa
+Arabia Saudyjska       sa
+Arábia Saudita        sa
+Arábia Saudita        sa
+Arabia Saudită        sa
+Саудовская Аравия      sa
+Saudská arábia       sa
+Saudova Arabija        sa
+I-Saudi Arabia sa
+Saudiarabien   sa
+º×¾¢ «§ÃÀ¢Â¡        sa
+Suudi Arabistan        sa
+Саудівська Аравія      sa
+沙特阿拉伯        sa
+沙烏地阿拉伯     sa
+Solomon Islands        sb
+Саламонавы астравы    sb
+Соломонови Острови    sb
+Solomonska ostrva      sb
+Salomon-øerne sb
+Islas Salomón sb
+Solomon Irlak  sb
+Salamon-szigetek       sb
+ສະໂລວະເນີຍ sb
+Ilhas Salomão sb
+Šalamúnove ostrovy   sb
+Solomonovi otoki       sb
+Salomonöarna  sb
+所罗门群岛        sb
+Seychelles     sc
+Сэйшэлы sc
+Сейшелски острови      sc
+Sejšeli       sc
+Seychellerne   sc
+ເຊລ      sc
+Sejšeli       sc
+Seychellerna   sc
+塞舌尔      sc
+Sudan  sd
+السودان sd
+Судан     sd
+Судан     sd
+Sudán sd
+Σουδάν   sd
+Sudano sd
+Sudán sd
+Sudaan sd
+سودان     sd
+Sudania        sd
+Soudan sd
+סודן       sd
+Szudán        sd
+Súdan       sd
+スーダン   sd
+수단 sd
+ຊູດານ        sd
+Sudanas        sd
+Sudāna        sd
+Sudão sd
+Sudão sd
+Судан     sd
+Sudán sd
+I-Sudan        sd
+ݼ¡ý       sd
+ซูดาน        sd
+Судан     sd
+Sudani sd
+Soudan sd
+苏丹 sd
+蘇丹 sd
+Sweden se
+Swede  se
+السويد   se
+İsveç        se
+Швэцыя   se
+Швеция   se
+Sveden se
+Švedska       se
+Suècia        se
+Švédsko      se
+Sverige        se
+Schweden       se
+Σουηδία se
+Svedio se
+Suecia se
+Rootsi se
+Suedia se
+سوید       se
+Ruotsi se
+Svøriki       se
+Suède se
+Suecia se
+שבדיה     se
+Švedska       se
+Svédország   se
+Swedia se
+Svíþjóð    se
+Svezia se
+スウェーデン     se
+스웨덴      se
+ສະວີເດນ  se
+Švedija       se
+Zviedrija      se
+Svezja se
+Sverige        se
+Zweden se
+Sverige        se
+Suècia        se
+Szwecja        se
+Suécia        se
+Suécia        se
+Suedia se
+Швеция   se
+Švédsko      se
+Švedska       se
+Švedska       se
+I-Sweden       se
+Sverige        se
+ÍÅ£¼ý     se
+สวีเดน     se
+İsveç        se
+Швеція   se
+Swidene        se
+Thuỵ Điển         se
+瑞典 se
+瑞典 se
+Singapore      sg
+Сынгапур       sg
+Сингапур       sg
+Singapur       sg
+Singapur       sg
+Szingapúr     sg
+ໂຊນາ   sg
+Singapura      sg
+Singapur       sg
+Singapur       sg
+新加坡      sg
+Saint Helena   sh
+Свети Елена  sh
+Sveta Helena   sh
+St. Helena     sh
+Santa Helena   sh
+Szent Heléna  sh
+ຫົວເລື່ອງ    sh
+Santa Helena   sh
+Svätá Helena sh
+Sveta Helena   sh
+圣赫勒拿岛        sh
+Slovenia       si
+Slovenië      si
+سلوفينيا       si
+Sloveniya      si
+Славенія       si
+Словения       si
+Slovenija      si
+Eslovenia      si
+Slovinsko      si
+Slovenien      si
+Slowenien      si
+Σλοβενία       si
+Slovenio       si
+Eslovenia      si
+Sloveenia      si
+Eslovenia      si
+اسلوانی si
+Slovénie      si
+Eslovenia      si
+סלובניה si
+Slovenija      si
+Szlovénia     si
+Slóvenía si
+スロベニア        si
+슬로베니아        si
+ສະໂລວະເນີຍ si
+Slovėnija     si
+Slovēnija     si
+Slovenja       si
+Slovenië      si
+Eslovenia      si
+Słowenia      si
+Eslovénia     si
+Eslovênia     si
+Словения       si
+Slovinsko      si
+Slovenija      si
+Slovenija      si
+I-Slovenia     si
+Slovenien      si
+ͧġŢɢ¡   si
+สโลเวเนีย    si
+Slovenya       si
+Словенія       si
+斯洛文尼亚        si
+斯洛維尼亞        si
+Slovakia       sk
+Slovakië      sk
+سلوفاكيا       sk
+Slovakiya      sk
+Славакія       sk
+Словакия       sk
+Slovačka      sk
+Eslovaquia     sk
+Slovensko      sk
+Slovakiet      sk
+Slowakien      sk
+Σλοβακία       sk
+Slovakujo      sk
+Eslovaquia     sk
+Slovakkia      sk
+Eslovakia      sk
+اسلواکی sk
+Slovaquie      sk
+סלובקיה sk
+Slovačka      sk
+Szlovákia     sk
+Slóvakía sk
+Slovacchia     sk
+スロバキア        sk
+슬로바키아        sk
+ສະໂລວັກ  sk
+Slovakija      sk
+Slovākija     sk
+Slovakja       sk
+Slovakije      sk
+Słowacja      sk
+Eslováquia    sk
+Eslováquia    sk
+Slovacia       sk
+Словакия       sk
+Slovensko      sk
+Slovaška      sk
+I-Slovakia     sk
+Slovakien      sk
+ͧġš츢   sk
+สโลวัค     sk
+Slovakya       sk
+Словакія       sk
+斯洛伐克   sk
+斯洛伐克   sk
+San Marino     sm
+Сан-Марына    sm
+Сан Марино    sm
+ໂຊນາ   sm
+圣马力诺   sm
+Senegal        sn
+Сэнэгал sn
+Сенегал sn
+Szenegál      sn
+ທົ່ວໄປ     sn
+塞内加尔   sn
+Somalia        so
+Somalië       so
+صومال     so
+Самалі   so
+Сомалия so
+Somalija       so
+Somàlia       so
+Somálsko      so
+Σομαλία so
+Somalio        so
+Somaalia       so
+سومالی   so
+Somalie        so
+סומליה   so
+Somalija       so
+Szomália      so
+ソマリア   so
+소말리아   so
+ໂລມາເນີຍ       so
+Somalis        so
+Somalija       so
+Somalie        so
+Somália       so
+Somália       so
+Сомали   so
+Somálsko      so
+Somalija       so
+I-Somalia      so
+§º¡Á¡Ä¢Â¡     so
+Somali so
+Сомалі   so
+Somaleye       so
+索马里      so
+索馬利亞   so
+Suriname       sr
+Сурынам sr
+Суринам sr
+Surinam        sr
+ເຊີເບີຍ  sr
+Surinam        sr
+Surinam        sr
+Surinam        sr
+Sao Tome and Principe  st
+Сан Томе и Приницпе    st
+Sao Tome i Principe    st
+Sao Tomé og Principe  st
+Sao Tome y Príncipe   st
+Sao Tome eta Principe  st
+Sao Tome és Principe  st
+ບໍລິການ  st
+São Tome e Príncipe  st
+Sao Tome a Principe    st
+Sao Tome in Principe   st
+São Tomé och Príncipe       st
+El Salvador    sv
+السلفادور     sv
+Сальвадор     sv
+Ел Салвадор  sv
+Salvador       sv
+Salvador       sv
+Ελ Σαλβαντόρ        sv
+Elsalvadoro    sv
+Salvador       sv
+السالوادور   sv
+Salvador       sv
+O Salvador     sv
+אל סלבדור      sv
+Salvador       sv
+エルサルバドル  sv
+엘살바도르        sv
+ເອລຊັນວາດດໍ      sv
+Salvadoras     sv
+Salvadora      sv
+Salwador       sv
+Salvador       sv
+Сальвадор     sv
+Salvádor      sv
+Salvador       sv
+I-El Salvador  sv
+±ø º¡øÅ§¼¡÷  sv
+เอลซัลวาดอร์   sv
+Ель-Сальвадор      sv
+萨尔瓦多   sv
+薩爾瓦多   sv
+Syria  sy
+Sirië sy
+سوريا     sy
+Сырыя     sy
+Сирия     sy
+Sirija sy
+Síria sy
+Sýrie sy
+Syrien sy
+Syrien sy
+Συρία     sy
+Sirio  sy
+Siria  sy
+Süüria       sy
+Siria  sy
+سوریه     sy
+Syyria sy
+Syrie  sy
+סוריה     sy
+Sirija sy
+Szíria        sy
+Siria  sy
+シリア      sy
+시리아      sy
+ເຊີເບີຍ  sy
+Sirija sy
+Siria  sy
+Syrië sy
+Síria sy
+Síria sy
+Siria  sy
+Сирия     sy
+Sýria sy
+Sirija sy
+I-Syria        sy
+Syrien sy
+º¢Ã¢Â¡   sy
+Suriye sy
+Сирія     sy
+叙利亚      sy
+敘利亞      sy
+Swaziland      sz
+Свазылэнд     sz
+Свазиленд     sz
+Svazilend      sz
+Swazilandia    sz
+Swazilandia    sz
+Szváziföld   sz
+ລາດສະນາຈັກໄທຍ        sz
+Suécia        sz
+Swazijsko      sz
+Svazi  sz
+斯威士兰   sz
+Turks and Caicos Islands       tc
+Turks i Kaikos ostrva  tc
+Turks- og Caicosøerne tc
+Islas Turcos y Caicos  tc
+Turks eta Caicos Irlak tc
+Turks- és Caicos-szigetek     tc
+Ilhas Caicos e Turca   tc
+Turks a Caicos ostrovy tc
+Otoka Turks in Caicos  tc
+Turks- och Caicosöarna        tc
+特克斯和凯科斯群岛    tc
+Chad   td
+Чад td
+Чад td
+Čad   td
+Tchad  td
+Txad   td
+Csád  td
+ເກມໄພ່     td
+Čad   td
+Čad   td
+Tchad  td
+乍得 td
+Togo   tg
+Тога       tg
+Того       tg
+ຂອງເລ່ນສະນຸກ   tg
+多哥 tg
+Thailand       th
+تايلاند th
+Tayland        th
+Тайлянд th
+Тайланд th
+Tajland        th
+Tailàndia     th
+Thajsko        th
+Ταϊλάνδη       th
+Tajlando       th
+Tailandia      th
+Tai    th
+Thailandia     th
+تایلند   th
+Thaimaa        th
+Tailand        th
+Thaïlande     th
+Tailandia      th
+תאילנד   th
+Tajland        th
+Thaiföld      th
+Tæland      th
+Tailandia      th
+タイ th
+태국 (타이)        th
+ລາດສະນາຈັກໄທຍ        th
+Tailandas      th
+Taivāna       th
+Tajlandja      th
+Tailandia      th
+Tajlandia      th
+Tailândia     th
+Tailândia     th
+Tailanda       th
+Таиланд th
+Thajsko        th
+Tajska th
+Tajland        th
+I-Thailand     th
+¾¡öÄ¡óРth
+ราชอาณาจักรไทย     th
+Tayland        th
+Таїланд th
+Thái Lan      th
+Taylande       th
+泰国 th
+泰國 th
+Tajikistan     tj
+Таджыкістан tj
+Таджикистан tj
+Tadžikistan   tj
+Tajikistán    tj
+Tadzsikisztán tj
+ໃຕ້ຫວັນ  tj
+Tajiquistão   tj
+Tadžikistan   tj
+Tadžikistan   tj
+Tadzjikistan   tj
+塔吉克斯坦        tj
+Tokelau        tk
+Токелао tk
+ເບລາລັສ  tk
+Turkmenistan   tm
+Туркмэністан       tm
+Туркменистан       tm
+Turkmenistán  tm
+Türkmenisztán        tm
+ຕຸລະກີ     tm
+Turquia        tm
+土库曼斯坦        tm
+Tunisia        tn
+Tunisië       tn
+تونس       tn
+Туніс     tn
+Тунис     tn
+Tunis  tn
+Tuníssia      tn
+Tunisko        tn
+Tunesien       tn
+Tunesien       tn
+Τυνησία tn
+Tunisio        tn
+Túnez tn
+Tuneesia       tn
+تونس       tn
+Tunesia        tn
+Tunisie        tn
+תוניסיה tn
+Tunis  tn
+Tunézia       tn
+チュニジア        tn
+튀니지      tn
+ລັດເຊີຍ  tn
+Tunisas        tn
+Tuneżija      tn
+Tunisie        tn
+Tunezja        tn
+Tunísia       tn
+Tunísia       tn
+Тунис     tn
+Tunisko        tn
+Tunizija       tn
+I-Tunisia      tn
+Tunisien       tn
+ÐÉ¢º¢Â¡ tn
+Tunus  tn
+Туніс     tn
+突尼斯      tn
+突尼西亞   tn
+Tonga  to
+Тонга     to
+Тонга     to
+ໂຊນາ   to
+汤加 to
+East Timor     tp
+Источен Тимур      tp
+Istočni Timor tp
+Østtimor      tp
+Timor oriental tp
+Ekialdeko Timor        tp
+Kelet-Timor    tp
+ວັນແລະເວລາ tp
+Timor Leste    tp
+Východný Timor       tp
+Vzhodni Timor  tp
+Östtimor      tp
+东帝汶      tp
+Turkey tr
+Turkeye        tr
+تركيا     tr
+Türkiyə      tr
+Турцыя   tr
+Турция   tr
+Turkia tr
+Turska tr
+Turquia        tr
+Turecko        tr
+Tyrkiet        tr
+Türkei        tr
+Τουρκία tr
+Turkujo        tr
+Turquía       tr
+Türgi tr
+Turkia tr
+ترکیه     tr
+Turkki tr
+Turkaland      tr
+Turquie        tr
+Turquía       tr
+טורקיה   tr
+Turska tr
+Törökország tr
+Turki  tr
+Tyrkland       tr
+Turchia        tr
+トルコ      tr
+터키 tr
+ຕຸລະກີ     tr
+Turkija        tr
+Turcija        tr
+Turkija        tr
+Tyrkia tr
+Turkije        tr
+Tyrkia tr
+Turquia        tr
+Turcja tr
+Turquia        tr
+Turquia        tr
+Turcia tr
+Турция   tr
+Turecko        tr
+Turčija       tr
+Turska tr
+I-Turkey       tr
+Turkiet        tr
+ÐÕ츢     tr
+ตุรกี        tr
+Türkiye       tr
+Туреччина     tr
+ThềENhĩ Kì         tr
+土耳其      tr
+土耳其      tr
+Trinidad and Tobago    tt
+Trinidad en Tobago     tt
+ترينيداد و توباغو       tt
+Trinidad və Tabaqo    tt
+Трынідад і Табага       tt
+Тринидад и Тобаго       tt
+Trinidad ha Tobago     tt
+Trinidad i Tobago      tt
+Trinidad i Tobago      tt
+Trinidad a Tobago      tt
+Trinidad og Tobago     tt
+Trinidad und Tobago    tt
+Τρίνινταντ και Τομπάγκο   tt
+Trinidado kaj Tobago   tt
+Trinidad y Tobago      tt
+Trinidad ja Tobago     tt
+Trinidad eta Tobago    tt
+ترینیداد و تُباگو       tt
+Trinidad ja Tobago     tt
+Trinidad og Tobago     tt
+Trinidad et Tobago     tt
+Trindade e Tobago      tt
+טרינידד וטובגו    tt
+Trinidad i Tobago      tt
+Trinidad és Tobago    tt
+Trinidad dan Tobago    tt
+Trinidad og Tobago     tt
+Trinidad e Tobago      tt
+トリニダードトバコ    tt
+트리니다드토바고       tt
+ຕີນິແດດແລະໂທບາໂກ       tt
+Trinidadas ir Tobagas  tt
+Trinidada un Tobago    tt
+Trinidad u Tobago      tt
+Trinidad og Tobago     tt
+Trinidad en Tobago     tt
+Trinidad og Tobago     tt
+Trinidad le Tobago     tt
+Trinidad e Tobago      tt
+Trinidad i Tobago      tt
+Trinidade e Tobago     tt
+Trinidad e Tobago      tt
+Trinidad şi Tobago    tt
+Тринидад и Тобаго       tt
+Trinidad a Tobago      tt
+Trinidad in Tabago     tt
+Trinidad i Tobago      tt
+I-Trinidad kanye neTobago      tt
+Trinidad och Tobago    tt
+ðâɢ¼¡ð & ¦¼¡À¡§¸¡    tt
+ตรีนิแดดและโทบาโก    tt
+Trinidad veTabago      tt
+Республіка Трінідад та Тобаго        tt
+Trinidad na Tobago     tt
+Trinidad và Tobago    tt
+Trinidad ne Tobago     tt
+特里尼达和多巴哥       tt
+千里達及托貝哥  tt
+Trinidad knaye ne-Tobago       tt
+Tuvalu tv
+Тувалу   tv
+Тували   tv
+ຊູລູ   tv
+图瓦卢      tv
+Taiwan tw
+تايوان   tw
+Tayvan tw
+Тайвань tw
+Тайван   tw
+Tajvan tw
+Ταϊβάν   tw
+Tajvano        tw
+Taiwán        tw
+Taivan tw
+تایوان   tw
+Taivan tw
+Taïwan        tw
+Taiwán        tw
+טיוואן   tw
+Tajvan tw
+Tajvan tw
+台湾 tw
+대만 tw
+ໃຕ້ຫວັນ  tw
+Taivanis       tw
+Taivāna       tw
+Tajwan tw
+Tajwan tw
+Formosa        tw
+Тайвань tw
+Tajvan tw
+Tajvan tw
+I-Taiwan       tw
+¾¡öÅ¡ý   tw
+ได้หวัน  tw
+Tayvan tw
+Тайвань tw
+Đài Loan     tw
+中国台湾   tw
+台灣 tw
+Tanzania, United Republic of   tz
+Злучаная Рэспубліка Танзанія tz
+Танзания       tz
+Tanzanija, Ujedinjena Republika        tz
+Tanzania       tz
+Tanzania, Republica de tz
+Tanazia, Errepublika Batua     tz
+Tanzánia      tz
+ໂດມິນິກັນ    tz
+República da Tanzânia        tz
+Tanzánia, Spojená republika  tz
+Tanzanija, Združena republika tz
+Förenade republiken Tanzania  tz
+坦桑尼亚   tz
+Ukraine        ua
+Ukraïne       ua
+أوكرانيا       ua
+Ukrayna        ua
+Украіна ua
+Украйна ua
+Ukraina        ua
+Ukrajina       ua
+Ucraïna       ua
+Ukrajina       ua
+Ουκρανία       ua
+Ukrainio       ua
+Ucrania        ua
+Ukraina        ua
+Ukrania        ua
+اکراین   ua
+Ukraina        ua
+Ukraina        ua
+Ucraína       ua
+אוקראינה       ua
+Ukrajina       ua
+Ukrajna        ua
+Ukraina        ua
\9akraína  ua
+Ucraina        ua
+ウクライナ        ua
+우크라이나        ua
+ຍູເຄຣນ     ua
+Ukraina        ua
+Ukraina        ua
+Ukranja        ua
+Ukraina        ua
+Oekraïne      ua
+Ukraina        ua
+Ucraina        ua
+Ukraina        ua
+Ucrânia       ua
+Ucrânia       ua
+Ucraina        ua
+Украина ua
+Ukrajina       ua
+Ukrajina       ua
+Ukrajna        ua
+I-Ukraine      ua
+Ukraina        ua
+¯ì¦Ãöý   ua
+ยูเครน     ua
+Ukrayna        ua
+Україна ua
+Ukraina        ua
+Ucrinne        ua
+乌克兰      ua
+烏克蘭      ua
+Uganda ug
+Уганда   ug
+Уганда   ug
+ແພນດ້າ     ug
+乌干达      ug
+United States of America       us
+Vereenigde State van Amerika   us
+الولايات المتحدة الأمريكية     us
+Amerika Birləşmiş Ştatları        us
+Злучаныя Штаты Амэрыкі     us
+САЩ us
+Stadoù-Unanet Amerika       us
+Sjedinjene Američke Države   us
+Estats Units d'Amèrica        us
+Spojené státy americké      us
+USA    us
+USA    us
+Ην. Πολιτείες της Αμερικής       us
+Usono  us
+Estados Unidos de América     us
+Ameerika Ühendriigid  us
+Amerikako Estatu Batuak        us
+ایالات متحده‌ی آمریکا      us
+Yhdysvallat    us
+Sambandsríki Amerika (USA)    us
+États Unis d'Amérique        us
+Estados Unidos de América     us
+ארצות הברית  us
+Sjedinjene Američke Države   us
+Amerikai Egyesült Államok    us
+Amerika Serikat        us
+Bandaríki Norður Ameríku      us
+Stati Uniti d'America  us
+アメリカ   us
+미 합중국  us
+ສະຫະລັດອາເມລິກາ  us
+Jungtinės Amerikos Valstijos  us
+Amerikas Savienotās Valstis   us
+Stati Uniti    us
+USA    us
+Verenigde Staten van Amerika   us
+USA    us
+Estats Units d'Amèrica        us
+Stany Zjednoczone Ameryki      us
+Estados Unidos da América     us
+Estados Unidos us
+Statele Unite ale Americii     us
+Соединенные Штаты Америки       us
+USA    us
+Združene države Amerike      us
+Sjedinjene američke države   us
+I-United States of America     us
+Amerikas förenta stater       us
+³ì¸¢Â «¦Áâ츠¿¡Î¸û   us
+สหรัฐอเมริกา   us
+Amerika Birleşik Devletleri   us
+США us
+mashango o tangananaho a America       us
+Mĩ    us
+United States ye Melika        us
+美利坚合众国     us
+美利堅合眾國     us
+Uruguay        uy
+الأوروغواي   uy
+Uruqvay        uy
+Уругвай uy
+Уругвай uy
+Urugvaj        uy
+Uruguai        uy
+Ουρουγουάη   uy
+Urugvajo       uy
+Uruguai        uy
+اروگویه uy
+Uruguai        uy
+אורוגואי       uy
+Urugvaj        uy
+ウルグアイ        uy
+우루과이   uy
+ອຸລຸກກວຍ       uy
+Urugvajus      uy
+Urugvaja       uy
+Urugwaj        uy
+Uruguai        uy
+Urugwaj        uy
+Uruguai        uy
+Uruguai        uy
+Uruguai        uy
+Уругвай uy
+Uruguaj        uy
+Urugvaj        uy
+Urugvaj        uy
+I-Uruguay      uy
+¯Õ̧Š    uy
+อุรุกวัย       uy
+Уругвай uy
+Ourougway      uy
+乌拉圭      uy
+烏拉圭      uy
+Uzbekistan     uz
+Узбэкістан   uz
+Узбекистан   uz
+Uzbekistán    uz
+Üzbegisztán  uz
+ເດນ່ງນ     uz
+Uzbequistão   uz
+乌兹别克斯坦     uz
+Vatican City   va
+Ватыкан va
+Ватикана       va
+Vatikan        va
+Vatikanstaten  va
+Vaticano       va
+Batikano Hiria va
+Vatikán       va
+ລັດເວີຍ  va
+Cidade do Vaticano     va
+Vatikán       va
+Vatikan        va
+Vatikanstaten  va
+梵蒂冈      va
+St. Vincent and the Grenadines vc
+St. Vincent en die Grenadene   vc
+سانت فينسنت و الغرينادين  vc
+St. Vincent və Grenadines     vc
+Сент-Винсент и Гренадине  vc
+S. Visant hag ar Grenadinez    vc
+Sveti Vincent i Grenadini      vc
+St. Vincent i les Granadines   vc
+St. Vincent a Grenadiny        vc
+St. Vincent og Grenadinerne    vc
+St. Vincent und Grenadinen     vc
+St. Vincent kaj la Grenadinoj  vc
+San Vicente y las Granadinas   vc
+St. Vincent ja Grenadines      vc
+St. Vincent eta Grenadines     vc
+سن وینسن و گرادینس      vc
+St. Vincent ja Grenadiinit     vc
+Sankta Vinsent og Grenadinoyggjar      vc
+St Vincent et les Grenadines   vc
+San Vicente e as Granadinas    vc
+סנט וינסנט והגרנדינים       vc
+St. Vincent és Grenadines     vc
+St. Vincent dan the Grenadines vc
+St. Vincent og Grenadines      vc
+S. Vincent e the Grenadines    vc
+セントヴィンセントグレナディン  vc
+세인트 빈센트 그레나딘       vc
+ເຊີນວິນແຊນ ແລະເກນາດີນ  vc
+Šv. Vincentas ir Grenadinai   vc
+Sv. Vincents un Grenadīnes    vc
+St. Vinċenz u l-Grenadini     vc
+St. Vincent og Grenadinene     vc
+St. Vincent en de Grenadines   vc
+St. Vincent og Grenadinane     vc
+St. Vincent le Grenadines      vc
+St. Vincent e les Granadines   vc
+St. Vincent i Grenadyny        vc
+São Vicente e Grenadinas      vc
+São Vicente e Grenadines      vc
+Sf. Vincent şi Grenadines     vc
+Сент-Винсент и Гренадины  vc
+St. Vincent a Grenadines       vc
+Sv. Vincent in Grenadini       vc
+St. Vincent i Grenadine        vc
+I-St. Vincent and the Grenadines       vc
+St. Vincent och Grenadinerna   vc
+¦ºÂ¢ýð Å¢ý¦ºýð & ¸¢¦ÃÉËý¸û       vc
+เซนต์วินเซนต์ และ เกรนาดีน     vc
+St. Vincent ve Grenadines      vc
+Сент-Вінсент і Гренадіни  vc
+St. Vincent na Grenadines      vc
+St. Vincent và Grenadines     vc
+St. Vincent ne Grenadines      vc
+圣文森特和格林那达    vc
+聖文森及格納那丁       vc
+I-St. Vincent kanye ne-Grenadines      vc
+Venezuela      ve
+فنزويلا ve
+Вэнэсуэла     ve
+Венецуела     ve
+Venecuela      ve
+Βενεζουέλα   ve
+Venezuelo      ve
+Venetsueela    ve
+ونزویلا ve
+Venesuela      ve
+Vénézuela    ve
+ונצואלה ve
+Venecuela      ve
+ベネズェラ        ve
+베네수엘라        ve
+ເວເນຊຸເອລາ ve
+Venesuela      ve
+Venecuēla     ve
+Veneżwela     ve
+Wenezuela      ve
+Венесуэла     ve
+Venecuela      ve
+I-Venezuela    ve
+¦ÅÉ¢ÍÅÄ¡       ve
+เวเนซูเอลา ve
+Венесуела     ve
+委内瑞拉   ve
+委內瑞拉   ve
+Virgin Islands, British        vg
+Вирджински Острови, Британски        vg
+Djevičanska ostrva, Britanska vg
+Britiske jomfruøer    vg
+Islas Vírgenes Británicas    vg
+Irla Birjinak, Britaniar       vg
+Virgin-szigetek (brit) vg
+Ilhas Virgens, Inglaterra      vg
+Panenské Ostrovy, Britské    vg
+Deviški otoki, Britanski      vg
+Brittiska Jungfruöarna        vg
+维尔京群岛,英国       vg
+Virgin Islands, U.S.   vi
+Вирджински Острови, Американски    vi
+Djevičanska ostrva, Američka vi
+Jomfruøerne   vi
+Islas Vírgenes Americanas     vi
+Irla Birjinak, EE.BB.  vi
+Virgin-szigetek (USA)  vi
+Ilhas Virgens, EUA     vi
+Panenské Ostrovy, Americké   vi
+Deviški otoki, ZDA    vi
+Amerikanska Jungfruöarna      vi
+维尔京群岛,美国       vi
+Vietnam        vn
+Viëtnam       vn
+فييتنام vn
+Vyetnam        vn
+Віетнам vn
+Виетнам vn
+Vijetnam       vn
+Βιετνάμ vn
+Vjetnamo       vn
+ویتنام   vn
+וייטנאם vn
+Vijetnam       vn
+Víetnam     vn
+ベトナム   vn
+베트남      vn
+ຫວງດນາມ  vn
+Vietnamas      vn
+Vjetnama       vn
+Vjetnam        vn
+Viëtnam       vn
+Wietnam        vn
+Vietname       vn
+Vietnã        vn
+Вьетнам vn
+I-Vietnam      vn
+เวียตนาม       vn
+В'єтнам  vn
+越南 vn
+越南 vn
+Vanuatu        vu
+Вануату vu
+Ванути   vu
+ຈີນ      vu
+瓦努阿图   vu
+Wallis and Futuna      wf
+Valis i Futuna wf
+Wallis- og Futuna-øerne       wf
+Wallis y Futuna        wf
+Wallis eta Futuna      wf
+Wallis és Futuna      wf
+ປັກອິນພາບ    wf
+Wallis e Futuna        wf
+Wallis a Futuna        wf
+Wallis in Futuna       wf
+Wallis och Futuna      wf
+Samoa  ws
+Самоа     ws
+Самоя     ws
+Szamoa ws
+ໂຊນາ   ws
+萨摩亚群岛        ws
+Yemen  ye
+اليمن     ye
+Емен       ye
+Йемен     ye
+Jemen  ye
+Iemen  ye
+Jemen  ye
+Jemen  ye
+Υεμένη   ye
+Jemeno ye
+Jeemen ye
+یمن ye
+Jemen  ye
+Jemen  ye
+תימן       ye
+Jemen  ye
+Jemen  ye
+イエメン   ye
+예멘 ye
+ເດມອນ        ye
+Jemenas        ye
+Jemen  ye
+Jemen  ye
+Jemen  ye
+Jemen  ye
+Jemen  ye
+Iémen ye
+Йемен     ye
+Jemen  ye
+Jemen  ye
+I-Yemen        ye
+Jemen  ye
+§ÂÁý       ye
+Ємен       ye
+也门 ye
+葉門 ye
+Yugoslavia     yu
+Югаславія     yu
+Югославия     yu
+Jugoslavija    yu
+Jugoslavien (Serbien/Montenegro)       yu
+Jugoszlávia   yu
+ໂບລິເວີຍ       yu
+Iugoslávia    yu
+Juhoslávia    yu
+Jugoslavija    yu
+Jugoslavien    yu
+南斯拉夫   yu
+South Africa   za
+Suid Afrika    za
+جنوب أفريقيا        za
+Cənubi Afrika za
+Паўднёвая Афрыка        za
+Южна Африка  za
+Suafrika       za
+Južna Afrika  za
+Sudàfrica     za
+Jižní Afrika za
+Sydafrikanske republik za
+Südafrika     za
+Νότια Αφρική        za
+Sudafriko      za
+Sudáfrica     za
+Lõuna Aafrika za
+Hego Afrika    za
+آفریقای جنوبی      za
+Etelä-Afrikka za
+Suðurafrika   za
+Afrique du sud za
+África do Sur za
+דרום אפריקה  za
+Južna Afrika  za
+Dél-Afrika    za
+Afrika Selatan za
+Suður Afríka     za
+Sud Africa     za
+南アフリカ        za
+남 아프리카 공화국     za
+ແອບພິກາໃຕ້ za
+Pietų Afrika  za
+Dienvid Āfrika        za
+Afrika t'Isfel za
+Sør-Afrika    za
+Zuid-Afrika    za
+Sør-Afrika    za
+Afrika Borwa   za
+Sudafrica      za
+Afryka Południowa     za
+Africa do Sul  za
+África do Sul za
+Africa de Sud  za
+Южная Африка        za
+Južná Afrika za
+Južna Afrika  za
+Južna Afrika  za
+I-South Africa         za
+Sydafrika      za
+¦¾ý ¬ôâ측  za
+แอฟริกาใต้ za
+Güney Afrika  za
+Південна Африка  za
+Afurika tshipembe      za
+Nam Phi        za
+Mzantsi Afrika za
+南非 za
+南非 za
+Emzantsi Afrika        za
+Zambia zm
+Замбія   zm
+Замбия   zm
+ຈາໄມກາ້  zm
+Zâmbia        zm
+Zambija        zm
+赞比亚      zm
+Zimbabwe       zw
+Зымбабвэ       zw
+Зимбабве       zw
+Zimbabve       zw
+ລິຊາ   zw
+Zimbabve       zw
+津巴布韦   zw
diff --git a/src/email.cpp b/src/email.cpp
new file mode 100644 (file)
index 0000000..105967c
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "email.h"
+
+#include <QMap>
+#include <qstringlist.h>
+#include <QDataStream>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Email::Private : public QSharedData
+{
+public:
+    Private()
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        parameters = other.parameters;
+        mail = other.mail;
+    }
+    QMap<QString, QStringList> parameters;
+    QString mail;
+};
+
+Email::Email()
+    : d(new Private)
+{
+
+}
+
+Email::Email(const QString &mail)
+    : d(new Private)
+{
+    d->mail = mail;
+}
+
+Email::Email(const Email &other)
+    : d(other.d)
+{
+}
+
+Email::~Email()
+{
+
+}
+
+QMap<QString, QStringList> Email::parameters() const
+{
+    return d->parameters;
+}
+
+bool Email::operator==(const Email &other) const
+{
+    return (d->parameters == other.parameters()) && (d->mail == other.mail());
+}
+
+bool Email::operator!=(const Email &other) const
+{
+    return !(other == *this);
+}
+
+Email &Email::operator=(const Email &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString Email::toString() const
+{
+    QString str;
+    str += QLatin1String("Email {\n");
+    str += QStringLiteral("    mail: %1\n").arg(d->mail);
+    if (!d->parameters.isEmpty()) {
+        QMapIterator<QString, QStringList> i(d->parameters);
+        QString param;
+        while (i.hasNext()) {
+            i.next();
+            param += QStringLiteral("%1 %2").arg(i.key(), i.value().join(QStringLiteral(",")));
+        }
+        str += QStringLiteral("    parameters: %1\n").arg(param);
+    }
+    str += QLatin1String("}\n");
+    return str;
+}
+
+void Email::setParameters(const QMap<QString, QStringList> &params)
+{
+    d->parameters = params;
+}
+
+void Email::setEmail(const QString &mail)
+{
+    d->mail = mail;
+}
+
+QString Email::mail() const
+{
+    return d->mail;
+}
+
+bool Email::isValid() const
+{
+    return !d->mail.isEmpty();
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Email &email)
+{
+    return s << email.d->parameters << email.d->mail;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Email &email)
+{
+    s >> email.d->parameters >> email.d->mail;
+    return s;
+}
+
diff --git a/src/email.h b/src/email.h
new file mode 100644 (file)
index 0000000..f51e6ce
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef EMAIL_H
+#define EMAIL_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+
+/** @short Class that holds a Email for a contact.
+ *  @since 4.14.5
+ */
+
+namespace KContacts
+{
+
+class KCONTACTS_EXPORT Email
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Email &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Email &);
+public:
+    /**
+     * Creates an empty email object.
+     */
+    Email();
+    Email(const Email &other);
+    Email(const QString &mail);
+
+    ~Email();
+
+    typedef QVector<Email> List;
+
+    void setEmail(const QString &mail);
+    QString mail() const;
+
+    bool isValid() const;
+
+    void setParameters(const QMap<QString, QStringList> &params);
+    QMap<QString, QStringList> parameters() const;
+
+    bool operator==(const Email &other) const;
+    bool operator!=(const Email &other) const;
+
+    Email &operator=(const Email &other);
+
+    QString toString() const;
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Email &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Email &object);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Email, Q_MOVABLE_TYPE);
+#endif // EMAIL_H
diff --git a/src/field.cpp b/src/field.cpp
new file mode 100644 (file)
index 0000000..3406c9d
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include <klocalizedstring.h>
+#include <kconfig.h>
+
+#include <kconfiggroup.h>
+#include <KSharedConfig>
+#include "field.h"
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Field::Private
+{
+public:
+    Private(int fieldId, int category = 0,
+            const QString &label = QString(),
+            const QString &key = QString(),
+            const QString &app = QString())
+        : mFieldId(fieldId), mCategory(category), mLabel(label),
+          mKey(key), mApp(app) {}
+
+    enum FieldId {
+        CustomField,
+        FormattedName,
+        FamilyName,
+        GivenName,
+        AdditionalName,
+        Prefix,
+        Suffix,
+        NickName,
+        Birthday,
+        HomeAddressStreet,
+        HomeAddressPostOfficeBox,
+        HomeAddressLocality,
+        HomeAddressRegion,
+        HomeAddressPostalCode,
+        HomeAddressCountry,
+        HomeAddressLabel,
+        BusinessAddressStreet,
+        BusinessAddressPostOfficeBox,
+        BusinessAddressLocality,
+        BusinessAddressRegion,
+        BusinessAddressPostalCode,
+        BusinessAddressCountry,
+        BusinessAddressLabel,
+        HomePhone,
+        BusinessPhone,
+        MobilePhone,
+        HomeFax,
+        BusinessFax,
+        CarPhone,
+        Isdn,
+        Pager,
+        Email,
+        Mailer,
+        Title,
+        Role,
+        Organization,
+        Department,
+        Note,
+        Url
+    };
+
+    int fieldId() const
+    {
+        return mFieldId;
+    }
+    int category() const
+    {
+        return mCategory;
+    }
+
+    QString label() const
+    {
+        return mLabel;
+    }
+    QString key() const
+    {
+        return mKey;
+    }
+    QString app() const
+    {
+        return mApp;
+    }
+
+    static Field::List mAllFields;
+    static Field::List mDefaultFields;
+    static Field::List mCustomFields;
+
+private:
+    int mFieldId;
+    int mCategory;
+
+    QString mLabel;
+    QString mKey;
+    QString mApp;
+};
+
+Field::List Field::Private::mAllFields;
+Field::List Field::Private::mDefaultFields;
+Field::List Field::Private::mCustomFields;
+
+Field::Field(Private *p)
+    : d(p)
+{
+}
+
+Field::~Field()
+{
+    delete d;
+}
+
+QString Field::label()
+{
+    switch (d->fieldId()) {
+    case Private::FormattedName:
+        return Addressee::formattedNameLabel();
+    case Private::FamilyName:
+        return Addressee::familyNameLabel();
+    case Private::GivenName:
+        return Addressee::givenNameLabel();
+    case Private::AdditionalName:
+        return Addressee::additionalNameLabel();
+    case Private::Prefix:
+        return Addressee::prefixLabel();
+    case Private::Suffix:
+        return Addressee::suffixLabel();
+    case Private::NickName:
+        return Addressee::nickNameLabel();
+    case Private::Birthday:
+        return Addressee::birthdayLabel();
+    case Private::HomeAddressStreet:
+        return Addressee::homeAddressStreetLabel();
+    case Private::HomeAddressPostOfficeBox:
+        return Addressee::homeAddressPostOfficeBoxLabel();
+    case Private::HomeAddressLocality:
+        return Addressee::homeAddressLocalityLabel();
+    case Private::HomeAddressRegion:
+        return Addressee::homeAddressRegionLabel();
+    case Private::HomeAddressPostalCode:
+        return Addressee::homeAddressPostalCodeLabel();
+    case Private::HomeAddressCountry:
+        return Addressee::homeAddressCountryLabel();
+    case Private::HomeAddressLabel:
+        return Addressee::homeAddressLabelLabel();
+    case Private::BusinessAddressStreet:
+        return Addressee::businessAddressStreetLabel();
+    case Private::BusinessAddressPostOfficeBox:
+        return Addressee::businessAddressPostOfficeBoxLabel();
+    case Private::BusinessAddressLocality:
+        return Addressee::businessAddressLocalityLabel();
+    case Private::BusinessAddressRegion:
+        return Addressee::businessAddressRegionLabel();
+    case Private::BusinessAddressPostalCode:
+        return Addressee::businessAddressPostalCodeLabel();
+    case Private::BusinessAddressCountry:
+        return Addressee::businessAddressCountryLabel();
+    case Private::BusinessAddressLabel:
+        return Addressee::businessAddressLabelLabel();
+    case Private::HomePhone:
+        return Addressee::homePhoneLabel();
+    case Private::BusinessPhone:
+        return Addressee::businessPhoneLabel();
+    case Private::MobilePhone:
+        return Addressee::mobilePhoneLabel();
+    case Private::HomeFax:
+        return Addressee::homeFaxLabel();
+    case Private::BusinessFax:
+        return Addressee::businessFaxLabel();
+    case Private::CarPhone:
+        return Addressee::carPhoneLabel();
+    case Private::Isdn:
+        return Addressee::isdnLabel();
+    case Private::Pager:
+        return Addressee::pagerLabel();
+    case Private::Email:
+        return Addressee::emailLabel();
+    case Private::Mailer:
+        return Addressee::mailerLabel();
+    case Private::Title:
+        return Addressee::titleLabel();
+    case Private::Role:
+        return Addressee::roleLabel();
+    case Private::Organization:
+        return Addressee::organizationLabel();
+    case Private::Department:
+        return Addressee::departmentLabel();
+    case Private::Note:
+        return Addressee::noteLabel();
+    case Private::Url:
+        return Addressee::urlLabel();
+    case Private::CustomField:
+        return d->label();
+    default:
+        return i18n("Unknown Field");
+    }
+}
+
+int Field::category()
+{
+    return d->category();
+}
+
+QString Field::categoryLabel(int category)
+{
+    switch (category) {
+    case All:
+        return i18n("All");
+    case Frequent:
+        return i18n("Frequent");
+    case Address:
+        return i18nc("street/postal", "Address");
+    case Email:
+        return i18n("Email");
+    case Personal:
+        return i18n("Personal");
+    case Organization:
+        return i18n("Organization");
+    case CustomCategory:
+        return i18n("Custom");
+    default:
+        return i18n("Undefined");
+    }
+}
+
+QString Field::value(const KContacts::Addressee &a)
+{
+    switch (d->fieldId()) {
+    case Private::FormattedName:
+        return a.formattedName();
+    case Private::FamilyName:
+        return a.familyName();
+    case Private::GivenName:
+        return a.givenName();
+    case Private::AdditionalName:
+        return a.additionalName();
+    case Private::Prefix:
+        return a.prefix();
+    case Private::Suffix:
+        return a.suffix();
+    case Private::NickName:
+        return a.nickName();
+    case Private::Mailer:
+        return a.mailer();
+    case Private::Title:
+        return a.title();
+    case Private::Role:
+        return a.role();
+    case Private::Organization:
+        return a.organization();
+    case Private::Department:
+        return a.department();
+    case Private::Note:
+        return a.note();
+    case Private::Email:
+        return a.preferredEmail();
+    case Private::Birthday:
+        if (a.birthday().isValid()) {
+            return a.birthday().date().toString(Qt::ISODate);
+        } else {
+            return QString();
+        }
+    case Private::Url:
+        return a.url().url().toDisplayString();
+    case Private::HomePhone: {
+        PhoneNumber::List::ConstIterator it;
+
+        {
+            // check for preferred number
+            const PhoneNumber::List list = a.phoneNumbers(PhoneNumber::Home | PhoneNumber::Pref);
+            PhoneNumber::List::ConstIterator end(list.end());
+            for (it = list.begin(); it != end; ++it) {
+                if (((*it).type() & ~(PhoneNumber::Pref)) == PhoneNumber::Home) {
+                    return (*it).number();
+                }
+            }
+        }
+
+        {
+            // check for normal home number
+            const PhoneNumber::List list = a.phoneNumbers(PhoneNumber::Home);
+            PhoneNumber::List::ConstIterator end(list.end());
+            for (it = list.begin(); it != end; ++it) {
+                if (((*it).type() & ~(PhoneNumber::Pref)) == PhoneNumber::Home) {
+                    return (*it).number();
+                }
+            }
+        }
+
+        return QString();
+    }
+    case Private::BusinessPhone: {
+        PhoneNumber::List::ConstIterator it;
+
+        {
+            // check for preferred number
+            const PhoneNumber::List list = a.phoneNumbers(PhoneNumber::Work | PhoneNumber::Pref);
+            PhoneNumber::List::ConstIterator end(list.end());
+            for (it = list.begin(); it != end; ++it) {
+                if (((*it).type() & ~(PhoneNumber::Pref)) == PhoneNumber::Work) {
+                    return (*it).number();
+                }
+            }
+        }
+
+        {
+            // check for normal work number
+            const PhoneNumber::List list = a.phoneNumbers(PhoneNumber::Work);
+            for (it = list.begin(); it != list.end(); ++it) {
+                if (((*it).type() & ~(PhoneNumber::Pref)) == PhoneNumber::Work) {
+                    return (*it).number();
+                }
+            }
+        }
+
+        return QString();
+    }
+    case Private::MobilePhone:
+        return a.phoneNumber(PhoneNumber::Cell).number();
+    case Private::HomeFax:
+        return a.phoneNumber(PhoneNumber::Home | PhoneNumber::Fax).number();
+    case Private::BusinessFax:
+        return a.phoneNumber(PhoneNumber::Work | PhoneNumber::Fax).number();
+    case Private::CarPhone:
+        return a.phoneNumber(PhoneNumber::Car).number();
+    case Private::Isdn:
+        return a.phoneNumber(PhoneNumber::Isdn).number();
+    case Private::Pager:
+        return a.phoneNumber(PhoneNumber::Pager).number();
+    case Private::HomeAddressStreet:
+        return a.address(Address::Home).street();
+    case Private::HomeAddressPostOfficeBox:
+        return a.address(Address::Home).postOfficeBox();
+    case Private::HomeAddressLocality:
+        return a.address(Address::Home).locality();
+    case Private::HomeAddressRegion:
+        return a.address(Address::Home).region();
+    case Private::HomeAddressPostalCode:
+        return a.address(Address::Home).postalCode();
+    case Private::HomeAddressCountry:
+        return a.address(Address::Home).country();
+    case Private::BusinessAddressStreet:
+        return a.address(Address::Work).street();
+    case Private::BusinessAddressPostOfficeBox:
+        return a.address(Address::Work).postOfficeBox();
+    case Private::BusinessAddressLocality:
+        return a.address(Address::Work).locality();
+    case Private::BusinessAddressRegion:
+        return a.address(Address::Work).region();
+    case Private::BusinessAddressPostalCode:
+        return a.address(Address::Work).postalCode();
+    case Private::BusinessAddressCountry:
+        return a.address(Address::Work).country();
+    case Private::CustomField:
+        return a.custom(d->app(), d->key());
+    default:
+        return QString();
+    }
+}
+
+bool Field::setValue(KContacts::Addressee &a, const QString &value)
+{
+    switch (d->fieldId()) {
+    case Private::FormattedName:
+        a.setFormattedName(value);
+        return true;
+    case Private::FamilyName:
+        a.setFamilyName(value);
+        return true;
+    case Private::GivenName:
+        a.setGivenName(value);
+        return true;
+    case Private::AdditionalName:
+        a.setAdditionalName(value);
+        return true;
+    case Private::Prefix:
+        a.setPrefix(value);
+        return true;
+    case Private::Suffix:
+        a.setSuffix(value);
+        return true;
+    case Private::NickName:
+        a.setNickName(value);
+        return true;
+    case Private::Mailer:
+        a.setMailer(value);
+        return true;
+    case Private::Title:
+        a.setTitle(value);
+        return true;
+    case Private::Role:
+        a.setRole(value);
+        return true;
+    case Private::Organization:
+        a.setOrganization(value);
+        return true;
+    case Private::Department:
+        a.setDepartment(value);
+        return true;
+    case Private::Note:
+        a.setNote(value);
+        return true;
+    case Private::Birthday:
+        a.setBirthday(QDateTime::fromString(value, Qt::ISODate));
+        return true;
+    case Private::CustomField:
+        a.insertCustom(d->app(), d->key(), value);
+        return true;
+    default:
+        return false;
+    }
+}
+
+QString Field::sortKey(const KContacts::Addressee &a)
+{
+    switch (d->fieldId()) {
+    case Private::FormattedName:
+        return a.formattedName();
+    case Private::FamilyName:
+        return a.familyName();
+    case Private::GivenName:
+        return a.givenName();
+    case Private::AdditionalName:
+        return a.additionalName();
+    case Private::Prefix:
+        return a.prefix();
+    case Private::Suffix:
+        return a.suffix();
+    case Private::NickName:
+        return a.nickName();
+    case Private::Mailer:
+        return a.mailer();
+    case Private::Title:
+        return a.title();
+    case Private::Role:
+        return a.role();
+    case Private::Organization:
+        return a.organization();
+    case Private::Department:
+        return a.department();
+    case Private::Note:
+        return a.note();
+    case Private::Birthday:
+        if (a.birthday().isValid()) {
+            QDate date = a.birthday().date();
+            QString key;
+            key.sprintf("%02d-%02d", date.month(), date.day());
+            return key;
+        } else {
+            return QStringLiteral("00-00");
+        }
+    default:
+        return value(a).toLower();
+    }
+}
+
+bool Field::isCustom()
+{
+    return d->fieldId() == Private::CustomField;
+}
+
+Field::List Field::allFields()
+{
+    if (Private::mAllFields.isEmpty()) {
+        createField(Private::FormattedName, Frequent);
+        createField(Private::FamilyName, Frequent);
+        createField(Private::GivenName, Frequent);
+        createField(Private::AdditionalName);
+        createField(Private::Prefix);
+        createField(Private::Suffix);
+        createField(Private::NickName, Personal);
+        createField(Private::Birthday, Personal);
+        createField(Private::HomeAddressStreet, Address | Personal);
+        createField(Private::HomeAddressPostOfficeBox, Address | Personal);
+        createField(Private::HomeAddressLocality, Address | Personal);
+        createField(Private::HomeAddressRegion, Address | Personal);
+        createField(Private::HomeAddressPostalCode, Address | Personal);
+        createField(Private::HomeAddressCountry, Address | Personal);
+        createField(Private::HomeAddressLabel, Address | Personal);
+        createField(Private::BusinessAddressStreet, Address | Organization);
+        createField(Private::BusinessAddressPostOfficeBox, Address | Organization);
+        createField(Private::BusinessAddressLocality, Address | Organization);
+        createField(Private::BusinessAddressRegion, Address | Organization);
+        createField(Private::BusinessAddressPostalCode, Address | Organization);
+        createField(Private::BusinessAddressCountry, Address | Organization);
+        createField(Private::BusinessAddressLabel, Address | Organization);
+        createField(Private::HomePhone, Personal | Frequent);
+        createField(Private::BusinessPhone, Organization | Frequent);
+        createField(Private::MobilePhone, Frequent);
+        createField(Private::HomeFax);
+        createField(Private::BusinessFax);
+        createField(Private::CarPhone);
+        createField(Private::Isdn);
+        createField(Private::Pager);
+        createField(Private::Email, Email | Frequent);
+        createField(Private::Mailer, Email);
+        createField(Private::Title, Organization);
+        createField(Private::Role, Organization);
+        createField(Private::Organization, Organization);
+        createField(Private::Department, Organization);
+        createField(Private::Note);
+        createField(Private::Url);
+    }
+
+    return Private::mAllFields;
+}
+
+Field::List Field::defaultFields()
+{
+    if (Private::mDefaultFields.isEmpty()) {
+        createDefaultField(Private::FormattedName);
+        createDefaultField(Private::Email);
+    }
+
+    return Private::mDefaultFields;
+}
+
+void Field::createField(int id, int category)
+{
+    Private::mAllFields.append(new Field(new Private(id, category)));
+}
+
+void Field::createDefaultField(int id, int category)
+{
+    Private::mDefaultFields.append(new Field(new Private(id, category)));
+}
+
+void Field::deleteFields()
+{
+    Field::List::ConstIterator it;
+
+    for (it = Private::mAllFields.constBegin(); it != Private::mAllFields.constEnd(); ++it) {
+        delete(*it);
+    }
+    Private::mAllFields.clear();
+
+    for (it = Private::mDefaultFields.constBegin(); it != Private::mDefaultFields.constEnd(); ++it) {
+        delete(*it);
+    }
+    Private::mDefaultFields.clear();
+
+    for (it = Private::mCustomFields.constBegin(); it != Private::mCustomFields.constEnd(); ++it) {
+        delete(*it);
+    }
+    Private::mCustomFields.clear();
+}
+
+void Field::saveFields(const QString &identifier,
+                       const Field::List &fields)
+{
+    KConfigGroup cg(KSharedConfig::openConfig(), "KABCFields");
+
+    saveFields(cg, identifier, fields);
+}
+
+void Field::saveFields(KConfigGroup &cfg, const QString &identifier,
+                       const Field::List &fields)
+{
+    QList<int> fieldIds;
+
+    int custom = 0;
+    Field::List::ConstIterator it;
+    fieldIds.reserve(fields.count());
+    for (it = fields.begin(); it != fields.end(); ++it) {
+        fieldIds.append((*it)->d->fieldId());
+        if ((*it)->isCustom()) {
+            QStringList customEntry;
+            customEntry << (*it)->d->label();
+            customEntry << (*it)->d->key();
+            customEntry << (*it)->d->app();
+            cfg.writeEntry(QLatin1String("KCONTACTS_CustomEntry_") + identifier + QLatin1Char('_') +
+                           QString::number(custom++), customEntry);
+        }
+    }
+
+    cfg.writeEntry(identifier, fieldIds);
+}
+
+Field::List Field::restoreFields(const QString &identifier)
+{
+    KConfigGroup cg(KSharedConfig::openConfig(), "KABCFields");
+
+    return restoreFields(cg, identifier);
+}
+
+Field::List Field::restoreFields(const KConfigGroup &cfg, const QString &identifier)
+{
+    const QList<int> fieldIds = cfg.readEntry(identifier, QList<int>());
+
+    Field::List fields;
+
+    int custom = 0;
+    QList<int>::ConstIterator it;
+    fields.reserve(fieldIds.count());
+    for (it = fieldIds.begin(); it != fieldIds.end(); ++it) {
+        Private *f = Q_NULLPTR;
+        if ((*it) == Private::CustomField) {
+            QStringList customEntry = cfg.readEntry(QLatin1String("KCONTACTS_CustomEntry_") +
+                                                    identifier + QLatin1Char('_') +
+                                                    QString::number(custom++), QStringList());
+            f = new Private(*it, CustomCategory, customEntry[ 0 ],
+                            customEntry[ 1 ], customEntry[ 2 ]);
+        } else {
+            f = new Private(*it);
+        }
+        fields.append(new Field(f));
+    }
+
+    return fields;
+}
+
+bool Field::equals(Field *field)
+{
+    bool sameId = (d->fieldId() == field->d->fieldId());
+
+    if (!sameId) {
+        return false;
+    }
+
+    if (d->fieldId() != Private::CustomField) {
+        return true;
+    }
+
+    return d->key() == field->d->key();
+}
+
+Field *Field::createCustomField(const QString &label, int category,
+                                const QString &key, const QString &app)
+{
+    Field *field = new Field(new Private(Private::CustomField,
+                                         category | CustomCategory,
+                                         label, key, app));
+    Private::mCustomFields.append(field);
+
+    return field;
+}
diff --git a/src/field.h b/src/field.h
new file mode 100644 (file)
index 0000000..485ce73
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_FIELD_H
+#define KCONTACTS_FIELD_H
+
+#include "addressee.h"
+#include <QtCore/QString>
+
+class KConfigGroup;
+
+namespace KContacts
+{
+
+/**
+ * @short Represents a field in the Addressbook
+ *
+ * This class represents a field in the Addressbook database.
+ * It provides methods for accessing meta-information about
+ * the field (such as label() or category()) as well as
+ * getting or setting the field's value in an instance of
+ * Addressee (value(), setValue()).
+ *
+ * Furthermore, some static methods are provided for getting
+ * a list of all fields (allFields(), defaultFields()), for
+ * creating new fields (createCustomField()) and for saving
+ * or loading fields to/from a config file (saveFields(),
+ * restoreFields()).
+ */
+class KCONTACTS_EXPORT Field
+{
+public:
+    /**
+     * This type is used for a list of fields.
+     */
+    typedef QList<Field *> List;
+
+    /**
+     * Represents the category a field belongs to.
+     */
+    enum FieldCategory {
+        /**
+         * All fields
+         */
+        All = 0x0,
+        /**
+         * Frequently used fields
+         */
+        Frequent = 0x01,
+        /**
+         * Fields which belong to the address, such as Street, City, Zip, etc.
+         */
+        Address = 0x02,
+        /**
+         * Fields which store information about the e-mail contact, such as
+         * e-mail address or mail client
+         */
+        Email = 0x04,
+        /**
+         * Personal fields, such as Birthday, Home Address fields, IM Address, etc.
+         */
+        Personal = 0x08,
+        /**
+         * Fields about the organization, such as Business Address fields, Department,
+         * Profession, etc.
+         */
+        Organization = 0x10,
+        /**
+         * Custom (user-defined) fields
+         */
+        CustomCategory = 0x20
+    };
+
+    /**
+     * Returns the translated label for this field.
+     */
+    virtual QString label();
+
+    /**
+     * Returns the  ored categories the field belongs to.
+     */
+    virtual int category();
+
+    /**
+     * Returns the translated label for @p category.
+     *
+     * @param category the category of type FieldCategory
+     * @return the translated label
+     */
+    static QString categoryLabel(int category);
+
+    /**
+     * Returns a string representation of the value the field has in the given
+     * Addressee.
+     *
+     * @return the string representation of the value or QString(), if it
+     * is not possible to convert the value to a string.
+     */
+    virtual QString value(const KContacts::Addressee &addressee);
+
+    /**
+     * Sets the value of the field in the given Addressee.
+     *
+     * @return @c true on success or @c false, if the given string couldn't
+     * be converted to a valid value.
+     */
+    virtual bool setValue(KContacts::Addressee &addressee, const QString &value);
+
+    /**
+     * Returns a string, that can be used for sorting.
+     */
+    QString sortKey(const KContacts::Addressee &addressee);
+
+    /**
+     * Returns, if the field is a user-defined field.
+     *
+     * @return @c true if this is a custom field, @c false otherwise
+     */
+    virtual bool isCustom();
+
+    /**
+     * Returns, if the field is equal with @p field.
+     *
+     * @param field the field to compare this field to
+     * @return @c true if the fields are equal, @c false otherwise
+     */
+    virtual bool equals(Field *field);
+
+    /**
+     * Returns a list of all fields.
+     */
+    static Field::List allFields();
+
+    /**
+     * Returns a list of the default fields.
+     */
+    static Field::List defaultFields();
+
+    /**
+     * Creates a custom field.
+     *
+     * @param label    The label for this field
+     * @param category The category of this field
+     * @param key      Unique key for this field
+     * @param app      Unique app name for this field
+     */
+    static Field *createCustomField(const QString &label, int category,
+                                    const QString &key, const QString &app);
+
+    /**
+     * Delete all fields from list.
+     */
+    static void deleteFields();
+
+    /**
+     * Save the field settings to a config file.
+     *
+     * @param cfg        The config file object
+     * @param identifier The unique identifier
+     * @param fields     The list of the fields
+     */
+    static void saveFields(KConfigGroup &cfg, const QString &identifier,
+                           const Field::List &fields);
+    /**
+     * @overload
+     *
+     * Here, the list is stored in KSharedConfig::openConfig() in group "KABCFields".
+     *
+     * @param identifier The unique identifier
+     * @param fields     The list of the fields
+     */
+    static void saveFields(const QString &identifier,
+                           const Field::List &fields);
+
+    /**
+     * Load the field settings from a config file.
+     *
+     * @param cfg        The config file object
+     * @param identifier The unique identifier
+     */
+    static Field::List restoreFields(const KConfigGroup &cfg, const QString &identifier);
+
+    /**
+     * @overload
+     *
+     * Here, the list is loaded from KSharedConfig::openConfig() from group "KABCFields".
+     *
+     * @param identifier The unique identifier
+     */
+    static Field::List restoreFields(const QString &identifier);
+
+protected:
+    /**
+     * @internal
+     *
+     * Creates a field and appends it to the general list of fields.
+     *
+     * @param id The identifier for the field
+     * @param category The optional category for the field
+     */
+    static void createField(int id, int category = 0);
+
+    /**
+     * @internal
+     *
+     * Creates a field and appends it to the list of default fields.
+     *
+     * @param id The identifier for the field
+     * @param category The optional category for the field
+     */
+    static void createDefaultField(int id, int category = 0);
+
+private:
+    class Private;
+
+    Field(Private *p);
+    virtual ~Field();
+
+    Private *const d;
+};
+
+}
+#endif
diff --git a/src/gender.cpp b/src/gender.cpp
new file mode 100644 (file)
index 0000000..2303e49
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "gender.h"
+
+#include <QMap>
+#include <qstringlist.h>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Gender::Private : public QSharedData
+{
+public:
+    Private()
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        comment = other.comment;
+        gender = other.gender;
+    }
+    QString gender;
+    QString comment;
+};
+
+Gender::Gender()
+    : d(new Private)
+{
+
+}
+
+Gender::Gender(const QString &gender)
+    : d(new Private)
+{
+    d->gender = gender;
+}
+
+Gender::Gender(const Gender &other)
+    : d(other.d)
+{
+}
+
+Gender::~Gender()
+{
+
+}
+
+bool Gender::operator==(const Gender &other) const
+{
+    return (d->comment == other.comment()) && (d->gender == other.gender());
+}
+
+bool Gender::operator!=(const Gender &other) const
+{
+    return !(other == *this);
+}
+
+Gender &Gender::operator=(const Gender &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString Gender::toString() const
+{
+    QString str;
+    str += QLatin1String("Gender {\n");
+    str += QStringLiteral("    gender: %1\n").arg(d->gender);
+    str += QStringLiteral("    comment: %1\n").arg(d->comment);
+    str += QLatin1String("}\n");
+    return str;
+}
+
+void Gender::setGender(const QString &gender)
+{
+    d->gender = gender;
+}
+
+QString Gender::gender() const
+{
+    return d->gender;
+}
+
+void Gender::setComment(const QString &comment)
+{
+    d->comment = comment;
+}
+
+QString Gender::comment() const
+{
+    return d->comment;
+}
+
+bool Gender::isValid() const
+{
+    return !d->gender.isEmpty() || !d->comment.isEmpty();
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Gender &gender)
+{
+    return s << gender.d->comment << gender.d->gender;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Gender &gender)
+{
+    s >> gender.d->comment >> gender.d->gender;
+    return s;
+}
+
diff --git a/src/gender.h b/src/gender.h
new file mode 100644 (file)
index 0000000..9bfb5a4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef GENDER_H
+#define GENDER_H
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+
+/** @short Class that holds a Gender for a contact.
+ *  @since 4.14.5
+ */
+
+namespace KContacts
+{
+
+class KCONTACTS_EXPORT Gender
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Gender &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Gender &);
+public:
+    /**
+     * Creates an empty Gender object.
+     */
+    Gender();
+    Gender(const Gender &other);
+    Gender(const QString &gender);
+
+    ~Gender();
+
+    void setGender(const QString &gender);
+    QString gender() const;
+
+    void setComment(const QString &comment);
+    QString comment() const;
+
+    bool isValid() const;
+
+    bool operator==(const Gender &other) const;
+    bool operator!=(const Gender &other) const;
+
+    Gender &operator=(const Gender &other);
+
+    QString toString() const;
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Gender &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Gender &object);
+}
+Q_DECLARE_TYPEINFO(KContacts::Gender, Q_MOVABLE_TYPE);
+#endif // GENDER_H
diff --git a/src/geo.cpp b/src/geo.cpp
new file mode 100644 (file)
index 0000000..4581101
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "geo.h"
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Geo::Private : public QSharedData
+{
+public:
+    Private()
+        : mLatitude(91), mLongitude(181),
+          mValidLatitude(false), mValidLongitude(false)
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mLatitude = other.mLatitude;
+        mLongitude = other.mLongitude;
+        mValidLatitude = other.mValidLatitude;
+        mValidLongitude = other.mValidLongitude;
+    }
+
+    float mLatitude;
+    float mLongitude;
+
+    bool mValidLatitude;
+    bool mValidLongitude;
+};
+
+Geo::Geo()
+    : d(new Private)
+{
+}
+
+Geo::Geo(float latitude, float longitude)
+    : d(new Private)
+{
+    setLatitude(latitude);
+    setLongitude(longitude);
+}
+
+Geo::Geo(const Geo &other)
+    : d(other.d)
+{
+}
+
+Geo::~Geo()
+{
+}
+
+void Geo::setLatitude(float latitude)
+{
+    if (latitude >= -90 && latitude <= 90) {
+        d->mLatitude = latitude;
+        d->mValidLatitude = true;
+    } else {
+        d->mLatitude = 91;
+        d->mValidLatitude = false;
+    }
+}
+
+float Geo::latitude() const
+{
+    return d->mLatitude;
+}
+
+void Geo::setLongitude(float longitude)
+{
+    if (longitude >= -180 && longitude <= 180) {
+        d->mLongitude = longitude;
+        d->mValidLongitude = true;
+    } else {
+        d->mLongitude = 181;
+        d->mValidLongitude = false;
+    }
+}
+
+float Geo::longitude() const
+{
+    return d->mLongitude;
+}
+
+bool Geo::isValid() const
+{
+    return d->mValidLatitude && d->mValidLongitude;
+}
+
+bool Geo::operator==(const Geo &other) const
+{
+    if (!other.isValid() && !isValid()) {
+        return true;
+    }
+
+    if (!other.isValid() || !isValid()) {
+        return false;
+    }
+
+    if (other.d->mLatitude == d->mLatitude && other.d->mLongitude == d->mLongitude) {
+        return true;
+    }
+
+    return false;
+}
+
+bool Geo::operator!=(const Geo &other) const
+{
+    return !(*this == other);
+}
+
+Geo &Geo::operator=(const Geo &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString Geo::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Geo {\n");
+    str += QStringLiteral("  Valid: %1\n").
+           arg(isValid() ? QStringLiteral("true") : QStringLiteral("false"));
+    str += QStringLiteral("  Latitude: %1\n").arg(d->mLatitude);
+    str += QStringLiteral("  Longitude: %1\n").arg(d->mLongitude);
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+void Geo::clear()
+{
+    d->mValidLatitude = false;
+    d->mValidLongitude = false;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Geo &geo)
+{
+    return s << geo.d->mLatitude << geo.d->mValidLatitude
+           << geo.d->mLongitude << geo.d->mValidLongitude;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Geo &geo)
+{
+    s >> geo.d->mLatitude >> geo.d->mValidLatitude
+      >> geo.d->mLongitude >> geo.d->mValidLongitude;
+
+    return s;
+}
diff --git a/src/geo.h b/src/geo.h
new file mode 100644 (file)
index 0000000..f455754
--- /dev/null
+++ b/src/geo.h
@@ -0,0 +1,141 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_GEO_H
+#define KCONTACTS_GEO_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+
+namespace KContacts
+{
+
+/**
+ * @short Geographic position
+ *
+ * This class represents a geographic position.
+ */
+class KCONTACTS_EXPORT Geo
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Geo &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Geo &);
+
+public:
+    /**
+     * Creates an invalid geographics position object.
+     */
+    Geo();
+
+    /**
+     * Creates a geographics position object.
+     *
+     * @param latitude  Geographical latitude
+     * @param longitude Geographical longitude
+     */
+    Geo(float latitude, float longitude);
+
+    /**
+     * Copy constructor.
+     */
+    Geo(const Geo &other);
+
+    /**
+     * Destroys the geographics position object.
+     */
+    ~Geo();
+
+    /**
+     * Sets the @p latitude.
+     *
+     * @param latitude The location's latitude coordinate
+     */
+    void setLatitude(float latitude);
+
+    /**
+     * Returns the latitude.
+     */
+    float latitude() const;
+
+    /**
+     * Sets the @p longitude.
+     *
+     * @param longitude The location's longitude coordinate
+     */
+    void setLongitude(float longitude);
+
+    /**
+     * Returns the longitude.
+     */
+    float longitude() const;
+
+    /**
+     * Returns, whether this object contains a valid geographical position.
+     */
+    bool isValid() const;
+
+    /**
+     * Equality operator.
+     *
+     * @note Two invalid Geo instance will return @c true
+     */
+    bool operator==(const Geo &other) const;
+
+    /**
+     * Not-Equal operator.
+     */
+    bool operator!=(const Geo &other) const;
+
+    /**
+     * Assignment operator.
+     *
+     * @param other The Geo instance to assign to @c this
+     */
+    Geo &operator=(const Geo &other);
+
+    /**
+     * Returns string representation of geographical position.
+     */
+    QString toString() const;
+
+    /**
+     * Clears the class, marking it as invalid.
+     *
+     * @since 5.6
+     */
+    void clear();
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+/**
+ * Serializes the geographical position @p object into the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Geo &object);
+
+/**
+ * Initializes the geographical position @p object from the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Geo &object);
+}
+Q_DECLARE_TYPEINFO(KContacts::Geo, Q_MOVABLE_TYPE);
+#endif
diff --git a/src/impp.cpp b/src/impp.cpp
new file mode 100644 (file)
index 0000000..9e41fe9
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+    This file is part of libkabc.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "impp.h"
+
+#include <QMap>
+#include <qstringlist.h>
+#include <QDataStream>
+#include "kcontacts_debug.h"
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Impp::Private : public QSharedData
+{
+public:
+    Private()
+        : type(KContacts::Impp::Unknown)
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        parameters = other.parameters;
+        type = other.type;
+    }
+    QMap<QString, QStringList> parameters;
+    QString address;
+    ImppType type;
+};
+
+Impp::Impp()
+    : d(new Private)
+{
+
+}
+
+Impp::Impp(const Impp &other)
+    : d(other.d)
+{
+}
+
+Impp::Impp(const QString &address)
+    : d(new Private)
+{
+    d->address = address;
+}
+
+Impp::~Impp()
+{
+
+}
+
+bool Impp::isValid() const
+{
+    return !d->address.isEmpty() && (d->type != Unknown);
+}
+
+Impp::ImppType Impp::type() const
+{
+    return d->type;
+}
+
+void Impp::setType(Impp::ImppType type)
+{
+    d->type = type;
+}
+
+void Impp::setAddress(const QString &address)
+{
+    d->address = address;
+}
+
+QString Impp::address() const
+{
+    return d->address;
+}
+
+void Impp::setParameters(const QMap<QString, QStringList> &params)
+{
+    d->parameters = params;
+}
+
+QMap<QString, QStringList> Impp::parameters() const
+{
+    return d->parameters;
+}
+
+bool Impp::operator==(const Impp &other) const
+{
+    return (d->parameters == other.parameters()) && (d->address == other.address()) && (d->type == other.type());
+}
+
+bool Impp::operator!=(const Impp &other) const
+{
+    return !(other == *this);
+}
+
+Impp &Impp::operator=(const Impp &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString Impp::toString() const
+{
+    QString str;
+    str += QLatin1String("Impp {\n");
+    str += QStringLiteral("    type: %1\n").arg(typeToString(d->type));
+    str += QStringLiteral("    address: %1\n").arg(d->address);
+    if (!d->parameters.isEmpty()) {
+        QMapIterator<QString, QStringList> i(d->parameters);
+        QString param;
+        while (i.hasNext()) {
+            i.next();
+            param += QStringLiteral("%1 %2").arg(i.key(), i.value().join(QStringLiteral(",")));
+        }
+        str += QStringLiteral("    parameters: %1\n").arg(param);
+    }
+    str += QLatin1String("}\n");
+    return str;
+}
+
+QString Impp::typeToString(ImppType type)
+{
+    switch (type) {
+    case Unknown:
+    case EndList:
+        qCWarning(KCONTACTS_LOG) << "Invalid type requested";
+        break;
+    case Skype:
+        return QStringLiteral("skype");
+    case Xmpp:
+        return QStringLiteral("xmpp");
+    case Jabber:
+        return QStringLiteral("jabber");
+    case Sip:
+        return QStringLiteral("sip");
+    case Aim:
+        return QStringLiteral("aim");
+    case Msn:
+        return QStringLiteral("msn");
+    case Twitter:
+        return QStringLiteral("twitter");
+    case GoogleTalk:
+        return QStringLiteral("googletalk");
+    case Yahoo:
+        return QStringLiteral("yahoo");
+    case Qq:
+        return QStringLiteral("qq");
+    case GaduGadu:
+        return QStringLiteral("gadugadu");
+    case Ownclound:
+        return QStringLiteral("owncloud-handle");
+    case Facebook:
+        return QStringLiteral("facebook");
+    case Icq:
+        return QStringLiteral("icq");
+    }
+    return QString();
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Impp &impp)
+{
+    return s << impp.d->parameters << impp.d->address << impp.d->type;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Impp &impp)
+{
+    int i;
+    s >> impp.d->parameters >> impp.d->address >> i;
+    impp.d->type = static_cast<Impp::ImppType>(i);
+    return s;
+}
+
diff --git a/src/impp.h b/src/impp.h
new file mode 100644 (file)
index 0000000..167ddb3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+    This file is part of libkabc.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef IMPP_H
+#define IMPP_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+
+/** @short Class that holds a IMPP for a contact.
+ *  @since 4.14.5
+ */
+namespace KContacts
+{
+class KCONTACTS_EXPORT Impp
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Impp &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Impp &);
+public:
+    Impp();
+    Impp(const Impp &other);
+    Impp(const QString &address);
+
+    ~Impp();
+
+    typedef QVector<Impp> List;
+    enum ImppType {
+        Unknown = 0,
+        Skype = 1,
+        Xmpp = 2,
+        Jabber = 3,
+        Sip = 4,
+        Aim = 5,
+        Msn = 6,
+        Twitter = 7,
+        GoogleTalk = 8,
+        Yahoo = 9,
+        Qq = 10,
+        GaduGadu = 11,
+        Ownclound = 12,
+        Icq = 13,
+        Facebook = 14,
+        EndList
+    };
+    bool isValid() const;
+
+    ImppType type() const;
+    void setType(ImppType type);
+
+    void setAddress(const QString &address);
+    QString address() const;
+
+    void setParameters(const QMap<QString, QStringList> &params);
+    QMap<QString, QStringList> parameters() const;
+
+    bool operator==(const Impp &other) const;
+    bool operator!=(const Impp &other) const;
+
+    Impp &operator=(const Impp &other);
+
+    QString toString() const;
+
+    static QString typeToString(ImppType type);
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Impp &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Impp &object);
+
+}
+
+Q_DECLARE_TYPEINFO(KContacts::Impp, Q_MOVABLE_TYPE);
+#endif // IMPP_H
diff --git a/src/key.cpp b/src/key.cpp
new file mode 100644 (file)
index 0000000..e8d6025
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "key.h"
+
+#include <klocalizedstring.h>
+#include <krandom.h>
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Key::Private : public QSharedData
+{
+public:
+    Private()
+        : mId(KRandom::randomString(8))
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mId = other.mId;
+        mBinaryData = other.mBinaryData;
+        mTextData = other.mTextData;
+        mCustomTypeString = other.mCustomTypeString;
+        mIsBinary = other.mIsBinary;
+        mType = other.mType;
+    }
+
+    QString mId;
+    QByteArray mBinaryData;
+    QString mTextData;
+    QString mCustomTypeString;
+
+    Type mType;
+    bool mIsBinary;
+};
+
+Key::Key(const QString &text, Type type)
+    : d(new Private)
+{
+    d->mTextData = text;
+    d->mIsBinary = false;
+    d->mType = type;
+}
+
+Key::Key(const Key &other)
+    : d(other.d)
+{
+}
+
+Key::~Key()
+{
+}
+
+bool Key::operator==(const Key &other) const
+{
+    if (d->mId != other.d->mId) {
+        return false;
+    }
+
+    if (d->mType != other.d->mType) {
+        return false;
+    }
+
+    if (d->mIsBinary != other.d->mIsBinary) {
+        return false;
+    }
+
+    if (d->mIsBinary) {
+        if (d->mBinaryData != other.d->mBinaryData) {
+            return false;
+        }
+    } else {
+        if (d->mTextData != other.d->mTextData) {
+            return false;
+        }
+    }
+
+    if (d->mCustomTypeString != other.d->mCustomTypeString) {
+        return false;
+    }
+
+    return true;
+}
+
+bool Key::operator!=(const Key &other) const
+{
+    return !(*this == other);
+}
+
+Key &Key::operator=(const Key &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+void Key::setId(const QString &id)
+{
+    d->mId = id;
+}
+
+QString Key::id() const
+{
+    return d->mId;
+}
+
+void Key::setBinaryData(const QByteArray &binary)
+{
+    d->mBinaryData = binary;
+    d->mIsBinary = true;
+}
+
+QByteArray Key::binaryData() const
+{
+    return d->mBinaryData;
+}
+
+void Key::setTextData(const QString &text)
+{
+    d->mTextData = text;
+    d->mIsBinary = false;
+}
+
+QString Key::textData() const
+{
+    return d->mTextData;
+}
+
+bool Key::isBinary() const
+{
+    return d->mIsBinary;
+}
+
+void Key::setType(Type type)
+{
+    d->mType = type;
+}
+
+void Key::setCustomTypeString(const QString &custom)
+{
+    d->mCustomTypeString = custom;
+}
+
+Key::Type Key::type() const
+{
+    return d->mType;
+}
+
+QString Key::customTypeString() const
+{
+    return d->mCustomTypeString;
+}
+
+QString Key::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Key {\n");
+    str += QStringLiteral("  Id: %1\n").arg(d->mId);
+    str += QStringLiteral("  Type: %1\n").arg(typeLabel(d->mType));
+    if (d->mType == Custom) {
+        str += QStringLiteral("  CustomType: %1\n").arg(d->mCustomTypeString);
+    }
+    str += QStringLiteral("  IsBinary: %1\n").
+           arg(d->mIsBinary ? QStringLiteral("true") : QStringLiteral("false"));
+    if (d->mIsBinary) {
+        str += QStringLiteral("  Binary: %1\n").
+               arg(QString::fromLatin1(d->mBinaryData.toBase64()));
+    } else {
+        str += QStringLiteral("  Text: %1\n").arg(d->mTextData);
+    }
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+Key::TypeList Key::typeList()
+{
+    static TypeList list;
+
+    if (list.isEmpty()) {
+        list << X509 << PGP << Custom;
+    }
+
+    return list;
+}
+
+QString Key::typeLabel(Type type)
+{
+    switch (type) {
+    case X509:
+        return i18nc("X.509 public key", "X509");
+        break;
+    case PGP:
+        return i18nc("Pretty Good Privacy key", "PGP");
+        break;
+    case Custom:
+        return i18nc("A custom key", "Custom");
+        break;
+    default:
+        return i18nc("another type of encryption key", "Unknown type");
+        break;
+    }
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Key &key)
+{
+    return s << key.d->mId << key.d->mType << key.d->mIsBinary << key.d->mBinaryData
+           << key.d->mTextData << key.d->mCustomTypeString;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Key &key)
+{
+    uint type;
+    s >> key.d->mId >> type >> key.d->mIsBinary >> key.d->mBinaryData >> key.d->mTextData
+      >> key.d->mCustomTypeString;
+
+    key.d->mType = Key::Type(type);
+
+    return s;
+}
diff --git a/src/key.h b/src/key.h
new file mode 100644 (file)
index 0000000..67e4776
--- /dev/null
+++ b/src/key.h
@@ -0,0 +1,186 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_KEY_H
+#define KCONTACTS_KEY_H
+
+#include "kcontacts_export.h"
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedDataPointer>
+
+namespace KContacts
+{
+
+/**
+ * @short A class to store an encryption key.
+ */
+class KCONTACTS_EXPORT Key
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Key &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Key &);
+
+public:
+    /**
+      List of keys.
+    */
+    typedef QVector<Key> List;
+
+    /**
+      Key types
+    */
+    enum Type {
+        X509,   /**< X509 key */
+        PGP,    /**< Pretty Good Privacy key */
+        Custom  /**< Custom or IANA conform key */
+    };
+
+    /**
+      List of key types.
+    */
+    typedef QList<Type> TypeList;
+
+    /**
+      Creates a new key.
+
+      @param text  The text data.
+      @param type  The key type, see Types.
+    */
+    explicit Key(const QString &text = QString(), Type type = PGP);
+
+    /**
+      Copy constructor.
+    */
+    Key(const Key &other);
+
+    /**
+      Destroys the key.
+    */
+    ~Key();
+
+    /**
+      Equality operator.
+    */
+    bool operator==(const Key &other) const;
+
+    /**
+      Not-equal operator.
+    */
+    bool operator!=(const Key &other) const;
+
+    /**
+      Assignment operator.
+
+      @param other The Key instance to assign to @c this
+    */
+    Key &operator=(const Key &other);
+
+    /**
+      Sets the unique @p identifier.
+    */
+    void setId(const QString &identifier);
+
+    /**
+      Returns the unique identifier.
+    */
+    QString id() const;
+
+    /**
+      Sets binary @p data.
+    */
+    void setBinaryData(const QByteArray &data);
+
+    /**
+      Returns the binary data.
+    */
+    QByteArray binaryData() const;
+
+    /**
+      Sets text @p data.
+    */
+    void setTextData(const QString &data);
+
+    /**
+      Returns the text data.
+    */
+    QString textData() const;
+
+    /**
+      Returns whether the key contains binary or text data.
+    */
+    bool isBinary() const;
+
+    /**
+      Sets the @p type.
+
+      @param type The type of the key
+
+      @see Type
+    */
+    void setType(Type type);
+
+    /**
+      Sets custom @p type string.
+    */
+    void setCustomTypeString(const QString &type);
+
+    /**
+      Returns the type, see Type.
+    */
+    Type type() const;
+
+    /**
+      Returns the custom type string.
+    */
+    QString customTypeString() const;
+
+    /**
+      Returns a string representation of the key.
+    */
+    QString toString() const;
+
+    /**
+      Returns a list of all available key types.
+    */
+    static TypeList typeList();
+
+    /**
+      Returns a translated label for a given key @p type.
+    */
+    static QString typeLabel(Type type);
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+/**
+  Serializes the @p key object into the @p stream.
+*/
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Key &key);
+
+/**
+  Initializes the @p key object from the @p stream.
+*/
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Key &key);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Key, Q_MOVABLE_TYPE);
+#endif
diff --git a/src/lang.cpp b/src/lang.cpp
new file mode 100644 (file)
index 0000000..ac13611
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "lang.h"
+#include <QMap>
+#include <qstringlist.h>
+#include <QDataStream>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Lang::Private : public QSharedData
+{
+public:
+    Private()
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        parameters = other.parameters;
+        language = other.language;
+    }
+    QMap<QString, QStringList> parameters;
+    QString language;
+};
+
+Lang::Lang()
+    : d(new Private)
+{
+
+}
+
+Lang::Lang(const Lang &other)
+    : d(other.d)
+{
+
+}
+
+Lang::Lang(const QString &language)
+    : d(new Private)
+{
+    d->language = language;
+}
+
+Lang::~Lang()
+{
+
+}
+
+void Lang::setLanguage(const QString &lang)
+{
+    d->language = lang;
+}
+
+QString Lang::language() const
+{
+    return d->language;
+}
+
+bool Lang::isValid() const
+{
+    return !d->language.isEmpty();
+}
+
+void Lang::setParameters(const QMap<QString, QStringList> &params)
+{
+    d->parameters = params;
+}
+
+QMap<QString, QStringList> Lang::parameters() const
+{
+    return d->parameters;
+}
+
+bool Lang::operator==(const Lang &other) const
+{
+    return (d->parameters == other.parameters()) && (d->language == other.language());
+}
+
+bool Lang::operator!=(const Lang &other) const
+{
+    return !(other == *this);
+}
+
+Lang &Lang::operator=(const Lang &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString Lang::toString() const
+{
+    QString str;
+    str += QLatin1String("Lang {\n");
+    str += QStringLiteral("    language: %1\n").arg(d->language);
+    if (!d->parameters.isEmpty()) {
+        QMapIterator<QString, QStringList> i(d->parameters);
+        QString param;
+        while (i.hasNext()) {
+            i.next();
+            param += QStringLiteral("%1 %2").arg(i.key(), i.value().join(QStringLiteral(",")));
+        }
+        str += QStringLiteral("    parameters: %1\n").arg(param);
+    }
+    str += QLatin1String("}\n");
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Lang &lang)
+{
+    return s << lang.d->parameters << lang.d->language;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Lang &lang)
+{
+    s >> lang.d->parameters >> lang.d->language;
+    return s;
+}
diff --git a/src/lang.h b/src/lang.h
new file mode 100644 (file)
index 0000000..75ac551
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef LANG_H
+#define LANG_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+
+/** @short Class that holds a Language for a contact.
+ *  @since 4.14.5
+ */
+namespace KContacts
+{
+class KCONTACTS_EXPORT Lang
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Lang &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Lang &);
+public:
+    Lang();
+    Lang(const Lang &other);
+    Lang(const QString &language);
+
+    ~Lang();
+
+    typedef QVector<Lang> List;
+
+    void setLanguage(const QString &lang);
+    QString language() const;
+
+    bool isValid() const;
+
+    void setParameters(const QMap<QString, QStringList> &params);
+    QMap<QString, QStringList> parameters() const;
+
+    bool operator==(const Lang &other) const;
+    bool operator!=(const Lang &other) const;
+
+    Lang &operator=(const Lang &other);
+
+    QString toString() const;
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Lang &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Lang &object);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Lang, Q_MOVABLE_TYPE);
+#endif // LANG_H
diff --git a/src/ldapdn.cpp b/src/ldapdn.cpp
new file mode 100644 (file)
index 0000000..ff37fdd
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+  A temporary copy to break dependency to KLDAP
+
+  This file is part of libkldap.
+  Copyright (c) 2006 Sean Harmer <sh@theharmers.co.uk>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General  Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "ldapdn_p.h"
+
+#include <algorithm>
+
+#include "kcontacts_debug.h"
+
+class Q_DECL_HIDDEN LdapDN::LdapDNPrivate
+{
+public:
+    LdapDNPrivate() : m_dn() {}
+    ~LdapDNPrivate() {}
+
+    bool isValidRDNString(const QString &rdn) const;
+    QStringList splitOnNonEscapedChar(const QString &rdn, QChar ch) const;
+
+    QString m_dn;
+};
+
+bool LdapDN::LdapDNPrivate::isValidRDNString(const QString &rdn) const
+{
+    qCDebug(KCONTACTS_LOG) << "Testing rdn:" << rdn;
+
+    // If it is a muli-valued rdn, split it into its constituent parts
+    QStringList rdnParts = splitOnNonEscapedChar(rdn, QLatin1Char('+'));
+    if (rdnParts.size() > 1) {
+        for (int i = 0; i < rdnParts.size(); i++) {
+            if (!isValidRDNString(rdnParts.at(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // Split the rdn into the attribute name and value parts
+    const QStringList components = rdn.split(QLatin1Char('='));
+
+    // We should have exactly two parts
+    if (components.size() != 2) {
+        return false;
+    }
+
+    return true;
+}
+
+QStringList LdapDN::LdapDNPrivate::splitOnNonEscapedChar(const QString &str, QChar ch) const
+{
+    QStringList strParts;
+    int index = 0;
+    int searchFrom = 0;
+    int strPartStartIndex = 0;
+    while ((index = str.indexOf(ch, searchFrom)) != -1) {
+        const QChar prev = str[std::max(0, index - 1)];
+        if (prev != QLatin1Char('\\')) {
+            // Found a component of a multi-valued RDN
+            //qCDebug(KCONTACTS_LOG) << "Found" << ch << "at index" << index;
+            QString tmp = str.mid(strPartStartIndex, index - strPartStartIndex);
+            //qCDebug(KCONTACTS_LOG) << "Adding part:" << tmp;
+            strParts.append(tmp);
+            strPartStartIndex = index + 1;
+        }
+
+        searchFrom = index + 1;
+    }
+
+    // Add on the part after the last found delimeter
+    QString tmp = str.mid(strPartStartIndex);
+    //qCDebug(KCONTACTS_LOG) << "Adding part:" << tmp;
+    strParts.append(tmp);
+
+    return strParts;
+}
+
+LdapDN::LdapDN()
+    : d(new LdapDNPrivate)
+{
+
+}
+
+LdapDN::LdapDN(const QString &dn)
+    : d(new LdapDNPrivate)
+{
+    d->m_dn = dn;
+}
+
+LdapDN::LdapDN(const LdapDN &that)
+    : d(new LdapDNPrivate)
+{
+    *d = *that.d;
+}
+
+LdapDN &LdapDN::operator=(const LdapDN &that)
+{
+    if (this == &that) {
+        return *this;
+    }
+
+    *d = *that.d;
+    return *this;
+}
+
+LdapDN::~LdapDN()
+{
+    delete d;
+}
+
+void LdapDN::clear()
+{
+    d->m_dn.clear();
+}
+
+bool LdapDN::isEmpty() const
+{
+    return d->m_dn.isEmpty();
+}
+
+QString LdapDN::toString() const
+{
+    return d->m_dn;
+}
+
+QString LdapDN::toString(int depth) const
+{
+    QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
+    if (depth >= rdns.size()) {
+        return QString();
+    }
+
+    // Construct a DN down to the requested depth
+    QString dn;
+    for (int i = depth; i >= 0; i--) {
+        dn += rdns.at(rdns.size() - 1 - i) + QLatin1Char(',');
+        qCDebug(KCONTACTS_LOG) << "dn =" << dn;
+    }
+    dn = dn.left(dn.length() - 1);   // Strip off the extraneous comma
+
+    return dn;
+}
+
+QString LdapDN::rdnString() const
+{
+    /** \TODO We should move this into the d pointer as we calculate rdns quite a lot */
+    QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
+    return rdns.at(0);
+}
+
+QString LdapDN::rdnString(int depth) const
+{
+    QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
+    if (depth >= rdns.size()) {
+        return QString();
+    }
+    return rdns.at(rdns.size() - 1 - depth);
+}
+
+bool LdapDN::isValid() const
+{
+    qCDebug(KCONTACTS_LOG) << "Testing dn:" << d->m_dn;
+
+    // Break the string into rdn's
+    QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
+
+    // Test to see if each rdn is valid
+    for (int i = 0; i < rdns.size(); i++) {
+        if (!d->isValidRDNString(rdns.at(i))) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+int LdapDN::depth() const
+{
+    QStringList rdns = d->splitOnNonEscapedChar(d->m_dn, QLatin1Char(','));
+    return rdns.size();
+}
+
+bool LdapDN::operator == (const LdapDN &rhs) const
+{
+    return d->m_dn == rhs.d->m_dn;
+}
+
+bool LdapDN::operator != (const LdapDN &rhs) const
+{
+    return d->m_dn != rhs.d->m_dn;
+}
diff --git a/src/ldapdn_p.h b/src/ldapdn_p.h
new file mode 100644 (file)
index 0000000..9ae33d1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+  A temporary copy to break dependency to KLDAP
+
+  This file is part of libkldap.
+  Copyright (c) 2006 Sean Harmer <sh@theharmers.co.uk>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General  Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef LDAPDN_P_H
+#define LDAPDN_P_H
+
+#include <QtCore/QStringList>
+
+class LdapDN
+{
+public:
+    explicit LdapDN();
+    explicit LdapDN(const QString &dn);
+
+    LdapDN(const LdapDN &that);
+    LdapDN &operator=(const LdapDN &that);
+
+    ~LdapDN();
+
+    void clear();
+
+    bool isEmpty() const;
+
+    /**
+     * \returns A QString representing the DN.
+     */
+    QString toString() const;
+
+    /**
+     * \param depth The depth of the DN to return using a zero-based index.
+     * \returns A QString representing the DN levels deep in the directory.
+     */
+    QString toString(int depth) const;
+
+    /**
+     * \returns A QString representing the RDN of this DN.
+     */
+    QString rdnString() const;
+
+    /**
+     * \param depth The depth of the RDN to return using a zero-based index.
+     * \returns A QString representing the RDN levels deep in the directory.
+     */
+    QString rdnString(int depth) const;
+
+    /**
+     * \returns True if this is a valid DN, false otherwise.
+     */
+    bool isValid() const;
+
+    /**
+     * \returns The depth of this DN in the directory.
+     */
+    int depth() const;
+
+    bool operator==(const LdapDN &rhs) const;
+
+    bool operator!=(const LdapDN &rhs) const;
+
+private:
+    class LdapDNPrivate;
+    LdapDNPrivate *const d;
+};
+
+#endif
diff --git a/src/ldif.cpp b/src/ldif.cpp
new file mode 100644 (file)
index 0000000..e90b683
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+  A temporary copy to break dependency to KLDAP
+
+  This file is part of libkldap.
+  Copyright (c) 2004-2006 Szombathelyi György <gyurco@freemail.hu>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General  Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#include "ldif_p.h"
+
+#include "kcontacts_debug.h"
+
+class Q_DECL_HIDDEN Ldif::LdifPrivate
+{
+public:
+    int mModType;
+    bool mDelOldRdn, mUrl;
+    LdapDN mDn;
+    QString mAttr, mNewRdn, mNewSuperior, mOid;
+    QByteArray mLdif, mValue;
+    EntryType mEntryType;
+
+    bool mIsNewLine, mIsComment, mCritical;
+    ParseValue mLastParseValue;
+    uint mPos, mLineNumber;
+    QByteArray mLine;
+};
+
+Ldif::Ldif() : d(new LdifPrivate)
+{
+    startParsing();
+}
+
+Ldif::Ldif(const Ldif &that) : d(new LdifPrivate)
+{
+    *d = *that.d;
+
+    startParsing();
+}
+
+Ldif &Ldif::operator=(const Ldif &that)
+{
+    if (this == &that) {
+        return *this;
+    }
+
+    *d = *that.d;
+
+    return *this;
+}
+
+Ldif::~Ldif()
+{
+    delete d;
+}
+
+QByteArray Ldif::assembleLine(const QString &fieldname,
+                              const QByteArray &value,
+                              uint linelen, bool url)
+{
+    bool safe = false;
+    bool isDn;
+    QByteArray result;
+
+    if (url) {
+        result = fieldname.toUtf8() + ":< " + value;
+    } else {
+        isDn = fieldname.toLower() == QLatin1String("dn");
+        //SAFE-INIT-CHAR
+        if (value.size() > 0 && value[0] > 0 && value[0] != '\n' &&
+                value[0] != '\r' && value[0] != ':' && value[0] != '<') {
+            safe = true;
+        }
+
+        //SAFE-CHAR
+        if (safe) {
+            for (int i = 1; i < value.size(); ++i) {
+                //allow utf-8 in Distinguished Names
+                if ((isDn && value[i] == 0) ||
+                        (!isDn && value[i] <= 0) ||
+                        value[i] == '\r' || value[i] == '\n') {
+                    safe = false;
+                    break;
+                }
+            }
+        }
+
+        if (value.size() == 0) {
+            safe = true;
+        }
+
+        if (safe) {
+            result = fieldname.toUtf8() + ": " + value;
+        } else {
+            result = fieldname.toUtf8() + ":: " + value.toBase64();
+        }
+
+        if (linelen > 0) {
+            int i = (uint)(fieldname.length() + 2) > linelen ? fieldname.length() + 2 : linelen;
+            while (i < result.length()) {
+                result.insert(i, "\n ");
+                i += linelen + 2;
+            }
+        }
+    }
+    return result;
+}
+
+QByteArray Ldif::assembleLine(const QString &fieldname, const QString &value,
+                              uint linelen, bool url)
+{
+    return assembleLine(fieldname, value.toUtf8(), linelen, url);
+}
+
+bool Ldif::splitLine(const QByteArray &line, QString &fieldname, QByteArray &value)
+{
+    int position;
+    QByteArray tmp;
+    int linelen;
+
+//  qCDebug(KCONTACTS_LOG) << "line:" << QString::fromUtf8(line);
+
+    position = line.indexOf(":");
+    if (position == -1) {
+        // strange: we did not find a fieldname
+        fieldname = QLatin1String("");
+        value = line.trimmed();
+//    qCDebug(KCONTACTS_LOG) << "value :" << value[0];
+        return false;
+    }
+
+    linelen = line.size();
+    fieldname = QString::fromUtf8(line.left(position).trimmed());
+
+    if (linelen > (position + 1) && line[ position + 1 ] == ':') {
+        // String is BASE64 encoded -> decode it now.
+        if (linelen <= (position + 3)) {
+            value.resize(0);
+            return false;
+        }
+        value = QByteArray::fromBase64(line.mid(position + 3));
+        return false;
+    }
+
+    if (linelen > (position + 1) && line[ position + 1 ] == '<') {
+        // String is an URL.
+        if (linelen <= (position + 3)) {
+            value.resize(0);
+            return false;
+        }
+        value = QByteArray::fromBase64(line.mid(position + 3));
+        return true;
+    }
+
+    if (linelen <= (position + 2)) {
+        value.resize(0);
+        return false;
+    }
+    value = line.mid(position + 2);
+    return false;
+}
+
+bool Ldif::splitControl(const QByteArray &line, QString &oid, bool &critical,
+                        QByteArray &value)
+{
+    QString tmp;
+    critical = false;
+    bool url = splitLine(line, tmp, value);
+
+    qCDebug(KCONTACTS_LOG) << "value:" << QString::fromUtf8(value);
+    if (tmp.isEmpty()) {
+        tmp = QString::fromUtf8(value);
+        value.resize(0);
+    }
+    if (tmp.endsWith(QLatin1String("true"))) {
+        critical = true;
+        tmp.truncate(tmp.length() - 5);
+    } else if (tmp.endsWith(QLatin1String("false"))) {
+        critical = false;
+        tmp.truncate(tmp.length() - 6);
+    }
+    oid = tmp;
+    return url;
+}
+
+Ldif::ParseValue Ldif::processLine()
+{
+
+    if (d->mIsComment) {
+        return None;
+    }
+
+    ParseValue retval = None;
+    if (d->mLastParseValue == EndEntry) {
+        d->mEntryType = Entry_None;
+    }
+
+    d->mUrl = splitLine(d->mLine, d->mAttr, d->mValue);
+
+    QString attrLower = d->mAttr.toLower();
+
+    switch (d->mEntryType) {
+    case Entry_None:
+        if (attrLower == QLatin1String("version")) {
+            if (!d->mDn.isEmpty()) {
+                retval = Err;
+            }
+        } else if (attrLower == QLatin1String("dn")) {
+            qCDebug(KCONTACTS_LOG) << "ldapentry dn:" << QString::fromUtf8(d->mValue);
+            d->mDn = LdapDN(QString::fromUtf8(d->mValue));
+            d->mModType = Mod_None;
+            retval = NewEntry;
+        } else if (attrLower == QLatin1String("changetype")) {
+            if (d->mDn.isEmpty()) {
+                retval = Err;
+            } else {
+                QString tmpval = QString::fromUtf8(d->mValue);
+                qCDebug(KCONTACTS_LOG) << "changetype:" << tmpval;
+                if (tmpval == QLatin1String("add")) {
+                    d->mEntryType = Entry_Add;
+                } else if (tmpval == QLatin1String("delete")) {
+                    d->mEntryType = Entry_Del;
+                } else if (tmpval == QLatin1String("modrdn") || tmpval == QLatin1String("moddn")) {
+                    d->mNewRdn = QLatin1String("");
+                    d->mNewSuperior = QLatin1String("");
+                    d->mDelOldRdn = true;
+                    d->mEntryType = Entry_Modrdn;
+                } else if (tmpval == QLatin1String("modify")) {
+                    d->mEntryType = Entry_Mod;
+                } else {
+                    retval = Err;
+                }
+            }
+        } else if (attrLower == QLatin1String("control")) {
+            d->mUrl = splitControl(d->mValue, d->mOid, d->mCritical, d->mValue);
+            retval = Control;
+        } else if (!d->mAttr.isEmpty() && d->mValue.size() > 0) {
+            d->mEntryType = Entry_Add;
+            retval = Item;
+        }
+        break;
+    case Entry_Add:
+        if (d->mAttr.isEmpty() && d->mValue.size() == 0) {
+            retval = EndEntry;
+        } else {
+            retval = Item;
+        }
+        break;
+    case Entry_Del:
+        if (d->mAttr.isEmpty() && d->mValue.size() == 0) {
+            retval = EndEntry;
+        } else {
+            retval = Err;
+        }
+        break;
+    case Entry_Mod:
+        if (d->mModType == Mod_None) {
+            qCDebug(KCONTACTS_LOG) << "new modtype" << d->mAttr;
+            if (d->mAttr.isEmpty() && d->mValue.size() == 0) {
+                retval = EndEntry;
+            } else if (attrLower == QLatin1String("add")) {
+                d->mModType = Mod_Add;
+            } else if (attrLower == QLatin1String("replace")) {
+                d->mModType = Mod_Replace;
+                d->mAttr = QString::fromUtf8(d->mValue);
+                d->mValue = QByteArray();
+                retval = Item;
+            } else if (attrLower == QLatin1String("delete")) {
+                d->mModType = Mod_Del;
+                d->mAttr = QString::fromUtf8(d->mValue);
+                d->mValue = QByteArray();
+                retval = Item;
+            } else {
+                retval = Err;
+            }
+        } else {
+            if (d->mAttr.isEmpty()) {
+                if (QString::fromUtf8(d->mValue) == QLatin1String("-")) {
+                    d->mModType = Mod_None;
+                } else if (d->mValue.size() == 0) {
+                    retval = EndEntry;
+                } else {
+                    retval = Err;
+                }
+            } else {
+                retval = Item;
+            }
+        }
+        break;
+    case Entry_Modrdn:
+        if (d->mAttr.isEmpty() && d->mValue.size() == 0) {
+            retval = EndEntry;
+        } else if (attrLower == QLatin1String("newrdn")) {
+            d->mNewRdn = QString::fromUtf8(d->mValue);
+        } else if (attrLower == QLatin1String("newsuperior")) {
+            d->mNewSuperior = QString::fromUtf8(d->mValue);
+        } else if (attrLower == QLatin1String("deleteoldrdn")) {
+            if (d->mValue.size() > 0 && d->mValue[0] == '0') {
+                d->mDelOldRdn = false;
+            } else if (d->mValue.size() > 0 && d->mValue[0] == '1') {
+                d->mDelOldRdn = true;
+            } else {
+                retval = Err;
+            }
+        } else {
+            retval = Err;
+        }
+        break;
+    }
+    return retval;
+}
+
+Ldif::ParseValue Ldif::nextItem()
+{
+    ParseValue retval = None;
+    char c = 0;
+
+    while (retval == None) {
+        if (d->mPos < (uint)d->mLdif.size()) {
+            c = d->mLdif[d->mPos];
+            d->mPos++;
+            if (d->mIsNewLine && c == '\r') {
+                continue; //handle \n\r line end
+            }
+            if (d->mIsNewLine && (c == ' ' || c == '\t')) {     //line folding
+                d->mIsNewLine = false;
+                continue;
+            }
+            if (d->mIsNewLine) {
+                d->mIsNewLine = false;
+                retval = processLine();
+                d->mLastParseValue = retval;
+                d->mLine.resize(0);
+                d->mIsComment = (c == '#');
+            }
+            if (c == '\n' || c == '\r') {
+                d->mLineNumber++;
+                d->mIsNewLine = true;
+                continue;
+            }
+        } else {
+            retval = MoreData;
+            break;
+        }
+
+        if (!d->mIsComment) {
+            d->mLine += c;
+        }
+    }
+    return retval;
+}
+
+void Ldif::endLdif()
+{
+    QByteArray tmp(3, '\n');
+    d->mLdif = tmp;
+    d->mPos = 0;
+}
+
+void Ldif::startParsing()
+{
+    d->mPos = d->mLineNumber = 0;
+    d->mDelOldRdn = false;
+    d->mEntryType = Entry_None;
+    d->mModType = Mod_None;
+    d->mDn = LdapDN();
+    d->mNewRdn.clear();
+    d->mNewSuperior.clear();
+    d->mLine = QByteArray();
+    d->mIsNewLine = false;
+    d->mIsComment = false;
+    d->mLastParseValue = None;
+}
+
+void Ldif::setLdif(const QByteArray &ldif)
+{
+    d->mLdif = ldif;
+    d->mPos = 0;
+}
+
+Ldif::EntryType Ldif::entryType() const
+{
+    return d->mEntryType;
+}
+
+int Ldif::modType() const
+{
+    return d->mModType;
+}
+
+LdapDN Ldif::dn() const
+{
+    return d->mDn;
+}
+
+QString Ldif::newRdn() const
+{
+    return d->mNewRdn;
+}
+
+QString Ldif::newSuperior() const
+{
+    return d->mNewSuperior;
+}
+
+bool Ldif::delOldRdn() const
+{
+    return d->mDelOldRdn;
+}
+
+QString Ldif::attr() const
+{
+    return d->mAttr;
+}
+
+QByteArray Ldif::value() const
+{
+    return d->mValue;
+}
+
+bool Ldif::isUrl() const
+{
+    return d->mUrl;
+}
+
+bool Ldif::isCritical() const
+{
+    return d->mCritical;
+}
+
+QString Ldif::oid() const
+{
+    return d->mOid;
+}
+
+uint Ldif::lineNumber() const
+{
+    return d->mLineNumber;
+}
diff --git a/src/ldif_p.h b/src/ldif_p.h
new file mode 100644 (file)
index 0000000..a850923
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+  A temporary copy to break dependency to KLDAP
+
+  This file is part of libkldap.
+  Copyright (c) 2004-2006 Szombathelyi György <gyurco@freemail.hu>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General  Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef LDIF_P_H
+#define LDIF_P_H
+
+#include <QtCore/QString>
+#include <QtCore/QByteArray>
+
+#include "ldapdn_p.h"
+
+/**
+ * Ldif
+ *
+ * Ldif implements an RFC 2849 compliant Ldif parser. Ldif files are used to
+ * represent directory information on LDAP-based servers, or to describe a set
+ * of changes which are to be applied to a directory.
+ */
+class Ldif
+{
+public:
+
+    typedef enum {
+        None, NewEntry, EndEntry, Item, Control, Err, MoreData
+    } ParseValue;
+
+    typedef enum {
+        Entry_None, Entry_Add, Entry_Del, Entry_Mod, Entry_Modrdn
+    } EntryType;
+
+    typedef enum {
+        Mod_None, Mod_Add, Mod_Replace, Mod_Del
+    } ModType;
+
+    Ldif();
+
+    Ldif(const Ldif &that);
+    Ldif &operator=(const Ldif &that);
+
+    virtual ~Ldif();
+
+    /**
+     * Assembles fieldname and value into a valid Ldif line, BASE64 encodes the
+     * value if necessary and optionally splits into more lines.
+     * @param fieldname The name of the entry.
+     * @param value The value of the entry.
+     * @param linelen Maximum length of the lines in the result.
+     * @param url If true, encode value as url ( use :< ).
+     */
+    static QByteArray assembleLine(const QString &fieldname,
+                                   const QByteArray &value, uint linelen = 0,
+                                   bool url = false);
+    /**
+     * This is the same as the above function, the only difference that
+     * this accepts QString as the value.
+     */
+    static QByteArray assembleLine(const QString &fieldname,
+                                   const QString &value, uint linelen = 0,
+                                   bool url = false);
+
+    /**
+     * Splits one line from an Ldif file to attribute and value components.
+     * @return true if value is an URL, false otherwise
+     */
+    static bool splitLine(const QByteArray &line, QString &fieldname,
+                          QByteArray &value);
+
+    /**
+     * Splits a control specification (without the "control:" directive)
+     * @param line is the control directive
+     * @param oid will contain the OID
+     * @param critical will contain the criticality of control
+     * @param value is the control value
+     */
+    static bool splitControl(const QByteArray &line, QString &oid,
+                             bool &critical, QByteArray &value);
+
+    /**
+     * Starts the parsing of a new Ldif
+     */
+    void startParsing();
+
+    /**
+     * Process one Ldif line
+     */
+    ParseValue processLine();
+
+    /**
+     * Process the Ldif until a complete item can be returned
+     * @return NewEntry if a new DN encountered, Item if a new item returned,
+     * Err if the Ldif contains error, EndEntry if the parser reached the end
+     * of the current entry and MoreData if the parser encountered the end of
+     * the current chunk of the Ldif.
+     *
+     * If you want to finish the parsing after receiving MoreData, then call
+     * endLdif(), so the parser can safely flush the current entry.
+     */
+    ParseValue nextItem();
+
+    /**
+     * Sets a chunk of Ldif. Call before startParsing(), or if nextItem()
+     * returned MoreData.
+     */
+    void setLdif(const QByteArray &ldif);
+
+    /**
+     * Indicates the end of the Ldif file/stream. Call if nextItem() returned
+     * MoreData, but actually you don't have more data.
+     */
+    void endLdif();
+
+    /**
+     * Returns the requested LDAP operation extracted from the current entry.
+     */
+    EntryType entryType() const;
+
+    /**
+     * Returns the LDAP modify request type if entryType() returned Entry_Mod.
+     */
+    int modType() const;
+
+    /**
+     * Returns the Distinguished Name of the current entry.
+     */
+    LdapDN dn() const;
+
+    /**
+     * Returns the new Relative Distinguished Name if modType() returned
+     * Entry_Modrdn.
+     */
+    QString newRdn() const;
+
+    /**
+     * Returns the new parent of the entry if modType() returned Entry_Modrdn.
+     */
+    QString newSuperior() const;
+
+    /**
+     * Returns if the delete of the old RDN is required.
+     */
+    bool delOldRdn() const;
+
+    /**
+     * Returns the attribute name.
+     */
+    QString attr() const;
+
+    /**
+     * Returns the attribute value.
+     */
+    QByteArray value() const;
+
+    /**
+     * Returns if val() is an url
+     */
+    bool isUrl() const;
+
+    /**
+     * Returns the criticality level when modType() returned Control.
+     */
+    bool isCritical() const;
+
+    /**
+     * Returns the OID when modType() returned Control.
+     */
+    QString oid() const;
+
+    /**
+     * Returns the line number which the parser processes.
+     */
+    uint lineNumber() const;
+
+private:
+    class LdifPrivate;
+    LdifPrivate *const d;
+};
+
+#endif
diff --git a/src/phonenumber.cpp b/src/phonenumber.cpp
new file mode 100644 (file)
index 0000000..de829a3
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "phonenumber.h"
+
+#include <klocalizedstring.h>
+#include <krandom.h>
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+static QString cleanupNumber(const QString &input)
+{
+    return input.simplified();
+}
+
+class Q_DECL_HIDDEN PhoneNumber::Private : public QSharedData
+{
+public:
+    Private(Type type)
+        : mId(KRandom::randomString(8)), mType(type)
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mId = other.mId;
+        mType = other.mType;
+        mNumber = other.mNumber;
+    }
+
+    QString mId;
+    QString mNumber;
+    Type mType;
+};
+
+PhoneNumber::PhoneNumber()
+    : d(new Private(Home))
+{
+}
+
+PhoneNumber::PhoneNumber(const QString &number, Type type)
+    : d(new Private(type))
+{
+    d->mNumber = cleanupNumber(number);
+}
+
+PhoneNumber::PhoneNumber(const PhoneNumber &other)
+    : d(other.d)
+{
+}
+
+PhoneNumber::~PhoneNumber()
+{
+}
+
+bool PhoneNumber::operator==(const PhoneNumber &other) const
+{
+    if (d->mId != other.d->mId) {
+        return false;
+    }
+
+    if (d->mNumber != other.d->mNumber) {
+        return false;
+    }
+
+    if (d->mType != other.d->mType) {
+        return false;
+    }
+
+    return true;
+}
+
+bool PhoneNumber::operator!=(const PhoneNumber &other) const
+{
+    return !(other == *this);
+}
+
+PhoneNumber &PhoneNumber::operator=(const PhoneNumber &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool PhoneNumber::isEmpty() const
+{
+    return d->mNumber.isEmpty();
+}
+
+void PhoneNumber::setId(const QString &id)
+{
+    d->mId = id;
+}
+
+QString PhoneNumber::id() const
+{
+    return d->mId;
+}
+
+void PhoneNumber::setNumber(const QString &number)
+{
+    d->mNumber = cleanupNumber(number);
+}
+
+QString PhoneNumber::number() const
+{
+    return d->mNumber;
+}
+
+void PhoneNumber::setType(Type type)
+{
+    d->mType = type;
+}
+
+PhoneNumber::Type PhoneNumber::type() const
+{
+    return d->mType;
+}
+
+QString PhoneNumber::typeLabel() const
+{
+    return typeLabel(type());
+}
+
+PhoneNumber::TypeList PhoneNumber::typeList()
+{
+    static TypeList list;
+
+    if (list.isEmpty()) {
+        list << Home << Work << Msg << Pref << Voice << Fax << Cell << Video
+             << Bbs << Modem << Car << Isdn << Pcs << Pager << Undefined;
+    }
+
+    return list;
+}
+
+QString PhoneNumber::typeFlagLabel(TypeFlag type)
+{
+    switch (type) {
+    case Undefined:
+        return i18nc("Undefined phone type", "Telephone number");
+    case Home:
+        return i18nc("Home phone", "Home");
+    case Work:
+        return i18nc("Work phone", "Work");
+    case Msg:
+        return i18n("Messenger");
+    case Pref:
+        return i18nc("Preferred phone", "Preferred");
+    case Voice:
+        return i18n("Voice");
+    case Fax:
+        return i18n("Fax");
+    case Cell:
+        return i18nc("Mobile Phone", "Mobile");
+    case Video:
+        return i18nc("Video phone", "Video");
+    case Bbs:
+        return i18n("Mailbox");
+    case Modem:
+        return i18n("Modem");
+    case Car:
+        return i18nc("Car Phone", "Car");
+    case Isdn:
+        return i18n("ISDN");
+    case Pcs:
+        return i18n("PCS");
+    case Pager:
+        return i18n("Pager");
+    default:
+        return i18nc("another type of phone", "Other");
+    }
+}
+
+QString PhoneNumber::typeLabel(Type type)
+{
+    QString label;
+    bool first = true;
+
+    // special cases
+    // Pref stand alone -> Preferred Number
+    // Home+Fax or Work+Fax -> combine as initial string
+    if (type == Pref) {
+        return i18n("Preferred Number");
+    }
+
+    if (type & Fax) {
+        if (type & Home) {
+            label = i18n("Home Fax");
+            first = false;
+            type &= ~Fax;
+            type &= ~Home;
+        } else if (type & Work) {
+            label = i18n("Work Fax");
+            first = false;
+            type &= ~Fax;
+            type &= ~Work;
+        }
+    }
+
+    const TypeList list = typeList();
+
+    TypeList::ConstIterator it;
+    TypeList::ConstIterator end(list.end());
+    for (it = list.begin(); it != end; ++it) {
+        // these are actually flags
+        const TypeFlag flag = static_cast<TypeFlag>(static_cast<int>(*it));
+        if (type & flag) {
+            if (!first) {
+                label.append(QLatin1Char('/'));
+            }
+
+            label.append(typeFlagLabel(flag));
+
+            if (first) {
+                first = false;
+            }
+        }
+    }
+
+    return label;
+}
+
+QString PhoneNumber::toString() const
+{
+    QString str;
+
+    str += QLatin1String("PhoneNumber {\n");
+    str += QStringLiteral("    Id: %1\n").arg(d->mId);
+    str += QStringLiteral("    Type: %1\n").arg(typeLabel(d->mType));
+    str += QStringLiteral("    Number: %1\n").arg(d->mNumber);
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const PhoneNumber &phone)
+{
+    return s << phone.d->mId << (uint)phone.d->mType << phone.d->mNumber;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, PhoneNumber &phone)
+{
+    uint type;
+    s >> phone.d->mId >> type >> phone.d->mNumber;
+    phone.d->mType = PhoneNumber::Type(type);
+
+    return s;
+}
diff --git a/src/phonenumber.h b/src/phonenumber.h
new file mode 100644 (file)
index 0000000..13eaa9b
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_PHONENUMBER_H
+#define KCONTACTS_PHONENUMBER_H
+
+#include "kcontacts_export.h"
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+
+namespace KContacts
+{
+
+/**
+ * @short Phonenumber information.
+ *
+ * This class provides phone number information. A phone number is classified by
+ * a type. The following types are available, it's possible to use multiple types
+ * Types for a number by combining them through a logical or.
+*/
+class KCONTACTS_EXPORT PhoneNumber
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const PhoneNumber &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, PhoneNumber &);
+
+public:
+    /**
+      Phone number types.
+    */
+    enum TypeFlag {
+        Home = 1,     /**< Home number */
+        Work = 2,     /**< Office number */
+        Msg = 4,      /**< Messaging */
+        Pref = 8,     /**< Preferred number */
+        Voice = 16,   /**< Voice */
+        Fax = 32,     /**< Fax machine */
+        Cell = 64,    /**< Cell phone */
+        Video = 128,  /**< Video phone */
+        Bbs = 256,    /**< Mailbox */
+        Modem = 512,  /**< Modem */
+        Car = 1024,   /**< Car phone */
+        Isdn = 2048,  /**< ISDN connection */
+        Pcs = 4096,   /**< Personal Communication Service*/
+        Pager = 8192,  /**< Pager */
+        Undefined = 16384 /** Undefined number type */
+    };
+
+    Q_DECLARE_FLAGS(Type, TypeFlag)
+
+    /**
+     * List of phone number types.
+     */
+    typedef QVector<TypeFlag> TypeList;
+
+    /**
+     * List of phone numbers.
+     */
+    typedef QVector<PhoneNumber> List;
+
+    /**
+     * Creates an empty phone number object.
+     */
+    PhoneNumber();
+
+    /**
+     * Creates a phone number object.
+     *
+     * @param number Number
+     * @param type   Type as defined in enum. Multiple types can be
+     *               specified by combining them by a logical or.
+     */
+    PhoneNumber(const QString &number, Type type = Home);   //krazy:exclude=explicit
+
+    /**
+     * Copy constructor.
+     *
+     * Fast operation, PhoneNumber's data is implicitly shared.
+     *
+     * @param other The PhoneNumber object to copy from
+     */
+    PhoneNumber(const PhoneNumber &other);
+
+    /**
+     * Destroys the phone number.
+     */
+    ~PhoneNumber();
+
+    /**
+     * Equality operator.
+     *
+     * @return @c true if number, type and identifier are equal,
+     *         otherwise @c false
+     */
+    bool operator==(const PhoneNumber &other) const;
+
+    /**
+     * Not-Equal operator.
+     */
+    bool operator!=(const PhoneNumber &other) const;
+
+    /**
+     * Assignment operator.
+     *
+     * Fast operation, PhoneNumber's data is implicitly shared.
+     *
+     * @param other The PhoneNumber object to asssign to @c this
+     */
+    PhoneNumber &operator=(const PhoneNumber &other);
+
+    /**
+     * Returns true, if the phone number is empty.
+     */
+    bool isEmpty() const;
+
+    /**
+     * Sets the unique @p identifier.
+     */
+    void setId(const QString &identifier);
+
+    /**
+     * Returns the unique identifier.
+     */
+    QString id() const;
+
+    /**
+     * Sets the phone @p number.
+     */
+    void setNumber(const QString &number);
+
+    /**
+     * Returns the phone number.
+     */
+    QString number() const;
+
+    /**
+     * Sets the @p type.
+     * Multiple types can be specified by combining them by a logical or.
+     *
+     * @param type The #Type of the phone number
+     */
+    void setType(Type type);
+
+    /**
+     * Returns the type. Can be a multiple types combined by a logical or.
+     *
+     * @see #TypeFlag
+     * @see typeLabel()
+     */
+    Type type() const;
+
+    /**
+     * Returns a translated string of the address' type.
+     */
+    QString typeLabel() const;
+
+    /**
+     * Returns a list of all available types
+     */
+    static TypeList typeList();
+
+    /**
+     * Returns the translated label for phone number @p type.
+     *
+     * In opposite to typeFlagLabel( TypeFlag type ), it returns all types
+     * of the phone number concatenated by '/'.
+     *
+     * @param type An OR'ed combination of #TypeFlag
+     *
+     * @see type()
+     */
+    static QString typeLabel(Type type);
+
+    /**
+     * Returns the translated label for phone number @p type.
+     *
+     * @param type An OR'ed combination of #TypeFlag
+     *
+     * @see typeLabel()
+     * @since 4.5
+     */
+    static QString typeFlagLabel(TypeFlag type);
+
+    /**
+     * Returns a string representation of the phone number.
+     */
+    QString toString() const;
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(PhoneNumber::Type)
+
+/**
+ * Serializes the phone @p number object into the @p stream.
+ *
+ * @param stream The stream to write into
+ * @param number The phone number object to serialize
+ */
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const PhoneNumber &number);
+
+/**
+ * Initializes the phone @p number object from the @p stream.
+ *
+ * @param stream The stream to read from
+ * @param number The phone number object to deserialize into
+ */
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, const PhoneNumber &number);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::PhoneNumber, Q_MOVABLE_TYPE);
+#endif
diff --git a/src/picture.cpp b/src/picture.cpp
new file mode 100644 (file)
index 0000000..412c4d6
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "picture.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QSharedData>
+
+namespace KContacts
+{
+
+class PicturePrivate : public QSharedData
+{
+public:
+    PicturePrivate()
+        : mIntern(false)
+    {
+    }
+
+    PicturePrivate(const PicturePrivate &other)
+        : QSharedData(other)
+    {
+        mUrl = other.mUrl;
+        mType = other.mType;
+        mData = other.mData;
+        mIntern = other.mIntern;
+    }
+
+    QString mUrl;
+    QString mType;
+    mutable QImage mData;
+    mutable QByteArray mRawData;
+
+    bool mIntern;
+};
+
+}
+
+Q_GLOBAL_STATIC_WITH_ARGS(QSharedDataPointer<KContacts::PicturePrivate>, s_sharedEmpty, (new KContacts::PicturePrivate))
+
+using namespace KContacts;
+
+Picture::Picture()
+    : d(*s_sharedEmpty())
+{
+}
+
+Picture::Picture(const QString &url)
+    : d(new PicturePrivate)
+{
+    d->mUrl = url;
+}
+
+Picture::Picture(const QImage &data)
+    : d(new PicturePrivate)
+{
+    setData(data);
+}
+
+Picture::Picture(const Picture &other)
+    : d(other.d)
+{
+}
+
+Picture::~Picture()
+{
+}
+
+Picture &Picture::operator=(const Picture &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool Picture::operator==(const Picture &p) const
+{
+    if (d->mIntern != p.d->mIntern) {
+        return false;
+    }
+
+    if (d->mType != p.d->mType) {
+        return false;
+    }
+
+    if (d->mIntern) {
+        if (!d->mData.isNull() && !p.d->mData.isNull()) {
+            if (d->mData != p.d->mData) {
+                return false;
+            }
+        } else if (!d->mRawData.isEmpty() && !p.d->mRawData.isEmpty()) {
+            if (d->mRawData != p.d->mRawData) {
+                return false;
+            }
+        } else if ((!d->mData.isNull() || !d->mRawData.isEmpty()) &&
+                   (!p.d->mData.isNull() || !p.d->mRawData.isEmpty())) {
+            if (data() != p.data()) {
+                return false;
+            }
+        } else {
+            // if one picture is empty and the other is not
+            return false;
+        }
+    } else {
+        if (d->mUrl != p.d->mUrl) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool Picture::operator!=(const Picture &p) const
+{
+    return !(p == *this);
+}
+
+bool Picture::isEmpty() const
+{
+    return
+        ((d->mIntern == false && d->mUrl.isEmpty()) ||
+         (d->mIntern == true && d->mData.isNull() && d->mRawData.isEmpty()));
+}
+
+void Picture::setUrl(const QString &url)
+{
+    d->mUrl = url;
+    d->mType.clear();
+    d->mIntern = false;
+}
+
+void Picture::setUrl(const QString &url, const QString &type)
+{
+    d->mUrl = url;
+    d->mType = type;
+    d->mIntern = false;
+}
+
+void Picture::setData(const QImage &data)
+{
+    d->mRawData.clear();
+    d->mData = data;
+    d->mIntern = true;
+
+// set the type, the raw data will have when accessed through Picture::rawData()
+    if (!d->mData.hasAlphaChannel()) {
+        d->mType = QStringLiteral("jpeg");
+    } else {
+        d->mType = QStringLiteral("png");
+    }
+}
+
+void Picture::setRawData(const QByteArray &rawData, const QString &type)
+{
+    d->mRawData = rawData;
+    d->mType = type;
+    d->mData = QImage();
+    d->mIntern = true;
+}
+
+void Picture::setType(const QString &type)
+{
+    d->mType = type;
+}
+
+bool Picture::isIntern() const
+{
+    return d->mIntern;
+}
+
+QString Picture::url() const
+{
+    return d->mUrl;
+}
+
+QImage Picture::data() const
+{
+    if (d->mData.isNull() && !d->mRawData.isEmpty()) {
+        d->mData.loadFromData(d->mRawData);
+    }
+
+    return d->mData;
+}
+
+QByteArray Picture::rawData() const
+{
+    if (d->mRawData.isEmpty() && !d->mData.isNull()) {
+        QBuffer buffer(&d->mRawData);
+        buffer.open(QIODevice::WriteOnly);
+
+        // d->mType was already set accordingly by Picture::setData()
+        d->mData.save(&buffer, d->mType.toUpper().toLatin1().data());
+    }
+
+    return d->mRawData;
+}
+
+QString Picture::type() const
+{
+    return d->mType;
+}
+
+QString Picture::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Picture {\n");
+    str += QStringLiteral("  Type: %1\n").arg(d->mType);
+    str += QStringLiteral("  IsIntern: %1\n").
+           arg(d->mIntern ? QStringLiteral("true") : QStringLiteral("false"));
+    if (d->mIntern) {
+        str += QStringLiteral("  Data: %1\n").arg(QString::fromLatin1(rawData().toBase64()));
+    } else {
+        str += QStringLiteral("  Url: %1\n").arg(d->mUrl);
+    }
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Picture &picture)
+{
+    return s << picture.d->mIntern << picture.d->mUrl << picture.d->mType << picture.data();
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Picture &picture)
+{
+    s >> picture.d->mIntern >> picture.d->mUrl >> picture.d->mType >> picture.d->mData;
+
+    return s;
+}
diff --git a/src/picture.h b/src/picture.h
new file mode 100644 (file)
index 0000000..554c27b
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_PICTURE_H
+#define KCONTACTS_PICTURE_H
+
+#include "kcontacts_export.h"
+
+#include <QtCore/QDataStream>
+#include <QtCore/QString>
+#include <QtCore/QSharedDataPointer>
+#include <QImage>
+
+namespace KContacts
+{
+
+class PicturePrivate;
+
+/**
+  A class to store a picture of an addressee. It can store the data directly or
+  an url reference to a picture.
+*/
+class KCONTACTS_EXPORT Picture
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Picture &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Picture &);
+
+public:
+    /**
+     * Creates an empty picture.
+     */
+    Picture();
+
+    /**
+     * Creates a picture which points to the given url.
+     *
+     * @param url A URL that describes the location of the picture file.
+     */
+    Picture(const QString &url);
+
+    /**
+     * Creates a picture with the given data.
+     *
+     * @param data The raw data of the picture.
+     */
+    Picture(const QImage &data);
+
+    /**
+     * Copy constructor.
+     *
+     * Fast operation, Picture's data is implicitly shared.
+     *
+     * @param picture The Picture instance to copy from
+     */
+    Picture(const Picture &picture);
+
+    /**
+     * Destructor.
+     */
+    ~Picture();
+
+    typedef QVector<Picture> List;
+    /**
+     * Assignment operator
+     *
+     * Fast operation, Picture's data is implicitly shared.
+     *
+     * @param other The Picture instance to assign to @c this
+     */
+    Picture &operator=(const Picture &other);
+
+    /**
+     * Equality operator.
+     */
+    bool operator==(const Picture &other) const;
+
+    /**
+     * Not-Equal operator.
+     */
+    bool operator!=(const Picture &other) const;
+
+    /**
+     * Returns true, if the picture is empty.
+     */
+    bool isEmpty() const;
+
+    /**
+     * Sets a URL for the location of the picture file. When using this
+     * function, isIntern() will return 'false' until you use
+     * setData().
+     * This also clears the type, as it is unknown.
+     *
+     * @param url  The location URL of the picture file.
+     */
+    void setUrl(const QString &url);
+
+    /**
+     * Sets a URL for the location of the picture file. When using this
+     * function, isIntern() will return 'false' until you use
+     * setData().
+     *
+     * @param url  The location URL of the picture file.
+     * @param type  The encoding format of the image, e.g. jpeg or png
+     * @since 4.10
+     */
+    void setUrl(const QString &url, const QString &type);
+
+    /**
+     * Sets the image data of the picture. When using this function,
+     * isIntern() will return 'true' until you use setUrl().
+     * This also sets type to "png" or "jpeg" depending
+     * on whether the image has an alpha channel or not.
+     *
+     * @param data  The image data of the picture.
+     */
+    void setData(const QImage &data);
+
+    /**
+     * Sets the raw data of the picture. When using this function,
+     * isIntern() will return 'true' until you use setUrl().
+     *
+     * @param rawData  The raw data of the picture.
+     * @param type  The encoding format of the image, e.g. jpeg or png
+     * @since 4.10
+     */
+    void setRawData(const QByteArray &rawData, const QString &type);
+
+    /**
+     * Sets the type of the picture.
+     * @param type the picture's data type
+     * @deprecated type should only be set along with setRawData()
+     */
+    void KCONTACTS_DEPRECATED setType(const QString &type);
+
+    /**
+     * Returns whether the picture is described by a URL (extern) or
+     * by the raw data (intern).
+     * When this method returns 'true' you can use data() to
+     * get the raw data. Otherwise you can request the URL of this
+     * picture by url() and load the raw data from that location.
+     */
+    bool isIntern() const;
+
+    /**
+     * Returns the location URL of this picture.
+     */
+    QString url() const;
+
+    /**
+     * Returns the image data of this picture.
+     */
+    QImage data() const;
+
+    /**
+     * Returns the raw data of this picture.
+     *
+     * @since 4.10
+     */
+    QByteArray rawData() const;
+
+    /**
+     * Returns the type of this picture.
+     */
+    QString type() const;
+
+    /**
+     * Returns string representation of the picture.
+     */
+    QString toString() const;
+
+private:
+    QSharedDataPointer<PicturePrivate> d;
+};
+
+/**
+ * Serializes the @p picture object into the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Picture &picture);
+
+/**
+ * Initializes the @p picture object from the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Picture &picture);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Picture, Q_MOVABLE_TYPE);
+#endif
diff --git a/src/related.cpp b/src/related.cpp
new file mode 100644 (file)
index 0000000..244ea26
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "related.h"
+#include <QMap>
+#include <qstringlist.h>
+#include <QDataStream>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Related::Private : public QSharedData
+{
+public:
+    Private()
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        parameters = other.parameters;
+        relatedTo = other.relatedTo;
+    }
+    QMap<QString, QStringList> parameters;
+    QString relatedTo;
+};
+
+Related::Related()
+    : d(new Private)
+{
+
+}
+
+Related::Related(const Related &other)
+    : d(other.d)
+{
+
+}
+
+Related::Related(const QString &relatedTo)
+    : d(new Private)
+{
+    d->relatedTo = relatedTo;
+}
+
+Related::~Related()
+{
+
+}
+
+void Related::setRelated(const QString &relatedTo)
+{
+    d->relatedTo = relatedTo;
+}
+
+QString Related::related() const
+{
+    return d->relatedTo;
+}
+
+bool Related::isValid() const
+{
+    return !d->relatedTo.isEmpty();
+}
+
+void Related::setParameters(const QMap<QString, QStringList> &params)
+{
+    d->parameters = params;
+}
+
+QMap<QString, QStringList> Related::parameters() const
+{
+    return d->parameters;
+}
+
+bool Related::operator==(const Related &other) const
+{
+    return (d->parameters == other.parameters()) && (d->relatedTo == other.related());
+}
+
+bool Related::operator!=(const Related &other) const
+{
+    return !(other == *this);
+}
+
+Related &Related::operator=(const Related &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString Related::toString() const
+{
+    QString str;
+    str += QLatin1String("Related {\n");
+    str += QStringLiteral("    relatedTo: %1\n").arg(d->relatedTo);
+    if (!d->parameters.isEmpty()) {
+        QMapIterator<QString, QStringList> i(d->parameters);
+        QString param;
+        while (i.hasNext()) {
+            i.next();
+            param += QStringLiteral("%1 %2").arg(i.key(), i.value().join(QStringLiteral(",")));
+        }
+        str += QStringLiteral("    parameters: %1\n").arg(param);
+    }
+    str += QLatin1String("}\n");
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Related &related)
+{
+    return s << related.d->parameters << related.d->relatedTo;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Related &related)
+{
+    s >> related.d->parameters >> related.d->relatedTo;
+    return s;
+}
diff --git a/src/related.h b/src/related.h
new file mode 100644 (file)
index 0000000..0dd314f
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef RELATED_H
+#define RELATED_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+
+namespace KContacts
+{
+class KCONTACTS_EXPORT Related
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Related &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Related &);
+public:
+    Related();
+    Related(const Related &other);
+    Related(const QString &related);
+
+    ~Related();
+
+    typedef QVector<Related> List;
+
+    void setRelated(const QString &relatedTo);
+    QString related() const;
+
+    bool isValid() const;
+
+    void setParameters(const QMap<QString, QStringList> &params);
+    QMap<QString, QStringList> parameters() const;
+
+    bool operator==(const Related &other) const;
+    bool operator!=(const Related &other) const;
+
+    Related &operator=(const Related &other);
+
+    QString toString() const;
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Related &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Related &object);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Related, Q_MOVABLE_TYPE);
+#endif // LANG_H
diff --git a/src/resourcelocatorurl.cpp b/src/resourcelocatorurl.cpp
new file mode 100644 (file)
index 0000000..076aca6
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "resourcelocatorurl.h"
+#include <QMap>
+#include <QUrl>
+#include <QDataStream>
+#include <qstringlist.h>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN ResourceLocatorUrl::Private : public QSharedData
+{
+public:
+    Private()
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        parameters = other.parameters;
+        url = other.url;
+    }
+    QMap<QString, QStringList> parameters;
+    QUrl url;
+};
+
+ResourceLocatorUrl::ResourceLocatorUrl()
+    : d(new Private)
+{
+}
+
+ResourceLocatorUrl::ResourceLocatorUrl(const ResourceLocatorUrl &other)
+    : d(other.d)
+{
+}
+
+ResourceLocatorUrl::~ResourceLocatorUrl()
+{
+
+}
+
+QMap<QString, QStringList> ResourceLocatorUrl::parameters() const
+{
+    return d->parameters;
+}
+
+bool ResourceLocatorUrl::operator==(const ResourceLocatorUrl &other) const
+{
+    return (d->parameters == other.parameters()) && (d->url == other.url());
+}
+
+bool ResourceLocatorUrl::operator!=(const ResourceLocatorUrl &other) const
+{
+    return !(other == *this);
+}
+
+ResourceLocatorUrl &ResourceLocatorUrl::operator=(const ResourceLocatorUrl &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString ResourceLocatorUrl::toString() const
+{
+    QString str;
+    str += QLatin1String("ResourceLocatorUrl {\n");
+    str += QStringLiteral("    url: %1\n").arg(d->url.toString());
+    if (!d->parameters.isEmpty()) {
+        QMapIterator<QString, QStringList> i(d->parameters);
+        QString param;
+        while (i.hasNext()) {
+            i.next();
+            param += QStringLiteral("%1 %2").arg(i.key(), i.value().join(QStringLiteral(",")));
+        }
+        str += QStringLiteral("    parameters: %1\n").arg(param);
+    }
+    str += QLatin1String("}\n");
+    return str;
+}
+
+void ResourceLocatorUrl::setParameters(const QMap<QString, QStringList> &params)
+{
+    d->parameters = params;
+}
+
+bool ResourceLocatorUrl::isValid() const
+{
+    return (d->url.isValid());
+}
+
+void ResourceLocatorUrl::setUrl(const QUrl &url)
+{
+    d->url = url;
+}
+
+QUrl ResourceLocatorUrl::url() const
+{
+    return d->url;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const ResourceLocatorUrl &calUrl)
+{
+    return s << calUrl.d->parameters << calUrl.d->url;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, ResourceLocatorUrl &calUrl)
+{
+    s >> calUrl.d->parameters >> calUrl.d->url;
+    return s;
+}
+
diff --git a/src/resourcelocatorurl.h b/src/resourcelocatorurl.h
new file mode 100644 (file)
index 0000000..4d7ff13
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef RESOURCELOCATORURL_H
+#define RESOURCELOCATORURL_H
+
+#include "kcontacts_export.h"
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QMap>
+class QUrl;
+/** @short Class that holds a Resource Locator
+ *  @since 5.0
+ */
+
+namespace KContacts
+{
+
+class KCONTACTS_EXPORT ResourceLocatorUrl
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const ResourceLocatorUrl &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, ResourceLocatorUrl &);
+public:
+
+    ResourceLocatorUrl();
+    ResourceLocatorUrl(const ResourceLocatorUrl &other);
+
+    ~ResourceLocatorUrl();
+
+    typedef QVector<ResourceLocatorUrl> List;
+
+    bool isValid() const;
+
+    void setUrl(const QUrl &url);
+    QUrl url() const;
+
+    void setParameters(const QMap<QString, QStringList> &params);
+    QMap<QString, QStringList> parameters() const;
+
+    bool operator==(const ResourceLocatorUrl &other) const;
+    bool operator!=(const ResourceLocatorUrl &other) const;
+
+    ResourceLocatorUrl &operator=(const ResourceLocatorUrl &other);
+
+    QString toString() const;
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const ResourceLocatorUrl &object);
+
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, ResourceLocatorUrl &object);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::ResourceLocatorUrl, Q_MOVABLE_TYPE);
+
+#endif // RESOURCELOCATORURL_H
diff --git a/src/secrecy.cpp b/src/secrecy.cpp
new file mode 100644 (file)
index 0000000..5f22e40
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "secrecy.h"
+
+#include <klocalizedstring.h>
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Secrecy::PrivateData : public QSharedData
+{
+public:
+    PrivateData()
+        : mType(Secrecy::Invalid)
+    {
+    }
+
+    PrivateData(const PrivateData &other)
+        : QSharedData(other)
+    {
+        mType = other.mType;
+    }
+
+    Type mType;
+};
+
+Secrecy::Secrecy(Type type)
+    : d(new PrivateData)
+{
+    d->mType = type;
+}
+
+Secrecy::Secrecy(const Secrecy &other)
+    : d(other.d)
+{
+}
+
+Secrecy::~Secrecy()
+{
+}
+
+Secrecy &Secrecy::operator=(const Secrecy &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool Secrecy::operator==(const Secrecy &other) const
+{
+    return d->mType == other.d->mType;
+}
+
+bool Secrecy::operator!=(const Secrecy &other) const
+{
+    return !(*this == other);
+}
+
+bool Secrecy::isValid() const
+{
+    return d->mType != Invalid;
+}
+
+void Secrecy::setType(Type type)
+{
+    d->mType = type;
+}
+
+Secrecy::Type Secrecy::type() const
+{
+    return d->mType;
+}
+
+Secrecy::TypeList Secrecy::typeList()
+{
+    static TypeList list;
+
+    if (list.isEmpty()) {
+        list << Public << Private << Confidential;
+    }
+
+    return list;
+}
+
+QString Secrecy::typeLabel(Type type)
+{
+    switch (type) {
+    case Public:
+        return i18nc("access is for everyone", "Public");
+        break;
+    case Private:
+        return i18nc("access is by owner only", "Private");
+        break;
+    case Confidential:
+        return i18nc("access is by owner and a controlled group", "Confidential");
+        break;
+    default:
+        return i18nc("unknown secrecy type", "Unknown type");
+        break;
+    }
+}
+
+QString Secrecy::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Secrecy {\n");
+    str += QStringLiteral("  Type: %1\n").arg(typeLabel(d->mType));
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Secrecy &secrecy)
+{
+    return s << (uint)secrecy.d->mType;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Secrecy &secrecy)
+{
+    uint type;
+    s >> type;
+
+    switch (type) {
+    case 0:
+        secrecy.d->mType = Secrecy::Public;
+        break;
+    case 1:
+        secrecy.d->mType = Secrecy::Private;
+        break;
+    case 2:
+        secrecy.d->mType = Secrecy::Confidential;
+        break;
+    default:
+        secrecy.d->mType = Secrecy::Invalid;
+        break;
+    }
+
+    return s;
+}
diff --git a/src/secrecy.h b/src/secrecy.h
new file mode 100644 (file)
index 0000000..33f6fc8
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_SECRECY_H
+#define KCONTACTS_SECRECY_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QVector>
+#include <QtCore/QSharedDataPointer>
+
+namespace KContacts
+{
+
+class KCONTACTS_EXPORT Secrecy
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Secrecy &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Secrecy &);
+
+public:
+    /**
+     * Secrecy types
+     *
+     * @li Public       - for public access
+     * @li Private      - only private access
+     * @li Confidential - access for confidential persons
+     */
+    enum Type {
+        Public,
+        Private,
+        Confidential,
+        Invalid
+    };
+
+    /**
+     * List of secrecy types.
+     */
+    typedef QVector<Type> TypeList;
+
+    /**
+     * Creates a new secrecy of the given type.
+     *
+     * @param type  The secrecy type. @see Type
+     */
+    Secrecy(Type type = Invalid);
+
+    /**
+     * Copy constructor.
+     */
+    Secrecy(const Secrecy &other);
+
+    /**
+     * Destroys the secrecy.
+     */
+    ~Secrecy();
+
+    Secrecy &operator=(const Secrecy &other);
+
+    bool operator==(const Secrecy &other) const;
+    bool operator!=(const Secrecy &other) const;
+
+    /**
+     * Returns if the Secrecy object has a valid value.
+     */
+    bool isValid() const;
+
+    /**
+     * Sets the @p type.
+     *
+     * @param type The #Type of secrecy
+     */
+    void setType(Type type);
+
+    /**
+     * Returns the type.
+     */
+    Type type() const;
+
+    /**
+     * Returns a list of all available secrecy types.
+     */
+    static TypeList typeList();
+
+    /**
+     * Returns a translated label for a given secrecy @p type.
+     */
+    static QString typeLabel(Type type);
+
+    /**
+     * Returns a string representation of the secrecy.
+     */
+    QString toString() const;
+
+private:
+    class PrivateData;
+    QSharedDataPointer<PrivateData> d;
+};
+
+/**
+ * Serializes the @p secrecy object into the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Secrecy &secrecy);
+
+/**
+ * Initializes the @p secrecy object from the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Secrecy &secrecy);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Secrecy, Q_MOVABLE_TYPE);
+#endif
diff --git a/src/sortmode.cpp b/src/sortmode.cpp
new file mode 100644 (file)
index 0000000..7775eb7
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "sortmode.h"
+#include "field.h"
+
+using namespace KContacts;
+
+SortMode::~SortMode()
+{
+}
+
+class Q_DECL_HIDDEN NameSortMode::Private
+{
+public:
+    NameType mNameType;
+    bool mAscendingOrder;
+};
+
+NameSortMode::NameSortMode()
+    : d(new Private)
+{
+    d->mNameType = FormattedName;
+    d->mAscendingOrder = true;
+}
+
+NameSortMode::NameSortMode(NameType type, bool ascending)
+    : d(new Private)
+{
+    d->mNameType = type;
+    d->mAscendingOrder = ascending;
+}
+
+NameSortMode::~NameSortMode()
+{
+    delete d;
+}
+
+bool NameSortMode::lesser(const KContacts::Addressee &first, const KContacts::Addressee &second) const
+{
+    bool lesser = false;
+
+    switch (d->mNameType) {
+    case FormattedName:
+        lesser = QString::localeAwareCompare(first.formattedName(), second.formattedName()) < 0;
+        break;
+    case FamilyName:
+        lesser = QString::localeAwareCompare(first.familyName(), second.familyName()) < 0;
+        break;
+    case GivenName:
+        lesser = QString::localeAwareCompare(first.givenName(), second.givenName()) < 0;
+        break;
+    default:
+        lesser = false;
+        break;
+    }
+
+    if (!d->mAscendingOrder) {
+        lesser = !lesser;
+    }
+
+    return lesser;
+}
+
+class Q_DECL_HIDDEN FieldSortMode::Private
+{
+public:
+    KContacts::Field *mField;
+    bool mAscendingOrder;
+};
+
+FieldSortMode::FieldSortMode(KContacts::Field *field, bool ascending)
+    : d(new Private)
+{
+    d->mField = field;
+    d->mAscendingOrder = ascending;
+}
+
+FieldSortMode::~FieldSortMode()
+{
+    delete d;
+}
+
+bool FieldSortMode::lesser(const KContacts::Addressee &first, const KContacts::Addressee &second) const
+{
+    if (!d->mField) {
+        return false;
+    } else {
+        bool lesser =
+            QString::localeAwareCompare(d->mField->value(first), d->mField->value(second)) < 0;
+        if (!d->mAscendingOrder) {
+            lesser = !lesser;
+        }
+
+        return lesser;
+    }
+}
diff --git a/src/sortmode.h b/src/sortmode.h
new file mode 100644 (file)
index 0000000..e81bc66
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_SORTMODE_H
+#define KCONTACTS_SORTMODE_H
+
+#include "kcontacts_export.h"
+#include "addressee.h"
+
+namespace KContacts
+{
+
+class Field;
+
+/**
+  @short Sort method for sorting an addressee list.
+
+  This interface should be reimplemented by classes which shall act as
+  SortModes for KContacts::AddresseeList.
+*/
+class KCONTACTS_EXPORT SortMode
+{
+public:
+    virtual ~SortMode();
+
+    /**
+      Reimplement this method and return whether the first contact is 'smaller'
+      than the second.
+     */
+    virtual bool lesser(const KContacts::Addressee &first, const KContacts::Addressee &second) const = 0;
+};
+
+/**
+  @short Implements comparison by name.
+
+  This class implements a comparison of two Addressee objects by name.
+ */
+class KCONTACTS_EXPORT NameSortMode : public SortMode
+{
+public:
+    /**
+      Specifies which parts of the name are used for comparison.
+     */
+    enum NameType {
+        FormattedName,    /**< use the formatted name, e.g. "John Doe" */
+        FamilyName,       /**< use the last name, e.g. "Doe" */
+        GivenName         /**< use the first name, e.g. "John" */
+    };
+
+    /**
+      Constructor.
+
+      Creates a NameSortMethod with FormattedName as name type set.
+     */
+    NameSortMode();
+
+    /**
+      Constructor.
+
+      Creates a NameSortMethod with the specified name type.
+
+      @param type The name type.
+      @param ascending if @c true, Addressee object are sorted in
+             ascending order; if @c false, objects are sorted in
+             descending order
+     */
+    explicit NameSortMode(NameType type, bool ascending = true);
+
+    virtual ~NameSortMode();
+
+    /**
+      Returns whether the first contact is 'smaller' then the second.
+     */
+    bool lesser(const KContacts::Addressee &first, const KContacts::Addressee &second) const Q_DECL_OVERRIDE;
+
+private:
+    class Private;
+    Private *const d;
+
+    Q_DISABLE_COPY(NameSortMode)
+};
+
+/**
+  @short Implements comparison by an arbitrary field.
+
+  This class implements a comparison of two Addressee objects by the
+  value of an arbitrary field.
+ */
+class KCONTACTS_EXPORT FieldSortMode : public SortMode
+{
+public:
+    /**
+      Constructor.
+
+      Creates a FieldSortMethod with the specified @p field.
+
+      @param field The field.
+      @param ascending if @c true, Addressee object are sorted in
+             ascending order; if @c false, objects are sorted in
+             descending order
+     */
+    explicit FieldSortMode(KContacts::Field *field, bool ascending = true);
+
+    virtual ~FieldSortMode();
+
+    /**
+      Returns whether the first contact is 'smaller' then the second.
+     */
+    bool lesser(const KContacts::Addressee &first, const KContacts::Addressee &second) const Q_DECL_OVERRIDE;
+
+private:
+    class Private;
+    Private *const d;
+
+    Q_DISABLE_COPY(FieldSortMode)
+};
+
+}
+
+#endif
diff --git a/src/sound.cpp b/src/sound.cpp
new file mode 100644 (file)
index 0000000..6fd53d5
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "sound.h"
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN Sound::Private : public QSharedData
+{
+public:
+    Private()
+        : mIntern(false)
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mUrl = other.mUrl;
+        mData = other.mData;
+        mIntern = other.mIntern;
+    }
+
+    QString mUrl;
+    QByteArray mData;
+
+    bool mIntern;
+};
+
+Sound::Sound()
+    : d(new Private)
+{
+}
+
+Sound::Sound(const QString &url)
+    : d(new Private)
+{
+    d->mUrl = url;
+}
+
+Sound::Sound(const QByteArray &data)
+    : d(new Private)
+{
+    d->mIntern = true;
+    d->mData = data;
+}
+
+Sound::Sound(const Sound &other)
+    : d(other.d)
+{
+}
+
+Sound::~Sound()
+{
+}
+
+Sound &Sound::operator=(const Sound &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+bool Sound::operator==(const Sound &other) const
+{
+    if (d->mIntern != other.d->mIntern) {
+        return false;
+    }
+
+    if (d->mIntern) {
+        if (d->mData != other.d->mData) {
+            return false;
+        }
+    } else {
+        if (d->mUrl != other.d->mUrl) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool Sound::operator!=(const Sound &other) const
+{
+    return !(other == *this);
+}
+
+void Sound::setUrl(const QString &url)
+{
+    d->mIntern = false;
+    d->mUrl = url;
+}
+
+void Sound::setData(const QByteArray &data)
+{
+    d->mIntern = true;
+    d->mData = data;
+}
+
+bool Sound::isIntern() const
+{
+    return d->mIntern;
+}
+
+bool Sound::isEmpty() const
+{
+    return
+        ((d->mIntern && d->mData.isEmpty()) || (!d->mIntern && d->mUrl.isEmpty()));
+}
+
+QString Sound::url() const
+{
+    return d->mUrl;
+}
+
+QByteArray Sound::data() const
+{
+    return d->mData;
+}
+
+QString Sound::toString() const
+{
+    QString str;
+
+    str += QLatin1String("Sound {\n");
+    str += QStringLiteral("  IsIntern: %1\n").
+           arg(d->mIntern ? QStringLiteral("true") : QStringLiteral("false"));
+    if (d->mIntern) {
+        str += QStringLiteral("  Data: %1\n").
+               arg(QString::fromLatin1(d->mData.toBase64()));
+    } else {
+        str += QStringLiteral("  Url: %1\n").arg(d->mUrl);
+    }
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const Sound &sound)
+{
+    return s << sound.d->mIntern << sound.d->mUrl << sound.d->mData;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, Sound &sound)
+{
+    s >> sound.d->mIntern >> sound.d->mUrl >> sound.d->mData;
+
+    return s;
+}
diff --git a/src/sound.h b/src/sound.h
new file mode 100644 (file)
index 0000000..f9fb1f3
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_SOUND_H
+#define KCONTACTS_SOUND_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QString>
+#include <QtCore/QSharedDataPointer>
+
+namespace KContacts
+{
+
+/** @short Class that holds a Sound clip for a contact.
+ *
+ *  The sound can be played doing something like this:
+ *
+ *  \code
+ *    KTempFile tmp;
+ *    if ( sound.isIntern() ) {
+ *      tmp.file()->write( sound.data() );
+ *      tmp.close();
+ *      KAudioPlayer::play( tmp.name() );
+ *    } else if( !sound.url().isEmpty() ) {
+ *      QString tmpFile;
+ *      if ( !KIO::NetAccess::download( QUrl( themeURL.url() ), tmpFile, 0 ) ) {
+ *        KMessageBox::error( 0,
+ *                            KIO::NetAccess::lastErrorString(),
+ *                            i18n( "Failed to download sound file" ),
+ *                            KMessageBox::Notify
+ *                          );
+ *        return;
+ *      }
+ *      KAudioPlayer::play( tmpFile );
+ *    }
+ *  \endcode
+ *
+ *  Unfortunately, KAudioPlayer::play is ASync, so to delete the temporary file
+ *  the best you can really do is set a timer.
+ *
+ */
+class KCONTACTS_EXPORT Sound
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const Sound &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, Sound &);
+
+public:
+
+    /**
+     * Creates an empty sound object.
+     */
+    Sound();
+
+    /**
+     * Creates a sound object for the given url.
+     *
+     * @param url A url that describes the position of the sound file.
+     */
+    Sound(const QString &url);
+
+    /**
+     * Creates a sound object for the given data.
+     *
+     * @param data The raw data of the sound.
+     */
+    Sound(const QByteArray &data);
+
+    /**
+     * Copy constructor.
+     */
+    Sound(const Sound &other);
+
+    /**
+     * Destroys the sound object.
+     */
+    ~Sound();
+
+    typedef QVector<Sound> List;
+    /**
+     * Assignment operator.
+     *
+     * @param other The sound object to assign to @c this
+     */
+    Sound &operator=(const Sound &other);
+
+    /**
+     * Equality operator.
+     *
+     * @param other The object to compare with
+     *
+     * @return @c true if the two objects are equal, otherwise @c false
+     */
+    bool operator==(const Sound &other) const;
+
+    /**
+     * Not-Equal operator.
+     *
+     * @param other The object to compare with
+     *
+     * @return @c true if the two objects are not equal, otherwise @c false
+     */
+    bool operator!=(const Sound &other) const;
+
+    /**
+     * Sets a URL for the location of the sound file. When using this
+     * function, isIntern() will return 'false' until you use
+     * setData().
+     *
+     * @param url  The location URL of the sound file.
+     */
+    void setUrl(const QString &url);
+
+    /**
+     * Returns true, if the sound object is empty.
+     */
+    bool isEmpty() const;
+
+    /**
+     * Sets the raw data of the sound. When using this function,
+     * isIntern() will return 'true' until you use setUrl().
+     *
+     * @param data  The raw data of the sound.
+     */
+    void setData(const QByteArray &data);
+
+    /**
+     * Returns whether the sound is described by a URL (extern) or
+     * by the raw data (intern).
+     * When this method returns 'true' you can use data() to
+     * get the raw data. Otherwise you can request the URL of this
+     * sound by url() and load the raw data from that location.
+     */
+    bool isIntern() const;
+
+    /**
+     * Returns the location URL of this sound.
+     */
+    QString url() const;
+
+    /**
+     * Returns the raw data of this sound.
+     */
+    QByteArray data() const;
+
+    /**
+     * Returns string representation of the sound.
+     */
+    QString toString() const;
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+/**
+ * Serializes the @p sound object into the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Sound &sound);
+
+/**
+ * Initializes the @p sound object from the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Sound &sound);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::Sound, Q_MOVABLE_TYPE);
+#endif
diff --git a/src/timezone.cpp b/src/timezone.cpp
new file mode 100644 (file)
index 0000000..b53a2c9
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "timezone.h"
+
+#include <QtCore/QDataStream>
+#include <QtCore/QSharedData>
+
+using namespace KContacts;
+
+class Q_DECL_HIDDEN TimeZone::Private : public QSharedData
+{
+public:
+    Private(int offset = 0, bool valid = false)
+        : mOffset(offset), mValid(valid)
+    {
+    }
+
+    Private(const Private &other)
+        : QSharedData(other)
+    {
+        mOffset = other.mOffset;
+        mValid = other.mValid;
+    }
+
+    int mOffset;
+    bool mValid;
+};
+
+TimeZone::TimeZone()
+    : d(new Private)
+{
+}
+
+TimeZone::TimeZone(int offset)
+    : d(new Private(offset, true))
+{
+}
+
+TimeZone::TimeZone(const TimeZone &other)
+    : d(other.d)
+{
+}
+
+TimeZone::~TimeZone()
+{
+}
+
+void TimeZone::setOffset(int offset)
+{
+    d->mOffset = offset;
+    d->mValid = true;
+}
+
+int TimeZone::offset() const
+{
+    return d->mOffset;
+}
+
+bool TimeZone::isValid() const
+{
+    return d->mValid;
+}
+
+bool TimeZone::operator==(const TimeZone &t) const
+{
+    if (!t.isValid() && !isValid()) {
+        return true;
+    }
+
+    if (!t.isValid() || !isValid()) {
+        return false;
+    }
+
+    if (t.d->mOffset == d->mOffset) {
+        return true;
+    }
+
+    return false;
+}
+
+bool TimeZone::operator!=(const TimeZone &t) const
+{
+    return !(*this == t);
+}
+
+TimeZone &TimeZone::operator=(const TimeZone &other)
+{
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
+}
+
+QString TimeZone::toString() const
+{
+    QString str;
+
+    str += QLatin1String("TimeZone {\n");
+    str += QStringLiteral("  Offset: %1\n").arg(d->mOffset);
+    str += QLatin1String("}\n");
+
+    return str;
+}
+
+QDataStream &KContacts::operator<<(QDataStream &s, const TimeZone &zone)
+{
+    return s << zone.d->mOffset << zone.d->mValid;
+}
+
+QDataStream &KContacts::operator>>(QDataStream &s, TimeZone &zone)
+{
+    s >> zone.d->mOffset >> zone.d->mValid;
+
+    return s;
+}
diff --git a/src/timezone.h b/src/timezone.h
new file mode 100644 (file)
index 0000000..f562649
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_TIMEZONE_H
+#define KCONTACTS_TIMEZONE_H
+
+#include "kcontacts_export.h"
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+
+namespace KContacts
+{
+
+/**
+ * @short Time zone information.
+ *
+ * This class stores information about a time zone.
+ */
+class KCONTACTS_EXPORT TimeZone
+{
+    friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &, const TimeZone &);
+    friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &, TimeZone &);
+
+public:
+    /**
+     * Construct invalid time zone.
+     */
+    TimeZone();
+
+    /**
+     * Construct time zone.
+     *
+     * @param offset Offset in minutes relative to UTC.
+     */
+    TimeZone(int offset);
+
+    /**
+     * Copy constructor.
+     */
+    TimeZone(const TimeZone &other);
+
+    /**
+     * Destroys the time zone.
+     */
+    ~TimeZone();
+
+    /**
+     * Set time zone offset relative to UTC.
+     *
+     * @param offset Offset in minutes.
+     */
+    void setOffset(int offset);
+
+    /**
+     * Return offset in minutes relative to UTC.
+     */
+    int offset() const;
+
+    /**
+     * Return, if this time zone object is valid.
+     */
+    bool isValid() const;
+
+    bool operator==(const TimeZone &other) const;
+    bool operator!=(const TimeZone &other) const;
+    TimeZone &operator=(const TimeZone &other);
+
+    /**
+     * Return string representation of time zone offset.
+     */
+    QString toString() const;
+
+private:
+    class Private;
+    QSharedDataPointer<Private> d;
+};
+
+/**
+ * Serializes the @p timezone object into the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const TimeZone &timeZone);
+
+/**
+ * Initializes the @p timezone object from the @p stream.
+ */
+KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, TimeZone &timeZone);
+
+}
+Q_DECLARE_TYPEINFO(KContacts::TimeZone, Q_MOVABLE_TYPE);
+
+#endif
diff --git a/src/vcarddrag.cpp b/src/vcarddrag.cpp
new file mode 100644 (file)
index 0000000..7d74634
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  This file is part of the KContacts framework.
+
+  Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "vcarddrag.h"
+
+#include "converter/vcardconverter.h"
+
+#include <QMimeData>
+#include <QMimeDatabase>
+
+using namespace KContacts;
+
+static QString findCompatibleMimeType(const QMimeData *md)
+{
+    // check the canonical MIME type first
+    if (md->hasFormat(KContacts::Addressee::mimeType())) {
+        return KContacts::Addressee::mimeType();
+    }
+
+    const QStringList mimeTypeOffers = md->formats();
+    const QMimeDatabase db;
+    Q_FOREACH (const QString &mimeType, mimeTypeOffers) {
+        const QMimeType mimeTypePtr = db.mimeTypeForName(mimeType);
+        if (mimeTypePtr.isValid() && mimeTypePtr.inherits(KContacts::Addressee::mimeType())) {
+            return mimeType;
+        }
+    }
+
+    return QString();
+}
+
+bool VCardDrag::populateMimeData(QMimeData *md, const QByteArray &content)
+{
+    md->setData(KContacts::Addressee::mimeType(), content);
+    return true;
+}
+
+bool VCardDrag::populateMimeData(QMimeData *md, const KContacts::Addressee::List &addressees)
+{
+    KContacts::VCardConverter converter;
+    const QByteArray vcards = converter.createVCards(addressees);
+    if (!vcards.isEmpty()) {
+        return populateMimeData(md, vcards);
+    } else {
+        return false;
+    }
+}
+
+bool VCardDrag::canDecode(const QMimeData *md)
+{
+    return !findCompatibleMimeType(md).isEmpty();
+}
+
+bool VCardDrag::fromMimeData(const QMimeData *md, QByteArray &content)
+{
+    const QString mimeOffer = findCompatibleMimeType(md);
+    if (mimeOffer.isEmpty()) {
+        return false;
+    }
+    content = md->data(mimeOffer);
+    return !content.isEmpty();
+}
+
+bool VCardDrag::fromMimeData(const QMimeData *md, KContacts::Addressee::List &addressees)
+{
+    const QString mimeOffer = findCompatibleMimeType(md);
+    if (mimeOffer.isEmpty()) {
+        return false;
+    }
+    addressees = KContacts::VCardConverter().parseVCards(md->data(mimeOffer));
+    return !addressees.isEmpty();
+}
+
diff --git a/src/vcarddrag.h b/src/vcarddrag.h
new file mode 100644 (file)
index 0000000..7d95e66
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+  This file is part of the KContacts framework.
+
+  Copyright (c) 2002-2010 Tobias Koenig <tokoe@kde.org>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_VCARDDRAG_H
+#define KCONTACTS_VCARDDRAG_H
+
+#include "kcontacts_export.h"
+
+#include <kcontacts/addressee.h>
+#include <QMimeData>
+
+namespace KContacts
+{
+
+/**
+  @brief Utility function for implementing drag&drop for vCards
+
+  See the Qt drag'n'drop documentation.
+
+  @since 4.5
+*/
+namespace VCardDrag
+{
+
+/**
+  Adds the vCard representation as data of the drag object.
+
+  @param md the object to set the data on
+  @param content the vCard data to set
+
+  @return Always @c true
+*/
+KCONTACTS_EXPORT bool populateMimeData(QMimeData *md, const QByteArray &content);
+
+/**
+  Adds the vCard representation as data of the drag object.
+
+  @param md the object to set the data on
+  @param contacts list of Addressee objects to serialize to vCard
+
+  @return @c true if serializing of the given list worked, otherwise @c false
+
+  @see VCardConverter::createVCards()
+*/
+KCONTACTS_EXPORT bool populateMimeData(QMimeData *md, const KContacts::Addressee::List &contacts);
+
+/**
+  Returns if drag&drop object can be decoded to vCard.
+
+  @param md the object to check for vCard data
+
+  @return @c true if the given data object contains a vCard MIME type, otherwise @c false
+*/
+KCONTACTS_EXPORT bool canDecode(const QMimeData *md);
+
+/**
+  Decodes the drag&drop object to vCard component @p content.
+
+  @param md the object to check for vCard data
+  @param content where to put the vCard data from @p md
+
+  @return @c true if there was data for the vCard MIME type, otherwise @c false
+
+  @see canDecode()
+*/
+KCONTACTS_EXPORT bool fromMimeData(const QMimeData *md, QByteArray &content);
+
+/**
+  Decodes the MIME data @p md and puts the resulting vCard into @p contactss.
+
+  @param md the object to check for vCard data
+  @param contacts where to put the parsed vCards from @p md
+
+  @return @c true if there was data for the vCard MIME type and it could be parsed successfully,
+          otherwise @c false
+
+  @see canDecode()
+*/
+KCONTACTS_EXPORT bool fromMimeData(const QMimeData *md, KContacts::Addressee::List &contacts);
+}
+
+}
+
+#endif // VCARDDRAG_H
diff --git a/src/vcardparser/CMakeLists.txt b/src/vcardparser/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5ada0ee
--- /dev/null
@@ -0,0 +1,10 @@
+
+
+########### install files ###############
+install(
+  FILES
+  vcard.h
+  vcardline.h
+  vcardparser.h
+  DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/kcontacts COMPONENT Devel
+)
diff --git a/src/vcardparser/vcard.cpp b/src/vcardparser/vcard.cpp
new file mode 100644 (file)
index 0000000..59717b3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "vcard.h"
+#include <QVector>
+
+using namespace KContacts;
+
+VCard::VCard()
+{
+}
+
+VCard::VCard(const VCard &vcard)
+{
+    mLineMap = vcard.mLineMap;
+}
+
+VCard::~VCard()
+{
+}
+
+VCard &VCard::operator=(const VCard &vcard)
+{
+    if (&vcard == this) {
+        return *this;
+    }
+
+    mLineMap = vcard.mLineMap;
+
+    return *this;
+}
+
+void VCard::clear()
+{
+    mLineMap.clear();
+}
+
+QStringList VCard::identifiers() const
+{
+    return mLineMap.keys();
+}
+
+void VCard::addLine(const VCardLine &line)
+{
+    mLineMap[ line.identifier() ].append(line);
+}
+
+VCardLine::List VCard::lines(const QString &identifier) const
+{
+    LineMap::ConstIterator it = mLineMap.find(identifier);
+    if (it == mLineMap.end()) {
+        return VCardLine::List();
+    }
+
+    return *it;
+}
+
+VCardLine VCard::line(const QString &identifier) const
+{
+    LineMap::ConstIterator it = mLineMap.find(identifier);
+    if (it == mLineMap.end()) {
+        return VCardLine();
+    }
+
+    if ((*it).isEmpty()) {
+        return VCardLine();
+    } else {
+        return (*it).first();
+    }
+}
+
+void VCard::setVersion(Version version)
+{
+    mLineMap.remove(QStringLiteral("VERSION"));
+
+    VCardLine line;
+    line.setIdentifier(QStringLiteral("VERSION"));
+    if (version == v2_1) {
+        line.setIdentifier(QStringLiteral("2.1"));
+    } else if (version == v3_0) {
+        line.setIdentifier(QStringLiteral("3.0"));
+    } else if (version == v4_0) {
+        line.setIdentifier(QStringLiteral("4.0"));
+    }
+
+    mLineMap[ QStringLiteral("VERSION") ].append(line);
+}
+
+VCard::Version VCard::version() const
+{
+    LineMap::ConstIterator versionEntry = mLineMap.find(QStringLiteral("VERSION"));
+    if (versionEntry == mLineMap.end()) {
+        return v3_0;
+    }
+
+    VCardLine line = (*versionEntry)[ 0 ];
+    if (line.value() == QLatin1String("2.1")) {
+        return v2_1;
+    } else if (line.value() == QLatin1String("3.0")) {
+        return v3_0;
+    } else {
+        return v4_0;
+    }
+}
diff --git a/src/vcardparser/vcard.h b/src/vcardparser/vcard.h
new file mode 100644 (file)
index 0000000..9ca5bd7
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef VCARDPARSER_VCARD_H
+#define VCARDPARSER_VCARD_H
+
+#include <QtCore/QMap>
+#include <QtCore/QStringList>
+
+#include "vcardline.h"
+
+namespace KContacts
+{
+
+class VCard
+{
+public:
+    typedef QVector<VCard> List;
+    typedef QMap<QString, VCardLine::List> LineMap;
+
+    enum Version {
+        v2_1,
+        v3_0,
+        v4_0
+    };
+
+    VCard();
+    VCard(const VCard &card);
+
+    ~VCard();
+
+    VCard &operator=(const VCard &card);
+
+    /**
+     * Removes all lines from the vCard.
+     */
+    void clear();
+
+    /**
+     * Returns a list of all identifiers that exists in the vCard.
+     */
+    QStringList identifiers() const;
+
+    /**
+     * Adds a VCardLine to the VCard
+     */
+    void addLine(const VCardLine &line);
+
+    /**
+     * Returns all lines of the vcard with a special identifier.
+     */
+    VCardLine::List lines(const QString &identifier) const;
+
+    /**
+     * Returns only the first line of the vcard with a special identifier.
+     */
+    VCardLine line(const QString &identifier) const;
+
+    /**
+     * Set the version of the vCard.
+     */
+    void setVersion(Version version);
+
+    /**
+     * Returns the version of this vCard.
+     */
+    Version version() const;
+
+private:
+    LineMap mLineMap;
+};
+
+}
+
+#endif
diff --git a/src/vcardparser/vcardline.cpp b/src/vcardparser/vcardline.cpp
new file mode 100644 (file)
index 0000000..d61a6cb
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "vcardline.h"
+
+using namespace KContacts;
+
+VCardLine::VCardLine()
+{
+}
+
+VCardLine::VCardLine(const QString &identifier)
+{
+    mIdentifier = identifier;
+}
+
+VCardLine::VCardLine(const QString &identifier, const QVariant &value)
+{
+    mIdentifier = identifier;
+    mValue = value;
+}
+
+VCardLine::VCardLine(const VCardLine &line)
+{
+    mParamMap = line.mParamMap;
+    mValue = line.mValue;
+    mIdentifier = line.mIdentifier;
+}
+
+VCardLine::~VCardLine()
+{
+}
+
+VCardLine &VCardLine::operator=(const VCardLine &line)
+{
+    if (&line == this) {
+        return *this;
+    }
+
+    mParamMap = line.mParamMap;
+    mValue = line.mValue;
+    mIdentifier = line.mIdentifier;
+
+    return *this;
+}
+
+void VCardLine::setIdentifier(const QString &identifier)
+{
+    mIdentifier = identifier;
+}
+
+QString VCardLine::identifier() const
+{
+    return mIdentifier;
+}
+
+void VCardLine::setValue(const QVariant &value)
+{
+    mValue = value;
+}
+
+QVariant VCardLine::value() const
+{
+    return mValue;
+}
+
+void VCardLine::setGroup(const QString &group)
+{
+    mGroup = group;
+}
+
+QString VCardLine::group() const
+{
+    return mGroup;
+}
+
+bool VCardLine::hasGroup() const
+{
+    return !mGroup.isEmpty();
+}
+
+QStringList VCardLine::parameterList() const
+{
+    return mParamMap.keys();
+}
+
+void VCardLine::addParameter(const QString &param, const QString &value)
+{
+    QStringList &list = mParamMap[ param ];
+    if (!list.contains(value)) {     // not included yet
+        list.append(value);
+    }
+}
+
+QStringList VCardLine::parameters(const QString &param) const
+{
+    ParamMap::ConstIterator it = mParamMap.find(param);
+    if (it == mParamMap.end()) {
+        return QStringList();
+    } else {
+        return *it;
+    }
+}
+
+QString VCardLine::parameter(const QString &param) const
+{
+    ParamMap::ConstIterator it = mParamMap.find(param);
+    if (it == mParamMap.end()) {
+        return QString();
+    } else {
+        if ((*it).isEmpty()) {
+            return QString();
+        } else {
+            return (*it).first();
+        }
+    }
+}
+
+VCardLine::ParamMap VCardLine::parameterMap() const
+{
+    return mParamMap;
+}
diff --git a/src/vcardparser/vcardline.h b/src/vcardparser/vcardline.h
new file mode 100644 (file)
index 0000000..a50287d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef VCARDLINE_H
+#define VCARDLINE_H
+
+#include <QtCore/QMap>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+
+namespace KContacts
+{
+
+class VCardLine
+{
+public:
+    typedef QVector<VCardLine> List;
+    typedef QMap<QString, QStringList> ParamMap;
+
+    VCardLine();
+    VCardLine(const QString &identifier);
+    VCardLine(const QString &identifier, const QVariant &value);
+    VCardLine(const VCardLine &line);
+
+    ~VCardLine();
+
+    VCardLine &operator=(const VCardLine &line);
+
+    /**
+     * Sets the identifier of this line e.g. UID, FN, CLASS
+     *
+     * @param identifier The VCard identifier of this line
+     */
+    void setIdentifier(const QString &identifier);
+
+    /**
+     * Returns the identifier of this line.
+     */
+    QString identifier() const;
+
+    /**
+     * Sets the value of of this line.
+     */
+    void setValue(const QVariant &value);
+
+    /**
+     * Returns the value of this line.
+     */
+    QVariant value() const;
+
+    /**
+     * Sets the group the line belongs to.
+     */
+    void setGroup(const QString &group);
+
+    /**
+     * Returns the group the line belongs to.
+     */
+    QString group() const;
+
+    /**
+     * Returns whether the line belongs to a group.
+     */
+    bool hasGroup() const;
+
+    /**
+     * Returns all parameters.
+     */
+    QStringList parameterList() const;
+
+    /**
+     * Add a new parameter to the line.
+     *
+     * @param param Name of the parameter to insert
+     * @param value Value of the parameter to insert
+     */
+    void addParameter(const QString &param, const QString &value);
+
+    /**
+     * Returns the values of a special parameter.
+     * You can get a list of all parameters with paramList().
+     *
+     * @param param Name of the parameter to look for
+     */
+    QStringList parameters(const QString &param) const;
+
+    /**
+     * Returns only the first value of a special parameter.
+     * You can get a list of all parameters with paramList().
+     *
+     * @param param Name of the parameter to look for
+     */
+    QString parameter(const QString &param) const;
+
+    /**
+     * Returns all parameters
+     * @since 4.14.5
+    */
+    ParamMap parameterMap() const;
+
+private:
+    ParamMap mParamMap;
+    QString mIdentifier;
+    QString mGroup;
+    QVariant mValue;
+};
+
+}
+
+#endif
diff --git a/src/vcardparser/vcardparser.cpp b/src/vcardparser/vcardparser.cpp
new file mode 100644 (file)
index 0000000..894d713
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "vcardparser.h"
+#include <kcodecs.h>
+#include "kcontacts_debug.h"
+#include <QtCore/QTextCodec>
+
+class StringCache
+{
+public:
+    QString fromLatin1(const QByteArray &value)
+    {
+        if (value.isEmpty()) {
+            return QString();
+        }
+
+        QHash<QByteArray, QString>::const_iterator it = m_values.constFind(value);
+        if (it != m_values.constEnd()) {
+            return it.value();
+        }
+
+        QString string = QString::fromLatin1(value);
+        m_values.insert(value, string);
+        return string;
+    }
+
+private:
+    QHash<QByteArray, QString> m_values;
+};
+
+#define FOLD_WIDTH 75
+
+using namespace KContacts;
+
+static void addEscapes(QByteArray &str, bool excludeEscapteComma)
+{
+    str.replace('\\', (char *)"\\\\");
+    if (!excludeEscapteComma) {
+        str.replace(',', (char *)"\\,");
+    }
+    str.replace('\r', (char *)"\\r");
+    str.replace('\n', (char *)"\\n");
+}
+
+static void removeEscapes(QByteArray &str)
+{
+    str.replace((char *)"\\n", "\n");
+    str.replace((char *)"\\N", "\n");
+    str.replace((char *)"\\r", "\r");
+    str.replace((char *)"\\,", ",");
+    str.replace((char *)"\\\\", "\\");
+}
+
+VCardParser::VCardParser()
+    : d(Q_NULLPTR)
+{
+}
+
+VCardParser::~VCardParser()
+{
+}
+
+VCard::List VCardParser::parseVCards(const QByteArray &text)
+{
+    VCard currentVCard;
+    VCard::List vCardList;
+    QByteArray currentLine;
+
+    QList<QByteArray> lines = text.split('\n');
+
+    bool inVCard = false;
+    QList<QByteArray>::Iterator it(lines.begin());
+    QList<QByteArray>::Iterator linesEnd(lines.end());
+
+    StringCache cache;
+
+    for (; it != linesEnd; ++it) {
+        // remove the trailing \r, left from \r\n
+        if ((*it).endsWith('\r')) {
+            (*it).chop(1);
+        }
+
+        if ((*it).startsWith(' ') ||
+                (*it).startsWith('\t')) {      //folded line => append to previous
+            currentLine.append((*it).mid(1));
+            continue;
+        } else {
+            if ((*it).trimmed().isEmpty()) {     // empty line
+                continue;
+            }
+            if (inVCard && !currentLine.isEmpty()) {   // now parse the line
+                int colon = currentLine.indexOf(':');
+                if (colon == -1) {   // invalid line
+                    currentLine = (*it);
+                    continue;
+                }
+
+                VCardLine vCardLine;
+                const QByteArray key = currentLine.left(colon).trimmed();
+                QByteArray value = currentLine.mid(colon + 1);
+
+                QList<QByteArray> params = key.split(';');
+
+                // check for group
+                int groupPos = params[ 0 ].indexOf('.');
+                if (groupPos != -1) {
+                    vCardLine.setGroup(cache.fromLatin1(params[ 0 ].left(groupPos)));
+                    vCardLine.setIdentifier(cache.fromLatin1(params[ 0 ].mid(groupPos + 1)));
+                } else {
+                    vCardLine.setIdentifier(cache.fromLatin1(params[ 0 ]));
+                }
+
+                if (params.count() > 1) {   // find all parameters
+                    QList<QByteArray>::ConstIterator paramIt(params.constBegin());
+                    for (++paramIt; paramIt != params.constEnd(); ++paramIt) {
+                        QList<QByteArray> pair = (*paramIt).split('=');
+                        if (pair.count() == 1) {
+                            // correct the fucking 2.1 'standard'
+                            if (pair[ 0 ].toLower() == "quoted-printable") {
+                                pair[ 0 ] = "encoding";
+                                pair.append("quoted-printable");
+                            } else if (pair[ 0 ].toLower() == "base64") {
+                                pair[ 0 ] = "encoding";
+                                pair.append("base64");
+                            } else {
+                                pair.prepend("type");
+                            }
+                        }
+                        if (pair[ 1 ].indexOf(',') != -1) {     // parameter in type=x,y,z format
+                            const QList<QByteArray> args = pair[ 1 ].split(',');
+                            QList<QByteArray>::ConstIterator argIt;
+                            QList<QByteArray>::ConstIterator argEnd(args.constEnd());
+                            for (argIt = args.constBegin(); argIt != argEnd; ++argIt) {
+                                vCardLine.addParameter(cache.fromLatin1(pair[ 0 ].toLower()),
+                                                       cache.fromLatin1(*argIt));
+                            }
+                        } else {
+                            vCardLine.addParameter(cache.fromLatin1(pair[ 0 ].toLower()),
+                                                   cache.fromLatin1(pair[ 1 ]));
+                        }
+                    }
+                }
+
+                removeEscapes(value);
+
+                QByteArray output;
+                bool wasBase64Encoded = false;
+
+                if (vCardLine.parameterList().contains(QStringLiteral("encoding"))) {
+                    const QString encoding = vCardLine.parameter(QStringLiteral("encoding")).toLower();
+
+                    // have to decode the data
+                    if (encoding == QLatin1String("b") || encoding == QLatin1String("base64")) {
+                        output = QByteArray::fromBase64(value);
+                        wasBase64Encoded = true;
+                    } else if (encoding == QLatin1String("quoted-printable")) {
+                        // join any qp-folded lines
+                        while (value.endsWith('=') && it != linesEnd) {
+                            value.chop(1);   // remove the '='
+                            value.append(*it);
+                            ++it;
+                            // remove the trailing \r, left from \r\n
+                            if ((*it).endsWith('\r')) {
+                                (*it).chop(1);
+                            }
+                        }
+                        KCodecs::quotedPrintableDecode(value, output);
+                    } else if (encoding == QLatin1String("8bit")) {
+                        output = value;
+                    } else {
+                        qDebug("Unknown vcard encoding type!");
+                    }
+                } else {
+                    output = value;
+                }
+
+                if (vCardLine.parameterList().contains(QStringLiteral("charset"))) {
+                    // have to convert the data
+                    QTextCodec *codec = QTextCodec::codecForName(
+                                            vCardLine.parameter(QStringLiteral("charset")).toLatin1());
+                    if (codec) {
+                        vCardLine.setValue(codec->toUnicode(output));
+                    } else {
+                        vCardLine.setValue(QString::fromUtf8(output));
+                    }
+                } else if (wasBase64Encoded) {
+                    vCardLine.setValue(output);
+                } else {
+                    vCardLine.setValue(QString::fromUtf8(output));
+                }
+
+                currentVCard.addLine(vCardLine);
+            }
+
+            // we do not save the start and end tag as vcardline
+            if ((*it).toLower().startsWith("begin:vcard")) {       //krazy:exclude=strings
+                inVCard = true;
+                currentLine.clear();
+                currentVCard.clear(); // flush vcard
+                continue;
+            }
+
+            if ((*it).toLower().startsWith("end:vcard")) {       //krazy:exclude=strings
+                inVCard = false;
+                vCardList.append(currentVCard);
+                currentLine.clear();
+                currentVCard.clear(); // flush vcard
+                continue;
+            }
+
+            currentLine = (*it);
+        }
+    }
+
+    return vCardList;
+}
+
+QByteArray VCardParser::createVCards(const VCard::List &list)
+{
+    QByteArray text;
+    QByteArray textLine;
+    QString encodingType;
+    QStringList idents;
+    QStringList params;
+    QStringList values;
+    QStringList::ConstIterator identIt;
+    QStringList::Iterator paramIt;
+    QStringList::ConstIterator valueIt;
+
+    VCardLine::List lines;
+    VCardLine::List::ConstIterator lineIt;
+    VCard::List::ConstIterator cardIt;
+
+    bool hasEncoding;
+
+    text.reserve(list.size() * 300);   // reserve memory to be more efficient
+
+    // iterate over the cards
+    VCard::List::ConstIterator listEnd(list.end());
+    for (cardIt = list.begin(); cardIt != listEnd; ++cardIt) {
+        text.append("BEGIN:VCARD\r\n");
+
+        idents = (*cardIt).identifiers();
+        //VERSION must be first
+        if (idents.contains(QStringLiteral("VERSION"))) {
+            const QString str = idents.takeAt(idents.indexOf(QStringLiteral("VERSION")));
+            idents.prepend(str);
+        }
+
+        for (identIt = idents.constBegin(); identIt != idents.constEnd(); ++identIt) {
+            lines = (*cardIt).lines((*identIt));
+
+            // iterate over the lines
+            for (lineIt = lines.constBegin(); lineIt != lines.constEnd(); ++lineIt) {
+                QVariant val = (*lineIt).value();
+                if (val.isValid()) {
+                    if ((*lineIt).hasGroup()) {
+                        textLine = (*lineIt).group().toLatin1() + '.' + (*lineIt).identifier().toLatin1();
+                    } else {
+                        textLine = (*lineIt).identifier().toLatin1();
+                    }
+
+                    params = (*lineIt).parameterList();
+                    hasEncoding = false;
+                    if (!params.isEmpty()) {   // we have parameters
+                        for (paramIt = params.begin(); paramIt != params.end(); ++paramIt) {
+                            if ((*paramIt) == QLatin1String("encoding")) {
+                                hasEncoding = true;
+                                encodingType = (*lineIt).parameter(QStringLiteral("encoding")).toLower();
+                            }
+
+                            values = (*lineIt).parameters(*paramIt);
+                            for (valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt) {
+                                textLine.append(';' + (*paramIt).toLatin1().toUpper());
+                                if (!(*valueIt).isEmpty()) {
+                                    textLine.append('=' + (*valueIt).toLatin1());
+                                }
+                            }
+                        }
+                    }
+
+                    QByteArray input, output;
+                    bool checkMultibyte = false;  // avoid splitting a multibyte character
+
+                    // handle charset
+                    if ((*lineIt).parameterList().contains(QStringLiteral("charset"))) {
+                        // have to convert the data
+                        const QString value = (*lineIt).value().toString();
+                        QTextCodec *codec = QTextCodec::codecForName(
+                                                (*lineIt).parameter(QStringLiteral("charset")).toLatin1());
+                        if (codec) {
+                            input = codec->fromUnicode(value);
+                        } else {
+                            checkMultibyte = true;
+                            input = value.toUtf8();
+                        }
+                    } else if ((*lineIt).value().type() == QVariant::ByteArray) {
+                        input = (*lineIt).value().toByteArray();
+                    } else {
+                        checkMultibyte = true;
+                        input = (*lineIt).value().toString().toUtf8();
+                    }
+
+                    // handle encoding
+                    if (hasEncoding) {   // have to encode the data
+                        if (encodingType == QLatin1String("b")) {
+                            checkMultibyte = false;
+                            output = input.toBase64();
+                        } else if (encodingType == QLatin1String("quoted-printable")) {
+                            checkMultibyte = false;
+                            KCodecs::quotedPrintableEncode(input, output, false);
+                        }
+                    } else {
+                        output = input;
+                    }
+                    addEscapes(output, ((*lineIt).identifier() == QLatin1String("CATEGORIES") || (*lineIt).identifier() == QLatin1String("GEO")));
+
+                    if (!output.isEmpty()) {
+                        textLine.append(':' + output);
+
+                        if (textLine.length() > FOLD_WIDTH) {   // we have to fold the line
+                            if (checkMultibyte) {
+                                // RFC 6350: Multi-octet characters MUST remain contiguous.
+                                // we know that textLine contains UTF-8 encoded characters
+                                int lineLength = 0;
+                                for (int i = 0; i < textLine.length(); ++i) {
+                                    if ((textLine[i] & 0xC0) == 0xC0) {    // a multibyte sequence follows
+                                        int sequenceLength = 2;
+                                        if ((textLine[i] & 0xE0) == 0xE0) {
+                                            sequenceLength = 3;
+                                        } else if ((textLine[i] & 0xF0) == 0xF0) {
+                                            sequenceLength = 4;
+                                        }
+                                        if ((lineLength + sequenceLength) > FOLD_WIDTH) {
+                                            // the current line would be too long. fold it
+                                            text += "\r\n " + textLine.mid(i, sequenceLength);
+                                            lineLength = 1 + sequenceLength;  // incl. leading space
+                                        } else {
+                                            text += textLine.mid(i, sequenceLength);
+                                            lineLength += sequenceLength;
+                                        }
+                                        i += sequenceLength - 1;
+                                    } else {
+                                        text += textLine[i];
+                                        ++lineLength;
+                                    }
+                                    if ((lineLength == FOLD_WIDTH) && (i < (textLine.length() - 1))) {
+                                        text += "\r\n ";
+                                        lineLength = 1;  // leading space
+                                    }
+                                }
+                                text += "\r\n";
+                            } else {
+                                for (int i = 0; i <= (textLine.length() / FOLD_WIDTH); ++i) {
+                                    text.append(
+                                        (i == 0 ? "" : " ") + textLine.mid(i * FOLD_WIDTH, FOLD_WIDTH) + "\r\n");
+                                }
+                            }
+                        } else {
+                            text.append(textLine + "\r\n");
+                        }
+                    }
+                }
+            }
+        }
+
+        text.append("END:VCARD\r\n");
+        text.append("\r\n");
+    }
+
+    return text;
+}
diff --git a/src/vcardparser/vcardparser.h b/src/vcardparser/vcardparser.h
new file mode 100644 (file)
index 0000000..4fc62a9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef VCARDPARSER_H
+#define VCARDPARSER_H
+
+#include "vcard.h"
+#include <QtCore/QByteArray>
+
+namespace KContacts
+{
+
+class VCardParser
+{
+public:
+    VCardParser();
+    ~VCardParser();
+
+    static VCard::List parseVCards(const QByteArray &text);
+    static QByteArray createVCards(const VCard::List &list);
+
+private:
+    Q_DISABLE_COPY(VCardParser)
+    class VCardParserPrivate;
+    VCardParserPrivate *d;
+};
+
+}
+
+#endif
diff --git a/src/vcardtool.cpp b/src/vcardtool.cpp
new file mode 100644 (file)
index 0000000..2c532c3
--- /dev/null
@@ -0,0 +1,1409 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+    Copyright (c) 2015 Laurent Montel <montel@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "vcardtool.h"
+#include "key.h"
+#include "picture.h"
+#include "secrecy.h"
+#include "sound.h"
+#include "lang.h"
+#include "gender.h"
+#include "related.h"
+#include <QtCore/QString>
+#include <QtCore/QBuffer>
+#include <QDebug>
+
+using namespace KContacts;
+
+static bool needsEncoding(const QString &value)
+{
+    uint length = value.length();
+    for (uint i = 0; i < length; ++i) {
+        char c = value.at(i).toLatin1();
+        if ((c < 33 || c > 126) && c != ' ' && c != '=') {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+VCardTool::VCardTool()
+{
+    mAddressTypeMap.insert(QStringLiteral("dom"), Address::Dom);
+    mAddressTypeMap.insert(QStringLiteral("intl"), Address::Intl);
+    mAddressTypeMap.insert(QStringLiteral("postal"), Address::Postal);
+    mAddressTypeMap.insert(QStringLiteral("parcel"), Address::Parcel);
+    mAddressTypeMap.insert(QStringLiteral("home"), Address::Home);
+    mAddressTypeMap.insert(QStringLiteral("work"), Address::Work);
+    mAddressTypeMap.insert(QStringLiteral("pref"), Address::Pref);
+
+    mPhoneTypeMap.insert(QStringLiteral("HOME"), PhoneNumber::Home);
+    mPhoneTypeMap.insert(QStringLiteral("WORK"), PhoneNumber::Work);
+    mPhoneTypeMap.insert(QStringLiteral("MSG"), PhoneNumber::Msg);
+    mPhoneTypeMap.insert(QStringLiteral("PREF"), PhoneNumber::Pref);
+    mPhoneTypeMap.insert(QStringLiteral("VOICE"), PhoneNumber::Voice);
+    mPhoneTypeMap.insert(QStringLiteral("FAX"), PhoneNumber::Fax);
+    mPhoneTypeMap.insert(QStringLiteral("CELL"), PhoneNumber::Cell);
+    mPhoneTypeMap.insert(QStringLiteral("VIDEO"), PhoneNumber::Video);
+    mPhoneTypeMap.insert(QStringLiteral("BBS"), PhoneNumber::Bbs);
+    mPhoneTypeMap.insert(QStringLiteral("MODEM"), PhoneNumber::Modem);
+    mPhoneTypeMap.insert(QStringLiteral("CAR"), PhoneNumber::Car);
+    mPhoneTypeMap.insert(QStringLiteral("ISDN"), PhoneNumber::Isdn);
+    mPhoneTypeMap.insert(QStringLiteral("PCS"), PhoneNumber::Pcs);
+    mPhoneTypeMap.insert(QStringLiteral("PAGER"), PhoneNumber::Pager);
+}
+
+VCardTool::~VCardTool()
+{
+}
+
+QByteArray VCardTool::exportVCards(const Addressee::List &list, VCard::Version version) const
+{
+    return createVCards(list, version, true /*export vcard*/);
+}
+
+QByteArray VCardTool::createVCards(const Addressee::List &list, VCard::Version version) const
+{
+    return createVCards(list, version, false /*don't export*/);
+}
+
+void VCardTool::addParameter(VCardLine &line, VCard::Version version, const QString &key, const QStringList &valueStringList) const
+{
+    if (version == VCard::v2_1) {
+        Q_FOREACH (const QString &valueStr, valueStringList) {
+            line.addParameter(valueStr, QString());
+        }
+    } else {
+        line.addParameter(key, valueStringList.join(QStringLiteral(",")));
+    }
+}
+
+QByteArray VCardTool::createVCards(const Addressee::List &list,
+                                   VCard::Version version, bool exportVcard) const
+{
+    VCard::List vCardList;
+
+    Addressee::List::ConstIterator addrIt;
+    Addressee::List::ConstIterator listEnd(list.constEnd());
+    for (addrIt = list.constBegin(); addrIt != listEnd; ++addrIt) {
+        VCard card;
+        QStringList::ConstIterator strIt;
+        // VERSION
+        if (version == VCard::v2_1) {
+            card.addLine(VCardLine(QStringLiteral("VERSION"), QLatin1String("2.1")));
+        } else if (version == VCard::v3_0) {
+            card.addLine(VCardLine(QStringLiteral("VERSION"), QLatin1String("3.0")));
+        } else if (version == VCard::v4_0) {
+            card.addLine(VCardLine(QStringLiteral("VERSION"), QLatin1String("4.0")));
+        }
+
+        // ADR + LABEL
+        const Address::List addresses = (*addrIt).addresses();
+        for (Address::List::ConstIterator it = addresses.begin(); it != addresses.end(); ++it) {
+            QStringList address;
+
+            const bool isEmpty = ((*it).postOfficeBox().isEmpty() &&
+                                  (*it).extended().isEmpty() &&
+                                  (*it).street().isEmpty() &&
+                                  (*it).locality().isEmpty() &&
+                                  (*it).region().isEmpty() &&
+                                  (*it).postalCode().isEmpty() &&
+                                  (*it).country().isEmpty());
+
+            address.append((*it).postOfficeBox().replace(QLatin1Char(';'),
+                           QLatin1String("\\;")));
+
+            address.append((*it).extended().replace(QLatin1Char(';'),
+                                                    QLatin1String("\\;")));
+
+            address.append((*it).street().replace(QLatin1Char(';'),
+                                                  QLatin1String("\\;")));
+
+            address.append((*it).locality().replace(QLatin1Char(';'),
+                                                    QLatin1String("\\;")));
+
+            address.append((*it).region().replace(QLatin1Char(';'),
+                                                  QLatin1String("\\;")));
+
+            address.append((*it).postalCode().replace(QLatin1Char(';'),
+                           QLatin1String("\\;")));
+
+            address.append((*it).country().replace(QLatin1Char(';'),
+                                                   QLatin1String("\\;")));
+
+            VCardLine adrLine(QStringLiteral("ADR"), address.join(QStringLiteral(";")));
+            if (version == VCard::v2_1 && needsEncoding(address.join(QStringLiteral(";")))) {
+                adrLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+                adrLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+            }
+
+            VCardLine labelLine(QStringLiteral("LABEL"), (*it).label());
+            if (version == VCard::v2_1 && needsEncoding((*it).label())) {
+                labelLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+                labelLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+            }
+
+            const bool hasLabel = !(*it).label().isEmpty();
+            QMap<QString, Address::TypeFlag>::ConstIterator typeIt;
+            QStringList addreLineType;
+            QStringList labelLineType;
+            for (typeIt = mAddressTypeMap.constBegin();
+                    typeIt != mAddressTypeMap.constEnd(); ++typeIt) {
+                if (typeIt.value() & (*it).type()) {
+                    addreLineType << typeIt.key();
+                    if (hasLabel) {
+                        labelLineType << typeIt.key();
+                    }
+                }
+            }
+
+            if (!isEmpty) {
+                addParameter(adrLine, version, QStringLiteral("TYPE"), addreLineType);
+                card.addLine(adrLine);
+            }
+            if (hasLabel) {
+                addParameter(labelLine, version, QStringLiteral("TYPE"), labelLineType);
+                card.addLine(labelLine);
+            }
+        }
+
+        // BDAY
+        card.addLine(VCardLine(QStringLiteral("BDAY"), createDateTime((*addrIt).birthday(), version)));
+
+        //Laurent: 31 Jan 2015. Not necessary to export it. When Categories were changes as AkonadiTag nobody thought that it was break categorie support...
+        //=> not necessary to export just tag...
+        // CATEGORIES only > 2.1
+        if (!exportVcard) {
+            if (version != VCard::v2_1) {
+                QStringList categories = (*addrIt).categories();
+                QStringList::Iterator catIt;
+                QStringList::Iterator catEnd(categories.end());
+                for (catIt = categories.begin(); catIt != catEnd; ++catIt) {
+                    (*catIt).replace(QLatin1Char(','), QLatin1String("\\,"));
+                }
+
+                VCardLine catLine(QStringLiteral("CATEGORIES"), categories.join(QStringLiteral(",")));
+                card.addLine(catLine);
+            }
+        }
+        // MEMBER (only in 4.0)
+        if (version == VCard::v4_0) {
+            // The KIND property must be set to "group" in order to use this property.
+            if ((*addrIt).kind().toLower() == QLatin1String("group")) {
+                Q_FOREACH (const QString &member, (*addrIt).members()) {
+                    VCardLine line(QStringLiteral("MEMBER"), member);
+                    card.addLine(line);
+                }
+            }
+        }
+        // SOURCE
+        Q_FOREACH (const QUrl &url, (*addrIt).sourcesUrlList()) {
+            VCardLine line = VCardLine(QStringLiteral("SOURCE"), url.url());
+            card.addLine(line);
+        }
+
+        if (version == VCard::v4_0) {
+            const Related::List relatedList = (*addrIt).relationShips();
+            Related::List::ConstIterator relatedIt;
+            Related::List::ConstIterator relatedEnd(relatedList.end());
+            for (relatedIt = relatedList.begin(); relatedIt != relatedEnd; ++relatedIt) {
+                VCardLine line(QStringLiteral("RELATED"), (*relatedIt).related());
+                QMapIterator<QString, QStringList> i((*relatedIt).parameters());
+                while (i.hasNext()) {
+                    i.next();
+                    line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+                }
+                card.addLine(line);
+            }
+        }
+        // CLASS only for version == 3.0
+        if (version == VCard::v3_0) {
+            card.addLine(createSecrecy((*addrIt).secrecy()));
+        }
+        // LANG only for version == 4.0
+        if (version == VCard::v4_0) {
+            const Lang::List langList = (*addrIt).langs();
+            Lang::List::ConstIterator langIt;
+            Lang::List::ConstIterator langEnd(langList.end());
+            for (langIt = langList.begin(); langIt != langEnd; ++langIt) {
+                VCardLine line(QStringLiteral("LANG"), (*langIt).language());
+                QMapIterator<QString, QStringList> i((*langIt).parameters());
+                while (i.hasNext()) {
+                    i.next();
+                    line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+                }
+                card.addLine(line);
+            }
+        }
+        // EMAIL
+        const Email::List emailList = (*addrIt).emailList();
+        Email::List::ConstIterator emailIt;
+        Email::List::ConstIterator emailEnd(emailList.end());
+        for (emailIt = emailList.begin(); emailIt != emailEnd; ++emailIt) {
+            VCardLine line(QStringLiteral("EMAIL"), (*emailIt).mail());
+            QMapIterator<QString, QStringList> i((*emailIt).parameters());
+            while (i.hasNext()) {
+                i.next();
+                if (version == VCard::v2_1) {
+                    if (i.key().toLower() == QLatin1String("type")) {
+                        QStringList valueStringList = i.value();
+                        bool hasPreferred = false;
+                        if (valueStringList.contains(QStringLiteral("PREF"))) {
+                            valueStringList.removeAll(QStringLiteral("PREF"));
+                            hasPreferred = true;
+                        }
+                        if (!valueStringList.isEmpty()) {
+                            addParameter(line, version, i.key(), valueStringList);
+                        }
+                        if (hasPreferred) {
+                            line.addParameter(QStringLiteral("PREF"), QString());
+                        }
+                    } else {
+                        line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+                    }
+                } else {
+                    line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+                }
+            }
+            card.addLine(line);
+        }
+
+        // FN required for only version > 2.1
+        VCardLine fnLine(QStringLiteral("FN"), (*addrIt).formattedName());
+        if (version == VCard::v2_1 && needsEncoding((*addrIt).formattedName())) {
+            fnLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+            fnLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+        }
+        card.addLine(fnLine);
+
+        // GEO
+        const Geo geo = (*addrIt).geo();
+        if (geo.isValid()) {
+            QString str;
+            if (version == VCard::v4_0) {
+                str.sprintf("geo:%.6f,%.6f", geo.latitude(), geo.longitude());
+            } else {
+                str.sprintf("%.6f;%.6f", geo.latitude(), geo.longitude());
+            }
+            card.addLine(VCardLine(QStringLiteral("GEO"), str));
+        }
+
+        // KEY
+        const Key::List keys = (*addrIt).keys();
+        Key::List::ConstIterator keyIt;
+        Key::List::ConstIterator keyEnd(keys.end());
+        for (keyIt = keys.begin(); keyIt != keyEnd; ++keyIt) {
+            card.addLine(createKey(*keyIt, version));
+        }
+
+        // LOGO
+        card.addLine(createPicture(QStringLiteral("LOGO"), (*addrIt).logo(), version));
+        Q_FOREACH (const Picture &logo, (*addrIt).extraLogoList()) {
+            card.addLine(createPicture(QStringLiteral("LOGO"), logo, version));
+        }
+
+        // MAILER only for version < 4.0
+        if (version != VCard::v4_0) {
+            VCardLine mailerLine(QStringLiteral("MAILER"), (*addrIt).mailer());
+            if (version == VCard::v2_1 && needsEncoding((*addrIt).mailer())) {
+                mailerLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+                mailerLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+            }
+            card.addLine(mailerLine);
+        }
+
+        // N required for only version < 4.0
+        QStringList name;
+        name.append((*addrIt).familyName().replace(QLatin1Char(';'),
+                    QLatin1String("\\;")));
+
+        name.append((*addrIt).givenName().replace(QLatin1Char(';'),
+                    QLatin1String("\\;")));
+
+        name.append((*addrIt).additionalName().replace(QLatin1Char(';'),
+                    QLatin1String("\\;")));
+
+        name.append((*addrIt).prefix().replace(QLatin1Char(';'),
+                                               QLatin1String("\\;")));
+
+        name.append((*addrIt).suffix().replace(QLatin1Char(';'),
+                                               QLatin1String("\\;")));
+
+        VCardLine nLine(QStringLiteral("N"), name.join(QStringLiteral(";")));
+        if (version == VCard::v2_1 && needsEncoding(name.join(QStringLiteral(";")))) {
+            nLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+            nLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+        }
+        if (version == VCard::v4_0 && !(*addrIt).sortString().isEmpty()) {
+            nLine.addParameter(QStringLiteral("SORT-AS"), (*addrIt).sortString());
+        }
+
+        card.addLine(nLine);
+
+        // NAME only for version < 4.0
+        if (version != VCard::v4_0) {
+            VCardLine nameLine(QStringLiteral("NAME"), (*addrIt).name());
+            if (version == VCard::v2_1 && needsEncoding((*addrIt).name())) {
+                nameLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+                nameLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+            }
+            card.addLine(nameLine);
+        }
+
+        // NICKNAME only for version > 2.1
+        if (version != VCard::v2_1) {
+            card.addLine(VCardLine(QStringLiteral("NICKNAME"), (*addrIt).nickName()));
+        }
+
+        // NOTE
+        VCardLine noteLine(QStringLiteral("NOTE"), (*addrIt).note());
+        if (version == VCard::v2_1 && needsEncoding((*addrIt).note())) {
+            noteLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+            noteLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+        }
+        card.addLine(noteLine);
+
+        // ORG
+        QStringList organization;
+        organization.append((*addrIt).organization().replace(QLatin1Char(';'),
+                            QLatin1String("\\;")));
+        if (!(*addrIt).department().isEmpty()) {
+            organization.append((*addrIt).department().replace(QLatin1Char(';'),
+                                QLatin1String("\\;")));
+        }
+        VCardLine orgLine(QStringLiteral("ORG"), organization.join(QStringLiteral(";")));
+        if (version == VCard::v2_1 && needsEncoding(organization.join(QStringLiteral(";")))) {
+            orgLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+            orgLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+        }
+        card.addLine(orgLine);
+
+        // PHOTO
+        card.addLine(createPicture(QStringLiteral("PHOTO"), (*addrIt).photo(), version));
+        Q_FOREACH (const Picture &photo, (*addrIt).extraPhotoList()) {
+            card.addLine(createPicture(QStringLiteral("PHOTO"), photo, version));
+        }
+
+        // PROID only for version > 2.1
+        if (version != VCard::v2_1) {
+            card.addLine(VCardLine(QStringLiteral("PRODID"), (*addrIt).productId()));
+        }
+
+        // REV
+        card.addLine(VCardLine(QStringLiteral("REV"), createDateTime((*addrIt).revision(), version)));
+
+        // ROLE
+        VCardLine roleLine(QStringLiteral("ROLE"), (*addrIt).role());
+        if (version == VCard::v2_1 && needsEncoding((*addrIt).role())) {
+            roleLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+            roleLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+        }
+        card.addLine(roleLine);
+
+        // SORT-STRING
+        if (version == VCard::v3_0) {
+            card.addLine(VCardLine(QStringLiteral("SORT-STRING"), (*addrIt).sortString()));
+        }
+
+        // SOUND
+        card.addLine(createSound((*addrIt).sound(), version));
+        Q_FOREACH (const Sound &sound, (*addrIt).extraSoundList()) {
+            card.addLine(createSound(sound, version));
+        }
+
+        // TEL
+        const PhoneNumber::List phoneNumbers = (*addrIt).phoneNumbers();
+        PhoneNumber::List::ConstIterator phoneIt;
+        PhoneNumber::List::ConstIterator phoneEnd(phoneNumbers.end());
+        for (phoneIt = phoneNumbers.begin(); phoneIt != phoneEnd; ++phoneIt) {
+            VCardLine line(QStringLiteral("TEL"), (*phoneIt).number());
+
+            QMap<QString, PhoneNumber::TypeFlag>::ConstIterator typeIt;
+            QMap<QString, PhoneNumber::TypeFlag>::ConstIterator typeEnd(mPhoneTypeMap.constEnd());
+            QStringList lst;
+            for (typeIt = mPhoneTypeMap.constBegin(); typeIt != typeEnd; ++typeIt) {
+                if (typeIt.value() & (*phoneIt).type()) {
+                    lst << typeIt.key();
+
+                }
+            }
+            if (!lst.isEmpty()) {
+                addParameter(line, version, QStringLiteral("TYPE"), lst);
+            }
+            card.addLine(line);
+        }
+
+        // TITLE
+        VCardLine titleLine(QStringLiteral("TITLE"), (*addrIt).title());
+        if (version == VCard::v2_1 && needsEncoding((*addrIt).title())) {
+            titleLine.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+            titleLine.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+        }
+        card.addLine(titleLine);
+
+        // TZ
+        // TODO Add vcard4.0 support
+        const TimeZone timeZone = (*addrIt).timeZone();
+        if (timeZone.isValid()) {
+            QString str;
+
+            int neg = 1;
+            if (timeZone.offset() < 0) {
+                neg = -1;
+            }
+
+            str.sprintf("%c%02d:%02d", (timeZone.offset() >= 0 ? '+' : '-'),
+                        (timeZone.offset() / 60) * neg,
+                        (timeZone.offset() % 60) * neg);
+
+            card.addLine(VCardLine(QStringLiteral("TZ"), str));
+        }
+
+        // UID
+        card.addLine(VCardLine(QStringLiteral("UID"), (*addrIt).uid()));
+
+        // URL
+        Q_FOREACH (const ResourceLocatorUrl &url, (*addrIt).extraUrlList()) {
+            VCardLine line(QStringLiteral("URL"), url.url());
+            QMapIterator<QString, QStringList> i(url.parameters());
+            while (i.hasNext()) {
+                i.next();
+                line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+            }
+            card.addLine(line);
+        }
+        if (version == VCard::v4_0) {
+            // GENDER
+            const Gender gender = (*addrIt).gender();
+            if (gender.isValid()) {
+                QString genderStr;
+                if (!gender.gender().isEmpty()) {
+                    genderStr = gender.gender();
+                }
+                if (!gender.comment().isEmpty()) {
+                    genderStr += QLatin1Char(';') + gender.comment();
+                }
+                VCardLine line(QStringLiteral("GENDER"), genderStr);
+                card.addLine(line);
+            }
+            // KIND
+            if (!(*addrIt).kind().isEmpty()) {
+                VCardLine line(QStringLiteral("KIND"), (*addrIt).kind());
+                card.addLine(line);
+            }
+        }
+        // From vcard4.
+        if (version == VCard::v4_0) {
+            Q_FOREACH (const CalendarUrl &url, (*addrIt).calendarUrlList()) {
+                if (url.isValid()) {
+                    QString type;
+                    switch (url.type()) {
+                    case CalendarUrl::Unknown:
+                    case CalendarUrl::EndCalendarType:
+                        break;
+                    case CalendarUrl::FBUrl:
+                        type = QStringLiteral("FBURL");
+                        break;
+                    case CalendarUrl::CALUri:
+                        type = QStringLiteral("CALURI");
+                        break;
+                    case CalendarUrl::CALADRUri:
+                        type = QStringLiteral("CALADRURI");
+                        break;
+
+                    }
+                    if (!type.isEmpty()) {
+                        VCardLine line(type, url.url().toDisplayString());
+                        QMapIterator<QString, QStringList> i(url.parameters());
+                        while (i.hasNext()) {
+                            i.next();
+                            line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+                        }
+                        card.addLine(line);
+                    }
+                }
+            }
+        }
+        // IMPP (supported in vcard 3 too)
+        Q_FOREACH (const Impp &impp, (*addrIt).imppList()) {
+            VCardLine line(QStringLiteral("IMPP"), impp.address());
+            line.addParameter(QStringLiteral("X-SERVICE-TYPE"), Impp::typeToString(impp.type()));
+            QMapIterator<QString, QStringList> i(impp.parameters());
+            while (i.hasNext()) {
+                i.next();
+                if (i.key().toLower() != QStringLiteral("x-service-type")) {
+                    line.addParameter(i.key(), i.value().join(QStringLiteral(",")));
+                }
+            }
+            card.addLine(line);
+        }
+
+        // X-
+        const QStringList customs = (*addrIt).customs();
+        for (strIt = customs.begin(); strIt != customs.end(); ++strIt) {
+            QString identifier = QLatin1String("X-") +
+                                 (*strIt).left((*strIt).indexOf(QLatin1Char(':')));
+            const QString value = (*strIt).mid((*strIt).indexOf(QLatin1Char(':')) + 1);
+            if (value.isEmpty()) {
+                continue;
+            }
+            //Convert to standard identifier
+            if (exportVcard) {
+                if (identifier == QLatin1String("X-messaging/aim-All")) {
+                    identifier = QStringLiteral("X-AIM");
+                } else if (identifier == QLatin1String("X-messaging/icq-All")) {
+                    identifier = QStringLiteral("X-ICQ");
+                } else if (identifier == QLatin1String("X-messaging/xmpp-All")) {
+                    identifier = QStringLiteral("X-JABBER");
+                } else if (identifier == QLatin1String("X-messaging/msn-All")) {
+                    identifier = QStringLiteral("X-MSN");
+                } else if (identifier == QLatin1String("X-messaging/yahoo-All")) {
+                    identifier = QStringLiteral("X-YAHOO");
+                } else if (identifier == QLatin1String("X-messaging/gadu-All")) {
+                    identifier = QStringLiteral("X-GADUGADU");
+                } else if (identifier == QLatin1String("X-messaging/skype-All")) {
+                    identifier = QStringLiteral("X-SKYPE");
+                } else if (identifier == QLatin1String("X-messaging/groupwise-All")) {
+                    identifier = QStringLiteral("X-GROUPWISE");
+                } else if (identifier == QLatin1String("X-messaging/sms-All")) {
+                    identifier = QStringLiteral("X-SMS");
+                } else if (identifier == QLatin1String("X-messaging/meanwhile-All")) {
+                    identifier = QStringLiteral("X-MEANWHILE");
+                } else if (identifier == QLatin1String("X-messaging/irc-All")) {
+                    identifier = QStringLiteral("X-IRC");   //Not defined by rfc but need for fixing #300869
+                } else if (identifier == QLatin1String("X-messaging/googletalk-All")) {
+                    //Not defined by rfc but need for fixing #300869
+                    identifier = QStringLiteral("X-GTALK");
+                } else if (identifier == QLatin1String("X-messaging/twitter-All")) {
+                    identifier = QStringLiteral("X-TWITTER");
+                }
+            }
+            if (identifier.toLower() == QLatin1String("x-kaddressbook-x-anniversary") && version == VCard::v4_0) {
+                // ANNIVERSARY
+                if (!value.isEmpty()) {
+                    const QDate date = QDate::fromString(value, Qt::ISODate);
+                    QDateTime dt = QDateTime(date);
+                    dt.setTime(QTime());
+                    card.addLine(VCardLine(QStringLiteral("ANNIVERSARY"), createDateTime(dt, version)));
+                }
+            } else {
+                VCardLine line(identifier, value);
+                if (version == VCard::v2_1 && needsEncoding(value)) {
+                    line.addParameter(QStringLiteral("charset"), QStringLiteral("UTF-8"));
+                    line.addParameter(QStringLiteral("encoding"), QStringLiteral("QUOTED-PRINTABLE"));
+                }
+                card.addLine(line);
+            }
+        }
+
+        vCardList.append(card);
+    }
+
+    return VCardParser::createVCards(vCardList);
+}
+
+Addressee::List VCardTool::parseVCards(const QByteArray &vcard) const
+{
+    static const QLatin1Char semicolonSep(';');
+    static const QLatin1Char commaSep(',');
+    QString identifier;
+
+    Addressee::List addrList;
+    const VCard::List vCardList = VCardParser::parseVCards(vcard);
+
+    VCard::List::ConstIterator cardIt;
+    VCard::List::ConstIterator listEnd(vCardList.end());
+    for (cardIt = vCardList.begin(); cardIt != listEnd; ++cardIt) {
+        Addressee addr;
+
+        const QStringList idents = (*cardIt).identifiers();
+        QStringList::ConstIterator identIt;
+        QStringList::ConstIterator identEnd(idents.end());
+        for (identIt = idents.begin(); identIt != identEnd; ++identIt) {
+            const VCardLine::List lines = (*cardIt).lines((*identIt));
+            VCardLine::List::ConstIterator lineIt;
+
+            // iterate over the lines
+            for (lineIt = lines.begin(); lineIt != lines.end(); ++lineIt) {
+                identifier = (*lineIt).identifier().toLower();
+                // ADR
+                if (identifier == QLatin1String("adr")) {
+                    Address address;
+                    const QStringList addrParts = splitString(semicolonSep, (*lineIt).value().toString());
+                    if (addrParts.count() > 0) {
+                        address.setPostOfficeBox(addrParts.at(0));
+                    }
+                    if (addrParts.count() > 1) {
+                        address.setExtended(addrParts.at(1));
+                    }
+                    if (addrParts.count() > 2) {
+                        address.setStreet(addrParts.at(2));
+                    }
+                    if (addrParts.count() > 3) {
+                        address.setLocality(addrParts.at(3));
+                    }
+                    if (addrParts.count() > 4) {
+                        address.setRegion(addrParts.at(4));
+                    }
+                    if (addrParts.count() > 5) {
+                        address.setPostalCode(addrParts.at(5));
+                    }
+                    if (addrParts.count() > 6) {
+                        address.setCountry(addrParts.at(6));
+                    }
+
+                    Address::Type type;
+
+                    const QStringList types = (*lineIt).parameters(QStringLiteral("type"));
+                    QStringList::ConstIterator end(types.end());
+                    for (QStringList::ConstIterator it = types.begin(); it != end; ++it) {
+                        type |= mAddressTypeMap[(*it).toLower() ];
+                    }
+
+                    address.setType(type);
+                    if (!(*lineIt).parameter(QStringLiteral("label")).isEmpty()) {
+                        address.setLabel((*lineIt).parameter(QStringLiteral("label")));
+                    }
+                    addr.insertAddress(address);
+                }
+                // ANNIVERSARY
+                else if (identifier == QLatin1String("anniversary")) {
+                    const QString t = (*lineIt).value().toString();
+                    const QDateTime dt(parseDateTime(t));
+                    addr.insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Anniversary"), dt.date().toString(Qt::ISODate));
+                }
+                // BDAY
+                else if (identifier == QLatin1String("bday")) {
+                    addr.setBirthday(parseDateTime((*lineIt).value().toString()));
+                }
+
+                // CATEGORIES
+                else if (identifier == QLatin1String("categories")) {
+                    const QStringList categories = splitString(commaSep, (*lineIt).value().toString());
+                    addr.setCategories(categories);
+                }
+                // FBURL
+                else if (identifier == QLatin1String("fburl")) {
+                    CalendarUrl calurl;
+                    calurl.setType(CalendarUrl::FBUrl);
+                    const QUrl url = QUrl((*lineIt).value().toString());
+                    calurl.setUrl(url);
+                    calurl.setParameters((*lineIt).parameterMap());
+                    addr.insertCalendarUrl(calurl);
+                }
+                // CALADRURI
+                else if (identifier == QLatin1String("caladruri")) {
+                    CalendarUrl calurl;
+                    calurl.setType(CalendarUrl::CALADRUri);
+                    const QUrl url = QUrl((*lineIt).value().toString());
+                    calurl.setUrl(url);
+                    calurl.setParameters((*lineIt).parameterMap());
+                    addr.insertCalendarUrl(calurl);
+                }
+                // CALURI
+                else if (identifier == QLatin1String("caluri")) {
+                    CalendarUrl calurl;
+                    calurl.setType(CalendarUrl::CALUri);
+                    const QUrl url = QUrl((*lineIt).value().toString());
+                    calurl.setUrl(url);
+                    calurl.setParameters((*lineIt).parameterMap());
+                    addr.insertCalendarUrl(calurl);
+                }
+                //IMPP
+                else if (identifier == QLatin1String("impp")) {
+                    QString imppStr = (*lineIt).value().toString();
+                    Impp impp;
+                    impp.setAddress(imppStr);
+                    impp.setParameters((*lineIt).parameterMap());
+                    if (!(*lineIt).parameter(QStringLiteral("x-service-type")).isEmpty()) {
+                        const QString serviceType = (*lineIt).parameter(QStringLiteral("x-service-type")).toLower();
+                        if (serviceType == QLatin1String("facebook")) {
+                            impp.setType(KContacts::Impp::Facebook);
+                        } else if (serviceType == QLatin1String("jabber")) {
+                            impp.setType(KContacts::Impp::Jabber);
+                        } else if (serviceType == QLatin1String("sip")) {
+                            impp.setType(KContacts::Impp::Sip);
+                        } else if (serviceType == QLatin1String("aim")) {
+                            impp.setType(KContacts::Impp::Aim);
+                        } else if (serviceType == QLatin1String("msn")) {
+                            impp.setType(KContacts::Impp::Msn);
+                        } else if (serviceType == QLatin1String("twitter")) {
+                            impp.setType(KContacts::Impp::Twitter);
+                        } else if (serviceType == QLatin1String("googletalk")) {
+                            impp.setType(KContacts::Impp::GoogleTalk);
+                        } else if (serviceType == QLatin1String("xmpp")) {
+                            impp.setType(KContacts::Impp::Xmpp);
+                        } else if (serviceType == QLatin1String("icq")) {
+                            impp.setType(KContacts::Impp::Icq);
+                        } else if (serviceType == QLatin1String("yahoo")) {
+                            impp.setType(KContacts::Impp::Yahoo);
+                        } else if (serviceType == QLatin1String("qq")) {
+                            impp.setType(KContacts::Impp::Qq);
+                        } else if (serviceType == QLatin1String("gadugadu")) {
+                            impp.setType(KContacts::Impp::GaduGadu);
+                        } else if (serviceType == QLatin1String("owncloud-handle")) {
+                            impp.setType(KContacts::Impp::Ownclound);
+                        } else if (serviceType == QLatin1String("skype")) {
+                            impp.setType(KContacts::Impp::Skype);
+                        } else {
+                            qDebug() << "unknown service type " << serviceType;
+                        }
+                    }
+                    addr.insertImpp(impp);
+                }
+                // CLASS
+                else if (identifier == QLatin1String("class")) {
+                    addr.setSecrecy(parseSecrecy(*lineIt));
+                }
+                // GENDER
+                else if (identifier == QLatin1String("gender")) {
+                    QString genderStr = (*lineIt).value().toString();
+                    if (!genderStr.isEmpty()) {
+                        Gender gender;
+                        if (genderStr.at(0) != QLatin1Char(';')) {
+                            gender.setGender(genderStr.at(0));
+                            if (genderStr.length() > 2 && (genderStr.at(1) == QLatin1Char(';'))) {
+                                gender.setComment(genderStr.right(genderStr.length() - 2));
+                            }
+                        } else {
+                            gender.setComment(genderStr.right(genderStr.length() - 1));
+                        }
+                        addr.setGender(gender);
+                    }
+                }
+                // LANG
+                else if (identifier == QLatin1String("lang")) {
+                    Lang lang;
+                    lang.setLanguage((*lineIt).value().toString());
+                    lang.setParameters((*lineIt).parameterMap());
+                    addr.insertLang(lang);
+                }
+                // EMAIL
+                else if (identifier == QLatin1String("email")) {
+                    const QStringList types = (*lineIt).parameters(QStringLiteral("type"));
+                    addr.insertEmail((*lineIt).value().toString(),
+                                     types.contains(QStringLiteral("PREF")), (*lineIt).parameterMap());
+                }
+                // KIND
+                else if (identifier == QLatin1String("kind")) {
+                    addr.setKind((*lineIt).value().toString());
+                }
+                // FN
+                else if (identifier == QLatin1String("fn")) {
+                    addr.setFormattedName((*lineIt).value().toString());
+                }
+
+                // GEO
+                else if (identifier == QLatin1String("geo")) {
+                    Geo geo;
+                    QString lineStr = (*lineIt).value().toString();
+                    if (lineStr.startsWith(QLatin1String("geo:"))) { //VCard 4.0
+                        lineStr.remove(QStringLiteral("geo:"));
+                        const QStringList geoParts =
+                            lineStr.split(QLatin1Char(','), QString::KeepEmptyParts);
+                        if (geoParts.size() >= 2) {
+                            geo.setLatitude(geoParts.at(0).toFloat());
+                            geo.setLongitude(geoParts.at(1).toFloat());
+                            addr.setGeo(geo);
+                        }
+                    } else {
+                        const QStringList geoParts =
+                            lineStr.split(QLatin1Char(';'), QString::KeepEmptyParts);
+                        if (geoParts.size() >= 2) {
+                            geo.setLatitude(geoParts.at(0).toFloat());
+                            geo.setLongitude(geoParts.at(1).toFloat());
+                            addr.setGeo(geo);
+                        }
+                    }
+                }
+
+                // KEY
+                else if (identifier == QLatin1String("key")) {
+                    addr.insertKey(parseKey(*lineIt));
+                }
+
+                // LABEL
+                else if (identifier == QLatin1String("label")) {
+                    Address::Type type;
+
+                    const QStringList types = (*lineIt).parameters(QStringLiteral("type"));
+                    QStringList::ConstIterator end(types.end());
+                    for (QStringList::ConstIterator it = types.begin(); it != end; ++it) {
+                        type |= mAddressTypeMap[(*it).toLower() ];
+                    }
+
+                    bool available = false;
+                    KContacts::Address::List addressList = addr.addresses();
+                    for (KContacts::Address::List::Iterator it = addressList.begin();
+                            it != addressList.end(); ++it) {
+                        if ((*it).type() == type) {
+                            (*it).setLabel((*lineIt).value().toString());
+                            addr.insertAddress(*it);
+                            available = true;
+                            break;
+                        }
+                    }
+
+                    if (!available) {   // a standalone LABEL tag
+                        KContacts::Address address(type);
+                        address.setLabel((*lineIt).value().toString());
+                        addr.insertAddress(address);
+                    }
+                }
+
+                // LOGO
+                else if (identifier == QLatin1String("logo")) {
+                    Picture picture = parsePicture(*lineIt);
+                    if (addr.logo().isEmpty()) {
+                        addr.setLogo(picture);
+                    } else {
+                        addr.insertExtraLogo(picture);
+                    }
+                }
+
+                // MAILER
+                else if (identifier == QLatin1String("mailer")) {
+                    addr.setMailer((*lineIt).value().toString());
+                }
+
+                // N
+                else if (identifier == QLatin1String("n")) {
+                    const QStringList nameParts = splitString(semicolonSep, (*lineIt).value().toString());
+                    const int numberOfParts(nameParts.count());
+                    if (numberOfParts > 0) {
+                        addr.setFamilyName(nameParts.at(0));
+                    }
+                    if (numberOfParts > 1) {
+                        addr.setGivenName(nameParts.at(1));
+                    }
+                    if (numberOfParts > 2) {
+                        addr.setAdditionalName(nameParts.at(2));
+                    }
+                    if (numberOfParts > 3) {
+                        addr.setPrefix(nameParts.at(3));
+                    }
+                    if (numberOfParts > 4) {
+                        addr.setSuffix(nameParts.at(4));
+                    }
+                    if (!(*lineIt).parameter(QStringLiteral("sort-as")).isEmpty()) {
+                        addr.setSortString((*lineIt).parameter(QStringLiteral("sort-as")));
+                    }
+                }
+
+                // NAME
+                else if (identifier == QLatin1String("name")) {
+                    addr.setName((*lineIt).value().toString());
+                }
+
+                // NICKNAME
+                else if (identifier == QLatin1String("nickname")) {
+                    addr.setNickName((*lineIt).value().toString());
+                }
+
+                // NOTE
+                else if (identifier == QLatin1String("note")) {
+                    addr.setNote((*lineIt).value().toString());
+                }
+
+                // ORGANIZATION
+                else if (identifier == QLatin1String("org")) {
+                    const QStringList orgParts = splitString(semicolonSep, (*lineIt).value().toString());
+                    if (orgParts.count() > 0) {
+                        addr.setOrganization(orgParts.at(0));
+                    }
+                    if (orgParts.count() > 1) {
+                        addr.setDepartment(orgParts.at(1));
+                    }
+                    if (!(*lineIt).parameter(QStringLiteral("sort-as")).isEmpty()) {
+                        addr.setSortString((*lineIt).parameter(QStringLiteral("sort-as")));
+                    }
+                }
+
+                // PHOTO
+                else if (identifier == QLatin1String("photo")) {
+                    Picture picture = parsePicture(*lineIt);
+                    if (addr.photo().isEmpty()) {
+                        addr.setPhoto(picture);
+                    } else {
+                        addr.insertExtraPhoto(picture);
+                    }
+                }
+
+                // PROID
+                else if (identifier == QLatin1String("prodid")) {
+                    addr.setProductId((*lineIt).value().toString());
+                }
+
+                // REV
+                else if (identifier == QLatin1String("rev")) {
+                    addr.setRevision(parseDateTime((*lineIt).value().toString()));
+                }
+
+                // ROLE
+                else if (identifier == QLatin1String("role")) {
+                    addr.setRole((*lineIt).value().toString());
+                }
+
+                // SORT-STRING
+                else if (identifier == QLatin1String("sort-string")) {
+                    addr.setSortString((*lineIt).value().toString());
+                }
+
+                // SOUND
+                else if (identifier == QLatin1String("sound")) {
+                    Sound sound = parseSound(*lineIt);
+                    if (addr.sound().isEmpty()) {
+                        addr.setSound(sound);
+                    } else {
+                        addr.insertExtraSound(sound);
+                    }
+                }
+
+                // TEL
+                else if (identifier == QLatin1String("tel")) {
+                    PhoneNumber phone;
+                    phone.setNumber((*lineIt).value().toString());
+
+                    PhoneNumber::Type type;
+                    bool foundType = false;
+                    const QStringList types = (*lineIt).parameters(QStringLiteral("type"));
+                    QStringList::ConstIterator typeEnd(types.end());
+                    for (QStringList::ConstIterator it = types.begin(); it != typeEnd; ++it) {
+                        type |= mPhoneTypeMap[(*it).toUpper()];
+                        foundType = true;
+                    }
+                    phone.setType(foundType ? type : PhoneNumber::Undefined);
+
+                    addr.insertPhoneNumber(phone);
+                }
+
+                // TITLE
+                else if (identifier == QLatin1String("title")) {
+                    addr.setTitle((*lineIt).value().toString());
+                }
+
+                // TZ
+                else if (identifier == QLatin1String("tz")) {
+                    TimeZone tz;
+                    const QString date = (*lineIt).value().toString();
+
+                    if (!date.isEmpty()) {
+                        int hours = date.midRef(1, 2).toInt();
+                        int minutes = date.midRef(4, 2).toInt();
+                        int offset = (hours * 60) + minutes;
+                        offset = offset * (date[ 0 ] == QLatin1Char('+') ? 1 : -1);
+
+                        tz.setOffset(offset);
+                        addr.setTimeZone(tz);
+                    }
+                }
+
+                // UID
+                else if (identifier == QLatin1String("uid")) {
+                    addr.setUid((*lineIt).value().toString());
+                }
+
+                // URL
+                else if (identifier == QLatin1String("url")) {
+                    const QUrl url = QUrl((*lineIt).value().toString());
+                    ResourceLocatorUrl resourceLocatorUrl;
+                    resourceLocatorUrl.setUrl(url);
+                    resourceLocatorUrl.setParameters((*lineIt).parameterMap());
+                    addr.insertExtraUrl(resourceLocatorUrl);
+                }
+                // SOURCE
+                else if (identifier == QLatin1String("source")) {
+                    const QUrl url = QUrl((*lineIt).value().toString());
+                    addr.insertSourceUrl(url);
+                }
+                // MEMBER (vcard 4.0)
+                else if (identifier == QLatin1String("member")) {
+                    addr.insertMember((*lineIt).value().toString());
+                }
+                // RELATED (vcard 4.0)
+                else if (identifier == QLatin1String("related")) {
+                    Related related;
+                    related.setRelated((*lineIt).value().toString());
+                    related.setParameters((*lineIt).parameterMap());
+                    addr.insertRelationShip(related);
+                }
+                // X-
+                //TODO import X-GENDER
+                else if (identifier.startsWith(QLatin1String("x-"))) {
+                    QString ident = (*lineIt).identifier();
+                    //X-Evolution
+                    if (identifier == QLatin1String("x-evolution-spouse") ||
+                            identifier == QLatin1String("x-spouse")) {
+                        ident = QStringLiteral("X-KADDRESSBOOK-X-SpousesName");
+                    } else if (identifier == QLatin1String("x-evolution-blog-url")) {
+                        ident = QStringLiteral("X-KADDRESSBOOK-BlogFeed");
+                    } else if (identifier == QLatin1String("x-evolution-assistant") ||
+                               identifier == QLatin1String("x-assistant")) {
+                        ident = QStringLiteral("X-KADDRESSBOOK-X-AssistantsName");
+                    } else if (identifier == QLatin1String("x-evolution-anniversary") ||
+                               identifier == QLatin1String("x-anniversary")) {
+                        ident = QStringLiteral("X-KADDRESSBOOK-X-Anniversary");
+                    } else if (identifier == QLatin1String("x-evolution-manager") ||
+                               identifier == QLatin1String("x-manager")) {
+                        ident = QStringLiteral("X-KADDRESSBOOK-X-ManagersName");
+                    } else if (identifier == QLatin1String("x-aim")) {
+                        ident = QStringLiteral("X-messaging/aim-All");
+                    } else if (identifier == QLatin1String("x-icq")) {
+                        ident = QStringLiteral("X-messaging/icq-All");
+                    } else if (identifier == QLatin1String("x-jabber")) {
+                        ident = QStringLiteral("X-messaging/xmpp-All");
+                    } else if (identifier == QLatin1String("x-jabber")) {
+                        ident = QStringLiteral("X-messaging/xmpp-All");
+                    } else if (identifier == QLatin1String("x-msn")) {
+                        ident = QStringLiteral("X-messaging/msn-All");
+                    } else if (identifier == QLatin1String("x-yahoo")) {
+                        ident = QStringLiteral("X-messaging/yahoo-All");
+                    } else if (identifier == QLatin1String("x-gadugadu")) {
+                        ident = QStringLiteral("X-messaging/gadu-All");
+                    } else if (identifier == QLatin1String("x-skype")) {
+                        ident = QStringLiteral("X-messaging/skype-All");
+                    } else if (identifier == QLatin1String("x-groupwise")) {
+                        ident = QStringLiteral("X-messaging/groupwise-All");
+                    } else if (identifier == QLatin1String("x-sms")) {
+                        ident = QStringLiteral("X-messaging/sms-All");
+                    } else if (identifier == QLatin1String("x-meanwhile")) {
+                        ident = QStringLiteral("X-messaging/meanwhile-All");
+                    } else if (identifier == QLatin1String("x-irc")) {
+                        ident = QStringLiteral("X-messaging/irc-All");
+                    } else if (identifier == QLatin1String("x-gtalk")) {
+                        ident = QStringLiteral("X-messaging/googletalk-All");
+                    } else if (identifier == QLatin1String("x-twitter")) {
+                        ident = QStringLiteral("X-messaging/twitter-All");
+                    }
+
+                    const QString key = ident.mid(2);
+                    const int dash = key.indexOf(QLatin1Char('-'));
+                    addr.insertCustom(key.left(dash), key.mid(dash + 1),
+                                      (*lineIt).value().toString());
+                }
+            }
+        }
+
+        addrList.append(addr);
+    }
+
+    return addrList;
+}
+
+QDateTime VCardTool::parseDateTime(const QString &str) const
+{
+    QDate date;
+    QTime time;
+
+    if (str.indexOf(QLatin1Char('-')) == -1) {       // is base format (yyyymmdd)
+        date = QDate(str.leftRef(4).toInt(), str.midRef(4, 2).toInt(),
+                     str.midRef(6, 2).toInt());
+    } else { // is extended format yyyy-mm-dd
+        date = QDate(str.leftRef(4).toInt(), str.midRef(5, 2).toInt(),
+                     str.midRef(8, 2).toInt());
+    }
+
+    // does it also contain a time ? (Note: mm, ss are optional according ISO-8601)
+    int timeStart = str.indexOf(QLatin1Char('T'));
+    if (timeStart >= 0) {
+        int hour = 0, minute = 0, second = 0;
+
+        hour = str.midRef(timeStart + 1, 2).toInt();    // hour must always be given
+
+        if (str.indexOf(QLatin1Char(':'), timeStart + 1) > 0) {        // extended format (hh:mm:ss)
+            if (str.length() >= (timeStart + 5)) {
+                minute = str.midRef(timeStart + 4, 2).toInt();
+                if (str.length() >= (timeStart + 8)) {
+                    second = str.midRef(timeStart + 7, 2).toInt();
+                }
+            }
+        } else {  // basic format (hhmmss)
+            if (str.length() >= (timeStart + 4)) {
+                minute = str.midRef(timeStart + 3, 2).toInt();
+                if (str.length() >= (timeStart + 6)) {
+                    second = str.midRef(timeStart + 5, 2).toInt();
+                }
+            }
+        }
+
+        time = QTime(hour, minute, second);
+    }
+
+    Qt::TimeSpec spec = (str.right(1) == QLatin1String("Z")) ? Qt::UTC : Qt::LocalTime;
+
+    QDateTime dateTime(date);
+
+    if (time.isValid()) {
+        dateTime.setTime(time);
+    }
+
+    dateTime.setTimeSpec(spec);
+    return dateTime;
+}
+
+QString VCardTool::createDateTime(const QDateTime &dateTime, VCard::Version version) const
+{
+    QString str;
+    if (version == VCard::v4_0) {
+        if (dateTime.date().isValid()) {
+            str.sprintf("%4d%02d%02d", dateTime.date().year(), dateTime.date().month(),
+                        dateTime.date().day());
+            if (dateTime.time().isValid()) {
+                QString tmp;
+                tmp.sprintf("T%02d%02d%02d", dateTime.time().hour(), dateTime.time().minute(),
+                            dateTime.time().second());
+                str += tmp;
+
+                if (dateTime.timeSpec() == Qt::UTC) {
+                    str += QLatin1Char('Z');
+                }
+            }
+        }
+
+    } else {
+        if (dateTime.date().isValid()) {
+            str.sprintf("%4d-%02d-%02d", dateTime.date().year(), dateTime.date().month(),
+                        dateTime.date().day());
+            if (dateTime.time().isValid()) {
+                QString tmp;
+                tmp.sprintf("T%02d:%02d:%02d", dateTime.time().hour(), dateTime.time().minute(),
+                            dateTime.time().second());
+                str += tmp;
+
+                if (dateTime.timeSpec() == Qt::UTC) {
+                    str += QLatin1Char('Z');
+                }
+            }
+        }
+    }
+    return str;
+}
+
+Picture VCardTool::parsePicture(const VCardLine &line) const
+{
+    Picture pic;
+
+    const QStringList params = line.parameterList();
+    QString type;
+    if (params.contains(QStringLiteral("type"))) {
+        type = line.parameter(QStringLiteral("type"));
+    }
+    if (params.contains(QStringLiteral("encoding"))) {
+        pic.setRawData(line.value().toByteArray(), type);
+    } else if (params.contains(QStringLiteral("value"))) {
+        if (line.parameter(QStringLiteral("value")).toLower() == QLatin1String("uri")) {
+            pic.setUrl(line.value().toString());
+        }
+    }
+
+    return pic;
+}
+
+VCardLine VCardTool::createPicture(const QString &identifier, const Picture &pic, VCard::Version version) const
+{
+    VCardLine line(identifier);
+
+    if (pic.isEmpty()) {
+        return line;
+    }
+
+    if (pic.isIntern()) {
+        line.setValue(pic.rawData());
+        if (version == VCard::v2_1) {
+            line.addParameter(QStringLiteral("ENCODING"), QStringLiteral("BASE64"));
+            line.addParameter(pic.type(), QString());
+        } else {
+            line.addParameter(QStringLiteral("encoding"), QStringLiteral("b"));
+            line.addParameter(QStringLiteral("type"), pic.type());
+        }
+    } else {
+        line.setValue(pic.url());
+        line.addParameter(QStringLiteral("value"), QStringLiteral("URI"));
+    }
+
+    return line;
+}
+
+Sound VCardTool::parseSound(const VCardLine &line) const
+{
+    Sound snd;
+
+    const QStringList params = line.parameterList();
+    if (params.contains(QStringLiteral("encoding"))) {
+        snd.setData(line.value().toByteArray());
+    } else if (params.contains(QStringLiteral("value"))) {
+        if (line.parameter(QStringLiteral("value")).toLower() == QLatin1String("uri")) {
+            snd.setUrl(line.value().toString());
+        }
+    }
+
+    /* TODO: support sound types
+      if ( params.contains( "type" ) )
+        snd.setType( line.parameter( "type" ) );
+    */
+
+    return snd;
+}
+
+VCardLine VCardTool::createSound(const Sound &snd, VCard::Version version) const
+{
+    Q_UNUSED(version);
+    VCardLine line(QStringLiteral("SOUND"));
+
+    if (snd.isIntern()) {
+        if (!snd.data().isEmpty()) {
+            line.setValue(snd.data());
+            if (version == VCard::v2_1) {
+                line.addParameter(QStringLiteral("ENCODING"), QStringLiteral("BASE64"));
+            } else {
+                line.addParameter(QStringLiteral("encoding"), QStringLiteral("b"));
+            }
+            // TODO: need to store sound type!!!
+        }
+    } else if (!snd.url().isEmpty()) {
+        line.setValue(snd.url());
+        line.addParameter(QStringLiteral("value"), QStringLiteral("URI"));
+    }
+
+    return line;
+}
+
+Key VCardTool::parseKey(const VCardLine &line) const
+{
+    Key key;
+
+    const QStringList params = line.parameterList();
+    if (params.contains(QStringLiteral("encoding"))) {
+        key.setBinaryData(line.value().toByteArray());
+    } else {
+        key.setTextData(line.value().toString());
+    }
+
+    if (params.contains(QStringLiteral("type"))) {
+        if (line.parameter(QStringLiteral("type")).toLower() == QLatin1String("x509")) {
+            key.setType(Key::X509);
+        } else if (line.parameter(QStringLiteral("type")).toLower() == QLatin1String("pgp")) {
+            key.setType(Key::PGP);
+        } else {
+            key.setType(Key::Custom);
+            key.setCustomTypeString(line.parameter(QStringLiteral("type")));
+        }
+    }
+
+    return key;
+}
+
+VCardLine VCardTool::createKey(const Key &key, VCard::Version version) const
+{
+    VCardLine line(QStringLiteral("KEY"));
+
+    if (key.isBinary()) {
+        if (!key.binaryData().isEmpty()) {
+            line.setValue(key.binaryData());
+            if (version == VCard::v2_1) {
+                line.addParameter(QStringLiteral("ENCODING"), QStringLiteral("BASE64"));
+            } else {
+                line.addParameter(QStringLiteral("encoding"), QStringLiteral("b"));
+            }
+        }
+    } else if (!key.textData().isEmpty()) {
+        line.setValue(key.textData());
+    }
+
+    if (key.type() == Key::X509) {
+        line.addParameter(QStringLiteral("type"), QStringLiteral("X509"));
+    } else if (key.type() == Key::PGP) {
+        line.addParameter(QStringLiteral("type"), QStringLiteral("PGP"));
+    } else if (key.type() == Key::Custom) {
+        line.addParameter(QStringLiteral("type"), key.customTypeString());
+    }
+
+    return line;
+}
+
+Secrecy VCardTool::parseSecrecy(const VCardLine &line) const
+{
+    Secrecy secrecy;
+
+    const QString value = line.value().toString().toLower();
+    if (value == QLatin1String("public")) {
+        secrecy.setType(Secrecy::Public);
+    } else if (value == QLatin1String("private")) {
+        secrecy.setType(Secrecy::Private);
+    } else if (value == QLatin1String("confidential")) {
+        secrecy.setType(Secrecy::Confidential);
+    }
+
+    return secrecy;
+}
+
+VCardLine VCardTool::createSecrecy(const Secrecy &secrecy) const
+{
+    VCardLine line(QStringLiteral("CLASS"));
+
+    int type = secrecy.type();
+
+    if (type == Secrecy::Public) {
+        line.setValue(QLatin1String("PUBLIC"));
+    } else if (type == Secrecy::Private) {
+        line.setValue(QLatin1String("PRIVATE"));
+    } else if (type == Secrecy::Confidential) {
+        line.setValue(QLatin1String("CONFIDENTIAL"));
+    }
+
+    return line;
+}
+
+QStringList VCardTool::splitString(QChar sep, const QString &str) const
+{
+    QStringList list;
+    QString value(str);
+
+    int start = 0;
+    int pos = value.indexOf(sep, start);
+
+    while (pos != -1) {
+        if (pos == 0 || value[ pos - 1 ] != QLatin1Char('\\')) {
+            if (pos > start && pos <= (int)value.length()) {
+                list << value.mid(start, pos - start);
+            } else {
+                list << QString();
+            }
+
+            start = pos + 1;
+            pos = value.indexOf(sep, start);
+        } else {
+            value.replace(pos - 1, 2, sep);
+            pos = value.indexOf(sep, pos);
+        }
+    }
+
+    int l = value.length() - 1;
+    const QString mid = value.mid(start, l - start + 1);
+    if (!mid.isEmpty()) {
+        list << mid;
+    } else {
+        list << QString();
+    }
+
+    return list;
+}
diff --git a/src/vcardtool.h b/src/vcardtool.h
new file mode 100644 (file)
index 0000000..816b47c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KCONTACTS_VCARDTOOL_H
+#define KCONTACTS_VCARDTOOL_H
+
+#include "kcontacts_export.h"
+#include "addressee.h"
+#include "vcardparser/vcardparser.h"
+
+class QDateTime;
+
+namespace KContacts
+{
+
+class Key;
+class Picture;
+class Secrecy;
+class Sound;
+
+class KCONTACTS_EXPORT VCardTool
+{
+public:
+    VCardTool();
+    ~VCardTool();
+
+    /**
+      Creates a string that contains the addressees from the list in
+      the vCard format.
+     */
+    QByteArray createVCards(const Addressee::List &list,
+                            VCard::Version version = VCard::v3_0) const;
+
+    /**
+     * @since 4.9.1
+     */
+    QByteArray exportVCards(const Addressee::List &list,
+                            VCard::Version version = VCard::v3_0) const;
+    /**
+      Parses the string and returns a list of addressee objects.
+     */
+    Addressee::List parseVCards(const QByteArray &vcard) const;
+
+private:
+
+    QByteArray createVCards(const Addressee::List &list,
+                            VCard::Version version, bool exportVcard) const;
+
+    /**
+      Split a string and replaces escaped separators on the fly with
+      unescaped ones.
+     */
+    QStringList splitString(QChar sep, const QString &value) const;
+
+    QDateTime parseDateTime(const QString &str) const;
+    QString createDateTime(const QDateTime &dateTime, VCard::Version version) const;
+
+    Picture parsePicture(const VCardLine &line) const;
+    VCardLine createPicture(const QString &identifier, const Picture &pic, VCard::Version version) const;
+
+    Sound parseSound(const VCardLine &line) const;
+    VCardLine createSound(const Sound &snd, VCard::Version version) const;
+
+    Key parseKey(const VCardLine &line) const;
+    VCardLine createKey(const Key &key, VCard::Version version) const;
+
+    Secrecy parseSecrecy(const VCardLine &line) const;
+    VCardLine createSecrecy(const Secrecy &secrecy) const;
+
+    void addParameter(VCardLine &line, VCard::Version version, const QString &key, const QStringList &valueStringList) const;
+
+    QMap<QString, Address::TypeFlag> mAddressTypeMap;
+    QMap<QString, PhoneNumber::TypeFlag> mPhoneTypeMap;
+
+    Q_DISABLE_COPY(VCardTool)
+    class VCardToolPrivate;
+    VCardToolPrivate *d;
+};
+
+}
+
+#endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f774782
--- /dev/null
@@ -0,0 +1,24 @@
+# FIXME: Make it build with this flag
+remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY)
+
+include(ECMMarkAsTest)
+
+########### next target ###############
+
+# FIXME: Build fails
+set(testread_SRCS testread.cpp)
+add_executable(testread ${testread_SRCS})
+target_link_libraries(testread KF5Contacts KF5::I18n Qt5::Gui)
+
+########### next target ###############
+
+# FIXME: Build fails
+set(testwrite_SRCS testwrite.cpp)
+add_executable(testwrite ${testwrite_SRCS})
+target_link_libraries(testwrite KF5Contacts Qt5::Gui KF5::I18n)
+
+########### next target ###############
+
+set(testread2_SRCS testread2.cpp testutils.cpp)
+add_executable(testread2 ${testread2_SRCS})
+target_link_libraries(testread2 KF5Contacts Qt5::Gui)
diff --git a/tests/README.testing b/tests/README.testing
new file mode 100644 (file)
index 0000000..1a70685
--- /dev/null
@@ -0,0 +1,12 @@
+For testing the vcardparser there are some test files and a small testsuite
+automatically checking for regressions. The tests directory contains some vCard
+files and correpsonding reference output files (with an additional ".ref"
+suffix). For running the geression test do "./testroundtrip" or simply run "ctest".
+
+For creating a new test put a vCard file to be parsed into the tests directory.
+Create a reference file by running "testread" on the test file. It will put out
+the parsed data as vCard again on stdout. Carefully check the output, manually
+correct any errors and save the result as reference file in the tests directory.
+Now add the filename to the testroundtrip.qrc resource file and run
+"make && ./roundtriptest" If the check fails adapt the reference file or fix the bugs
+in the parser, whatever is appropriate.
diff --git a/tests/checkvcard.pl b/tests/checkvcard.pl
new file mode 100755 (executable)
index 0000000..5965c89
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+
+if ( @ARGV != 1 ) {
+  print STDERR "Missing arg: filename\n";
+  system "touch FAILED";
+  exit 1;
+}
+
+$file = $ARGV[0];
+
+if ( !open( IN, "$file" ) ) {
+  print STDERR "Unable to open '$file'\n";
+  system "touch FAILED";
+  exit 1;
+}
+
+while( <IN> ) {
+  if (/^VERSION:(.*)$/ ) {
+    $version = $1;
+    if ( $version eq "2.1" ) { $options = "--vcard21"; }
+  }
+}
+
+close IN;
+
+$ref = "$file.ref";
+
+if ( !open( REF, "$ref" ) ) {
+  print STDERR "Unable to open $ref\n";
+  system "touch FAILED";
+  exit 1;
+}
+
+while( <REF> ) {
+  push @ref, $_;
+}
+
+close REF;
+
+if ( !open( READ, "./testread $file $options 2> /dev/null |" ) ) {
+  print STDERR "Unable to open testread\n";
+  system "touch FAILED";
+  exit 1;
+}
+
+print "Checking '$file':\n";
+
+$gotsomething = 0;
+$error = 0;
+$i = 0;
+while( <READ> ) {
+  $gotsomething = 1;
+  $out = $_;
+  $ref = @ref[$i++];
+
+  if ( $out ne $ref ) {
+    if ( $ref =~ /^UID/ && $out =~ /^UID/ ) { next; }
+    $error++;
+    print "  Expected      : $ref";
+    print "  Parser output : $out";
+  }
+}
+
+close READ;
+
+if ( $gotsomething == 0 ) {
+  print "\n  FAILED: testread didn't output anything\n";
+  system "touch FAILED";
+  exit 1;
+}
+if ( $error > 0 ) {
+  print "\n  FAILED: $error errors found.\n";
+  system "touch FAILED";
+  exit 1;
+} else {
+  print "  OK\n";
+}
+
+exit 0;
diff --git a/tests/testread.cpp b/tests/testread.cpp
new file mode 100644 (file)
index 0000000..3856282
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include <iostream>
+#include <stdlib.h>
+
+#include <QtCore/QFile>
+
+#include <qdebug.h>
+#include <KAboutData>
+#include <QCoreApplication>
+#include <klocalizedstring.h>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+
+#include "converter/kcontacts/vcardconverter.h"
+#include "vcard.h"
+
+int main(int argc, char **argv)
+{
+    KAboutData aboutData(QStringLiteral("testread"), i18n("vCard test reader"), QStringLiteral("0.1"));
+    aboutData.addAuthor(i18n("Cornelius Schumacher"), QString(), QStringLiteral("schumacher@kde.org"));
+
+    QCoreApplication app(argc, argv);
+    QCommandLineParser parser;
+    parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("vcard21"), i18n("vCard 2.1")));
+    parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("+inputfile"), i18n("Input file")));
+
+    KAboutData::setApplicationData(aboutData);
+    parser.addVersionOption();
+    parser.addHelpOption();
+    aboutData.setupCommandLine(&parser);
+    parser.process(app);
+    aboutData.processCommandLine(&parser);
+
+    if (parser.positionalArguments().count() != 1) {
+        std::cerr << "Missing argument" << std::endl;
+        return 1;
+    }
+
+    QString inputFile(parser.positionalArguments().at(0));
+
+    QFile file(inputFile);
+    if (!file.open(QIODevice::ReadOnly)) {
+        qDebug("Unable to open file '%s' for reading!", qPrintable(file.fileName()));
+        return 1;
+    }
+
+    QByteArray text = file.readAll();
+    file.close();
+
+    KContacts::VCardConverter converter;
+    KContacts::Addressee::List list = converter.parseVCards(text);
+
+    if (parser.isSet(QStringLiteral("vcard21"))) {
+        text = converter.createVCards(list, KContacts::VCardConverter::v2_1);   // uses version 2.1
+    } else {
+        text = converter.createVCards(list);   // uses version 3.0
+    }
+
+    std::cout << text.data();
+
+    return 0;
+}
diff --git a/tests/testread2.cpp b/tests/testread2.cpp
new file mode 100644 (file)
index 0000000..37c2f75
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include "testutils.h"
+#include <kcontacts/addressee.h>
+#include <converter/kcontacts/vcardconverter.h>
+#include <qdebug.h>
+
+using namespace KContacts;
+
+int main()
+{
+    Addressee::List l = vCardsAsAddresseeList();
+    QByteArray vcards = vCardsAsText();
+
+    VCardConverter vct;
+
+    Addressee::List parsed = vct.parseVCards(vcards);
+
+    if (l.size() != parsed.size()) {
+        qDebug() << "\tSize - FAILED :" << l.size() << "vs. parsed" << parsed.size();
+    } else {
+        qDebug() << "\tSize - PASSED";
+    }
+
+    Addressee::List::iterator itr1;
+    Addressee::List::iterator itr2;
+    for (itr1 = l.begin(), itr2 = parsed.begin();
+         itr1 != l.end() && itr2 != parsed.end(); ++itr1, ++itr2) {
+        if ((*itr1).fullEmail() == (*itr2).fullEmail() &&
+            (*itr1).organization() == (*itr2).organization() &&
+            (*itr1).phoneNumbers() == (*itr2).phoneNumbers()  &&
+            (*itr1).emails() == (*itr2).emails() &&
+            (*itr1).role() == (*itr2).role()) {
+            qDebug() << "\tAddressee  - PASSED";
+            qDebug() << "\t\t" << (*itr1).fullEmail() << "VS." << (*itr2).fullEmail();
+        } else {
+            qDebug() << "\tAddressee  - FAILED";
+            qDebug() << (*itr1).toString();
+            qDebug() << (*itr2).toString();
+            //qDebug()<<"\t\t"<< (*itr1).fullEmail() << "VS." << (*itr2).fullEmail();
+        }
+    }
+}
diff --git a/tests/testutils.cpp b/tests/testutils.cpp
new file mode 100644 (file)
index 0000000..0acb44c
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include <QtCore/QFile>
+
+#include <kcontacts/addressee.h>
+
+#include "vcardparser.h"
+
+using namespace KContacts;
+
+Addressee vcard1()
+{
+    Addressee addr;
+
+    addr.setName(QStringLiteral("Frank Dawson"));
+    addr.setOrganization(QStringLiteral("Lotus Development Corporation"));
+    KContacts::ResourceLocatorUrl url;
+    url.setUrl(QUrl(QStringLiteral("http://home.earthlink.net/~fdawson")));
+    addr.setUrl(url);
+    addr.insertEmail(QStringLiteral("fdawson@earthlink.net"));
+    addr.insertEmail(QStringLiteral("Frank_Dawson@Lotus.com"), true);
+    addr.insertPhoneNumber(PhoneNumber(QStringLiteral("+1-919-676-9515"),
+                                       PhoneNumber::Voice | PhoneNumber::Msg | PhoneNumber::Work));
+    addr.insertPhoneNumber(PhoneNumber(QStringLiteral("+1-919-676-9564"),
+                                       PhoneNumber::Fax | PhoneNumber::Work));
+    Address a(Address::Work | Address::Postal | Address::Parcel);
+    a.setStreet(QStringLiteral("6544 Battleford Drive"));
+    a.setLocality(QStringLiteral("Raleigh"));
+    a.setRegion(QStringLiteral("NC"));
+    a.setPostalCode(QStringLiteral("27613-3502"));
+    a.setCountry(QStringLiteral("U.S.A."));
+    addr.insertAddress(a);
+    return addr;
+}
+
+Addressee vcard2()
+{
+    Addressee addr;
+
+    addr.setName(QStringLiteral("Tim Howes"));
+    addr.setOrganization(QStringLiteral("Netscape Communications Corp."));
+    addr.insertEmail(QStringLiteral("howes@netscape.com"));
+    addr.insertPhoneNumber(PhoneNumber(QStringLiteral("+1-415-937-3419"),
+                                       PhoneNumber::Voice | PhoneNumber::Msg | PhoneNumber::Work));
+    addr.insertPhoneNumber(PhoneNumber(QStringLiteral("+1-415-528-4164"),
+                                       PhoneNumber::Fax | PhoneNumber::Work));
+    Address a(Address::Work);
+    a.setStreet(QStringLiteral("501 E. Middlefield Rd."));
+    a.setLocality(QStringLiteral("Mountain View"));
+    a.setRegion(QStringLiteral("CA"));
+    a.setPostalCode(QStringLiteral("94043"));
+    a.setCountry(QStringLiteral("U.S.A."));
+    addr.insertAddress(a);
+    return addr;
+}
+
+Addressee vcard3()
+{
+    Addressee addr;
+
+    addr.setName(QStringLiteral("ian geiser"));
+    addr.setOrganization(QStringLiteral("Source eXtreme"));
+    addr.insertEmail(QStringLiteral("geiseri@yahoo.com"));
+    addr.setTitle(QStringLiteral("VP of Engineering"));
+    return addr;
+}
+
+QByteArray vcardAsText(const QString &location)
+{
+    QByteArray text;
+
+    QFile file(location);
+    if (file.open(QIODevice::ReadOnly)) {
+        text = file.readAll();
+        file.close();
+    }
+
+    return text;
+}
+
+Addressee::List vCardsAsAddresseeList()
+{
+    Addressee::List l;
+
+    l.append(vcard1());
+    l.append(vcard2());
+    l.append(vcard3());
+
+    return l;
+}
+
+QByteArray vCardsAsText()
+{
+    QByteArray vcards = vcardAsText(QStringLiteral("tests/vcard1.vcf"));
+    vcards += vcardAsText(QStringLiteral("tests/vcard2.vcf"));
+    vcards += vcardAsText(QStringLiteral("tests/vcard3.vcf"));
+
+    return vcards;
+}
diff --git a/tests/testutils.h b/tests/testutils.h
new file mode 100644 (file)
index 0000000..d299563
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef TESTUTILS_H
+#define TESTUTILS_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+
+#include <kcontacts/addressee.h>
+
+KContacts::Addressee vcard1();
+KContacts::Addressee vcard2();
+KContacts::Addressee vcard3();
+KContacts::Addressee::List vCardsAsAddresseeList();
+QByteArray vCardAsText(const QString &location);
+QByteArray vCardsAsText();
+
+#endif
diff --git a/tests/testwrite.cpp b/tests/testwrite.cpp
new file mode 100644 (file)
index 0000000..4983682
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+    This file is part of the KContacts framework.
+    Copyright (c) 2007 KDE-PIM team <kde-pim@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#include <QtCore/QFile>
+
+#include <kaboutdata.h>
+#include <klocalizedstring.h>
+
+#include <QCoreApplication>
+#include <KAboutData>
+#include <QCommandLineParser>
+
+#include "kcontacts/addressee.h"
+#include "kcontacts/phonenumber.h"
+#include "kcontacts/address.h"
+#include "kcontacts/key.h"
+#include "kcontacts/picture.h"
+#include "kcontacts/sound.h"
+#include "kcontacts/secrecy.h"
+#include "converter/kcontacts/vcardconverter.h"
+
+int main(int argc, char **argv)
+{
+    KAboutData aboutData(QLatin1String("testwrite"), i18n("vCard test writer"), QLatin1String("0.1"));
+
+    QCoreApplication app(argc, argv);
+    QCommandLineParser parser;
+    KAboutData::setApplicationData(aboutData);
+    parser.addVersionOption();
+    parser.addHelpOption();
+    aboutData.setupCommandLine(&parser);
+    parser.process(app);
+    aboutData.processCommandLine(&parser);
+
+    KContacts::Addressee addressee;
+
+    addressee.setNameFromString(QLatin1String("Mr. Tobias Koenig Jr."));
+    addressee.setNickName(QLatin1String("tokoe"));
+    addressee.setBirthday(QDateTime(QDate(1982, 7, 19)));
+    addressee.setMailer(QLatin1String("mutt1.2"));
+    addressee.setTimeZone(KContacts::TimeZone(+2));
+
+    KContacts::Geo geo;
+    geo.setLatitude(30);
+    geo.setLongitude(51);
+    addressee.setGeo(geo);
+
+    addressee.setTitle(QLatin1String("nerd"));
+    addressee.setRole(QLatin1String("Maintainer"));
+    addressee.setOrganization(QLatin1String("KDE"));
+    addressee.setNote(QLatin1String("nerver\ntouch a running system"));
+    addressee.setProductId(QLatin1String("testId"));
+    addressee.setRevision(QDateTime::currentDateTime());
+    addressee.setSortString(QLatin1String("koenig"));
+    KContacts::ResourceLocatorUrl url;
+    url.setUrl(QUrl(QLatin1String("http://wgess16.dyndns.org")));
+    addressee.setUrl(url);
+    addressee.setSecrecy(KContacts::Secrecy(KContacts::Secrecy::Confidential));
+
+    addressee.insertEmail(QLatin1String("tokoe@kde.org"), true);
+    addressee.insertEmail(QLatin1String("tokoe82@yahoo.de"), true);
+
+    KContacts::PhoneNumber phone1(QLatin1String("3541523475"),
+                             KContacts::PhoneNumber::Pref | KContacts::PhoneNumber::Home);
+    KContacts::PhoneNumber phone2(QLatin1String("+46745673475"),
+                             KContacts::PhoneNumber::Work);
+    addressee.insertPhoneNumber(phone1);
+    addressee.insertPhoneNumber(phone2);
+
+    KContacts::Key key(QLatin1String("secret key"), KContacts::Key::X509);
+    addressee.insertKey(key);
+
+    QStringList categories;
+    categories << QLatin1String("Friends") << QLatin1String("School") << QLatin1String("KDE");
+    addressee.setCategories(categories);
+
+    KContacts::Address a(KContacts::Address::Work | KContacts::Address::Postal | KContacts::Address::Parcel);
+    a.setStreet(QLatin1String("6544 Battleford Drive"));
+    a.setLocality(QLatin1String("Raleigh"));
+    a.setRegion(QLatin1String("NC"));
+    a.setPostalCode(QLatin1String("27613-3502"));
+    a.setCountry(QLatin1String("U.S.A."));
+    addressee.insertAddress(a);
+
+    addressee.insertCustom(QLatin1String("1hsdf"), QLatin1String("ertuer"),
+                           QLatin1String("iurt"));
+    addressee.insertCustom(QLatin1String("2hsdf"), QLatin1String("ertuer"),
+                           QLatin1String("iurt"));
+    addressee.insertCustom(QLatin1String("3hsdf"), QLatin1String("ertuer"),
+                           QLatin1String("iurt"));
+
+    KContacts::Addressee::List list;
+    for (int i = 0; i < 1000; ++i) {
+        KContacts::Addressee addr = addressee;
+        addr.setUid(QString::number(i));
+        list.append(addr);
+    }
+
+    KContacts::VCardConverter converter;
+    QByteArray txt = converter.createVCards(list);
+
+    QFile file(QLatin1String("out.vcf"));
+    if (!file.open(QIODevice::WriteOnly)) {
+        qDebug("Can't open file '%s' fro writing", qPrintable(file.fileName()));
+        return 1;
+    }
+
+    file.write(txt);
+    file.close();
+
+    return 0;
+}