pax_global_header00006660000000000000000000000064144035751220014515gustar00rootroot0000000000000052 comment=62e0f8978369f20d8cf0b9b18919a1ac8f084292 contactsd-1.4.14/000077500000000000000000000000001440357512200135665ustar00rootroot00000000000000contactsd-1.4.14/.gitignore000066400000000000000000000024221440357512200155560ustar00rootroot00000000000000.qmake.cache *.[ao] *.gcda *.gcno *.gcov *.prl *.pro.user* *.so *.so.* *.moc build-stamp* moc_*.cpp src/contactsd Makefile Makefile.* contactsd-1.0.pc core .project .cproject build* configure-stamp debian/*.debhelper* debian/*.substvars debian/files debian/tmp/ tests/dbus-1/session.conf .*.sw? *~ debian/contactsd-dbg debian/contactsd-dev debian/contactsd-plugin-birthday debian/contactsd-tests debian/contactsd plugins/telepathy/buddymanagementadaptor.cpp plugins/telepathy/buddymanagementadaptor.h tests/tests.xml tests/mktests.sh tests/ut_birthdayplugin/check-ut_birthdayplugin-wrapper.sh tests/ut_birthdayplugin/check-with-daemon.sh tests/ut_birthdayplugin/ut_birthdayplugin tests/ut_birthdayplugin/ut_birthdayplugin-wrapper.sh tests/ut_birthdayplugin/with-daemon.sh tests/ut_contactsd/ut_contactsd tests/ut_telepathyplugin/buddymanagementinterface.cpp tests/ut_telepathyplugin/buddymanagementinterface.h tests/ut_telepathyplugin/com.nokia.contacts.buddymanagement.xml tests/ut_telepathyplugin/check-ut_telepathyplugin-wrapper.sh tests/ut_telepathyplugin/check-with-daemon.sh tests/ut_telepathyplugin/ut_telepathyplugin tests/ut_telepathyplugin/ut_telepathyplugin-wrapper.sh tests/ut_telepathyplugin/with-daemon.sh src/contactsimportprogressadaptor.cpp src/contactsimportprogressadaptor.h contactsd-1.4.14/DEBUG_INFO000066400000000000000000000001661440357512200151550ustar00rootroot00000000000000For additional logging run contactsd --log-console. The --enable-debug and the --plugins option also might be useful. contactsd-1.4.14/EXPECTFAIL000066400000000000000000000005301440357512200151330ustar00rootroot00000000000000ut_birthdayplugin::testAddAndRemoveBirthday: abustany: Plugin is not included in images yet ut_birthdayplugin::testChangeBirthday: abustany: Plugin is not included in images yet ut_birthdayplugin::testChangeName: abustany: Plugin is not included in images yet ut_birthdayplugin::testLocaleChange: abustany: Plugin is not included in images yet contactsd-1.4.14/INSTALL000066400000000000000000000021301440357512200146130ustar00rootroot00000000000000Contents: - Basic installation - Running - Generating debian packages =============================================================================== Basic installation =============================================================================== The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any unit-tests that come with the package. 4. Type `make install' to install the program, plugins, and any data files. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. =============================================================================== Running =============================================================================== After build a binary "contactsd" will be available inside the src/ directory located inside the build directory. contactsd-1.4.14/LGPL_EXCEPTION.txt000066400000000000000000000022431440357512200165040ustar00rootroot00000000000000Nokia Qt LGPL Exception version 1.1 As an additional permission to the GNU Lesser General Public License version 2.1, the object code form of a "work that uses the Library" may incorporate material from a header file that is part of the Library. You may distribute such object code under terms of your choice, provided that: (i) the header files of the Library have not been modified; and (ii) the incorporated material is limited to numerical parameters, data structure layouts, accessors, macros, inline functions and templates; and (iii) you comply with the terms of Section 6 of the GNU Lesser General Public License version 2.1. Moreover, you may apply this exception to a modified version of the Library, provided that such modification does not involve copying material from the Library into the modified Library's header files unless such material is limited to (i) numerical parameters; (ii) data structure layouts; (iii) accessors; and (iv) small macros, templates and inline functions of five lines or less in length. Furthermore, you are not required to apply this additional permission to a modified version of the Library. contactsd-1.4.14/LICENSE.LGPL000066400000000000000000000635041440357512200153400ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, 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. 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. 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. 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. 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. 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. 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. 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. 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 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. Copyright (C) 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 Street, 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. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! contactsd-1.4.14/configure000077500000000000000000000105441440357512200155010ustar00rootroot00000000000000#!/bin/sh # # Configures to build the project # #------------------------------------------------------------------------------- # script initialization #------------------------------------------------------------------------------- # Making releases: # set the new version number: # odd minor -> development series # even minor -> stable series # increment micro for each release within a series # set nano_version to 0 # update debian/changelog, use dch # make the release, tag it # set nano_version to 1 if [ -z $VERSION ]; then MAJOR_VERSION=1 MINOR_VERSION=1 MICRO_VERSION=1 NANO_VERSION= if [ -z $NANO_VERSION ] || [ $NANO_VERSION -eq "0" ]; then VERSION="$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION" else VERSION="$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION.$NANO_VERSION" fi fi # Qt Version QMAKE=`which qmake 2>/dev/null` if [ -z "$QMAKE" ] ; then QMAKE=`which qmake-qt4 2>/dev/null` fi if [ -z "$QMAKE" ] ; then echo echo "You need qmake in your PATH to build contactsd" echo "Cannot proceed." exit 1 fi export QMAKE QT_VERSION=`$QMAKE -query QT_VERSION` if [ -z "$QT_VERSION" ] ; then echo echo "Cannot proceed without QT_VERSION determined." exit 1 fi relpath=`dirname $0` relpath=`(cd "$relpath"; /bin/pwd)` outpath=`/bin/pwd` PREFIX=/usr LOCALSTATEDIR=/var TOP_SOURCEDIR=$relpath TOP_BUILDDIR=$outpath while [ "$#" -gt 0 ]; do case "$1" in -h|--help) echo "Usage: ./configure [OPTION]..." echo echo "Configuration:" echo " -h, --help display this help and exit" echo echo "Installation directories:" echo " --prefix PREFIX install everything relative to PREFIX" echo " [/usr]" echo echo "Fine tuning of the installation directories:" echo " --bindir DIR user executables [PREFIX/bin]" echo " --libdir DIR object code libraries [PREFIX/lib]" echo " --includedir DIR C header files [PREFIX/include]" echo " --localstatedir DIR modifiable single-machine data [/var]" echo exit ;; --prefix) shift PREFIX=$1 ;; --bindir) shift BINDIR=$1 ;; --libdir) shift LIBDIR=$1 ;; --includedir) shift INCLUDEDIR=$1 ;; --localstatedir) shift LOCALSTATEDIR=$1 ;; --enable-coverage) ENABLE_COVERAGE=coverage ;; --disable-coverage) ENABLE_COVERAGE=nocoverage ;; *) echo >&2 "configure: error: unrecognized option: '$1'" echo >&2 "Try './configure --help' for more information." exit ;; esac shift done [ -z $BINDIR ] && BINDIR=$PREFIX/bin [ -z $LIBDIR ] && LIBDIR=$PREFIX/lib [ -z $INCLUDEDIR ] && INCLUDEDIR=$PREFIX/include #coverage options if [ -z "$ENABLE_COVERAGE" ] then echo "Coverage not enabled" ENABLE_COVERAGE=nocoverage fi # save configuration into .qmake.cache CACHEFILE="$outpath/.qmake.cache" [ -f "$CACHEFILE" ] && rm -f "$CACHEFILE" cat >> "$CACHEFILE" << EOF CONFIGURED = \$\$quote(yes) VERSION = \$\$quote($VERSION) PREFIX = \$\$quote($PREFIX) BINDIR = \$\$quote($BINDIR) LIBDIR = \$\$quote($LIBDIR) INCLUDEDIR = \$\$quote($INCLUDEDIR) LOCALSTATEDIR = \$\$quote($LOCALSTATEDIR) TOP_SOURCEDIR = \$\$quote($TOP_SOURCEDIR) TOP_BUILDDIR = \$\$quote($TOP_BUILDDIR) CONFIG += \$\$quote($ENABLE_COVERAGE) EOF infiles="contactsd-1.0.pc tests/mktests.sh" for infile in $infiles; do echo "Generating $infile" # pre-process .in files mkdir -p `dirname $infile` cat $TOP_SOURCEDIR/$infile.in | sed -e "s,@VERSION@,${VERSION},g" \ -e "s,@PREFIX@,${PREFIX},g" \ -e "s,@BINDIR@,${BINDIR},g" \ -e "s,@LIBDIR@,${LIBDIR},g" \ -e "s,@INCLUDEDIR@,${INCLUDEDIR},g" \ -e "s,@LOCALSTATEDIR@,${LOCALSTATEDIR},g" \ -e "s,@TOP_SOURCEDIR@,${TOP_SOURCEDIR},g" \ -e "s,@TOP_BUILDDIR@,${TOP_BUILDDIR},g" \ > $TOP_BUILDDIR/$infile done echo echo "contactsd is now configured for building. Now run 'qmake' and then 'make'." echo "Once everything is built, you must run 'make install'." echo "contactsd will be installed into $PREFIX" echo echo "To reconfigure, run 'make confclean' and 'configure'." contactsd-1.4.14/contactsd-1.0.pc.in000066400000000000000000000005371440357512200170020ustar00rootroot00000000000000prefix=@PREFIX@ exec_prefix=${prefix} libdir=${exec_prefix}/lib plugindir=${prefix}/lib/contactsd-1.0/plugins includedir=${prefix}/include moc_includedir=${includedir}/contactsd-1.0 Name: contactsd Description: Contact synchronization daemon for meego Version: @VERSION@ Cflags: -I${includedir}/contactsd-1.0 Libs: -L${libdir} -module -avoid-version contactsd-1.4.14/contactsd.conf000066400000000000000000000005371440357512200164240ustar00rootroot00000000000000 nokia Contacts Daemon permanent-backup-files $HOME/.cache/telepathy/avatars contactsd-1.4.14/contactsd.pro000066400000000000000000000016341440357512200162760ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += lib src plugins tests translations src.depends = lib plugins.depends = src tests.depends = src PACKAGENAME=contactsd PKGCONFIG_FILE=$${PACKAGENAME}-1.0.pc pkgconfig.path=$$LIBDIR/pkgconfig pkgconfig.files=$${PKGCONFIG_FILE} backupconf.path=$$PREFIX/share/backup-framework/applications/ backupconf.files=$${PACKAGENAME}.conf systemdservice.path=/usr/lib/systemd/user/ systemdservice.files=$${PACKAGENAME}.service INSTALLS += pkgconfig backupconf systemdservice check.target = check check.CONFIG = recursive QMAKE_EXTRA_TARGETS += check confclean.depends += distclean confclean.commands += \ $(DEL_FILE) $$TOP_BUILDDIR/.qmake.cache \ $(DEL_FILE) $$TOP_BUILDDIR/$${PKGCONFIG_FILE} QMAKE_EXTRA_TARGETS += confclean OTHER_FILES += \ configure \ rpm/* # Run configure script when building the project from tools like QtCreator isEmpty(CONFIGURED):system('$$PWD/configure') contactsd-1.4.14/contactsd.service000066400000000000000000000004161440357512200171330ustar00rootroot00000000000000[Unit] Description=Contacts daemon After=user-session.target booster-qt5.service Requires=dbus.socket booster-qt5.service [Service] ExecStart=/usr/bin/invoker -o -s --global-syms --type=qt5 /usr/bin/contactsd Restart=always [Install] WantedBy=post-user-session.target contactsd-1.4.14/lib.pri000066400000000000000000000000651440357512200150510ustar00rootroot00000000000000INCLUDEPATH=$$PWD/lib LIBS+=-L$$PWD/lib/ -lcontactsd contactsd-1.4.14/lib/000077500000000000000000000000001440357512200143345ustar00rootroot00000000000000contactsd-1.4.14/lib/Debug000066400000000000000000000021561440357512200153110ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CONTACTSD_DEBUG #define CONTACTSD_DEBUG #include #endif contactsd-1.4.14/lib/base-plugin.cpp000066400000000000000000000035651440357512200172570ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "base-plugin.h" #include "debug.h" #include namespace Contactsd { const QString BasePlugin::metaDataKeyVersion = QString::fromLatin1("version"); const QString BasePlugin::metaDataKeyName = QString::fromLatin1("name"); const QString BasePlugin::metaDataKeyComment = QString::fromLatin1("comment"); QDir BasePlugin::cacheDir() { const QString cacheRoot = QString::fromLatin1(".local/share/system/privileged/Contacts"); QDir cacheDir = QDir::home().filePath(cacheRoot); if (not cacheDir.exists()) { if (not cacheDir.mkpath(QString::fromLatin1("."))) { qCWarning(lcContactsd) << "Could not create cache dir"; return QDir(); } } return cacheDir; } QString BasePlugin::cacheFileName(const QString &fileName) { return cacheDir().filePath(fileName); } } // Contactsd contactsd-1.4.14/lib/base-plugin.h000066400000000000000000000043731440357512200167220ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CONTACTSD_BASE_PLUGIN_H #define CONTACTSD_BASE_PLUGIN_H #include #include #include #include #include #include #include namespace Contactsd { class Q_DECL_EXPORT BasePlugin : public QObject { Q_OBJECT public: static const QString metaDataKeyVersion; static const QString metaDataKeyName; static const QString metaDataKeyComment; typedef QMap MetaData; virtual ~BasePlugin () {} virtual void init() = 0; virtual MetaData metaData() = 0; static QDir cacheDir(); static QString cacheFileName(const QString &fileName); Q_SIGNALS: // \param service - display name of a service (e.g. Gtalk, MSN) // \param account - account id or account path that can uniquely identify an account void importStarted(const QString &service, const QString &account); void importEnded(const QString &service, const QString &account, int contactsAdded, int contactsRemoved, int contactsMerged); void error(int code, const QString &message); // Emitted to inform that import timeout should be extended void importAlive(); }; } // Contactsd #endif // CONTACTSD_PLUGININTERFACE_H contactsd-1.4.14/lib/debug.cpp000066400000000000000000000001301440357512200161200ustar00rootroot00000000000000#include Q_LOGGING_CATEGORY(lcContactsd, "contactsd", QtWarningMsg) contactsd-1.4.14/lib/debug.h000066400000000000000000000017311440357512200155750ustar00rootroot00000000000000/* * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * 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 */ #ifndef CONTACTSD_DEBUG_H #define CONTACTSD_DEBUG_H #include Q_DECLARE_LOGGING_CATEGORY(lcContactsd) #endif contactsd-1.4.14/lib/lib.pro000066400000000000000000000006561440357512200156330ustar00rootroot00000000000000TEMPLATE = lib QT -= gui QT += dbus TARGET = contactsd TARGET = $$qtLibraryTarget($$TARGET) TARGETPATH = $$[QT_INSTALL_LIBS] target.path = $$TARGETPATH INSTALLS += target VERSIONED_TARGET = $$TARGET-1.0 HEADERS += \ debug.h \ base-plugin.h SOURCES += \ debug.cpp \ base-plugin.cpp headers.files = \ BasePlugin base-plugin.h \ Debug debug.h headers.path = $$INCLUDEDIR/$${VERSIONED_TARGET}/Contactsd contactsd-1.4.14/plugins/000077500000000000000000000000001440357512200152475ustar00rootroot00000000000000contactsd-1.4.14/plugins/birthday/000077500000000000000000000000001440357512200170555ustar00rootroot00000000000000contactsd-1.4.14/plugins/birthday/birthday.pro000066400000000000000000000031601440357512200214050ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../../lib.pri) TEMPLATE = lib QT -= gui CONFIG += plugin CONFIG += link_pkgconfig PKGCONFIG += mlocale5 libmkcal-qt5 KF5CalendarCore PKGCONFIG += Qt5Contacts contactcache-qt5 qtcontacts-sqlite-qt5-extensions CONFIG(coverage):{ QMAKE_CXXFLAGS += -c -g --coverage -ftest-coverage -fprofile-arcs LIBS += -lgcov } DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII HEADERS = cdbirthdaycalendar.h \ cdbirthdaycontroller.h \ cdbirthdayplugin.h SOURCES = cdbirthdaycalendar.cpp \ cdbirthdaycontroller.cpp \ cdbirthdayplugin.cpp TARGET = birthdayplugin target.path = $$LIBDIR/contactsd-1.0/plugins INSTALLS += target contactsd-1.4.14/plugins/birthday/cdbirthdaycalendar.cpp000066400000000000000000000311501440357512200233700ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include #include #include #include #include #include "cdbirthdaycalendar.h" #include "debug.h" using namespace ML10N; // A random ID. const QLatin1String calNotebookId("b1376da7-5555-1111-2222-227549c4e570"); const QLatin1String calNotebookColor("#e00080"); // Pink const QString calIdExtension = QLatin1String("com.nokia.birthday/"); CDBirthdayCalendar::CDBirthdayCalendar(SyncMode syncMode, QObject *parent) : QObject(parent), mCalendar(0), mStorage(0) { mCalendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mStorage = mKCal::ExtendedCalendar::defaultStorage(mCalendar); MLocale * const locale = new MLocale(this); if (not locale->isInstalledTrCatalog(QLatin1String("calendar"))) { locale->installTrCatalog(QLatin1String("calendar")); } locale->connectSettings(); connect(locale, SIGNAL(settingsChanged()), this, SLOT(onLocaleChanged())); MLocale::setDefault(*locale); mStorage->open(); mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId); if (notebook.isNull()) { notebook = createNotebook(); mStorage->addNotebook(notebook); } else { // Clear the calendar database if and only if restoring from a backup. switch(syncMode) { case KeepOldDB: // Force calendar name update, if a locale change happened while contactsd was not running. onLocaleChanged(); break; case DropOldDB: mStorage->deleteNotebook(notebook); notebook = createNotebook(); mStorage->addNotebook(notebook); break; } } } CDBirthdayCalendar::~CDBirthdayCalendar() { if (mStorage) { mStorage->close(); } qCDebug(lcContactsd) << "Destroyed birthday calendar"; } mKCal::Notebook::Ptr CDBirthdayCalendar::createNotebook() { return mKCal::Notebook::Ptr(new mKCal::Notebook(calNotebookId, //% "Birthdays" qtTrId("qtn_caln_birthdays"), QLatin1String(""), calNotebookColor, false, // Not shared. true, // Is master. false, // Not synced to Ovi. true, // Not writable. true, // Visible. QLatin1String("Birthday-Nokia"), QLatin1String(""), 0)); } QHash CDBirthdayCalendar::birthdays() { if (not mStorage->loadNotebookIncidences(calNotebookId)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Failed to load all incidences"; return QHash(); } QHash result; foreach(const KCalendarCore::Event::Ptr event, mCalendar->events()) { const QString eventUid = event->uid(); const QContactId contactId = localContactId(eventUid); if (!contactId.isNull()) { result.insert(contactId, CalendarBirthday(event->dtStart().date(), event->summary())); } else { qCWarning(lcContactsd) << Q_FUNC_INFO << "Birthday event with a bad uid: " << eventUid; } } return result; } void CDBirthdayCalendar::updateBirthday(const QContact &contact) { if (contact.id().isNull()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Updating birthday for null contact"; return; } const QDate contactBirthday = contact.detail().date(); if (contactBirthday.isNull()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Contact without birthday, local ID: " << contact.id(); return; } QString displayLabel = contact.detail().label(); if (displayLabel.isEmpty()) { displayLabel = contact.detail().nickname(); } if (displayLabel.isEmpty()) { displayLabel = contact.detail().name(); } if (displayLabel.isEmpty()) { displayLabel = contact.detail().emailAddress(); } if (displayLabel.isEmpty()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Contact without name to use " << contact.id(); return; } QString eventId = calendarEventId(contact.id()); if (eventId.isEmpty()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Failed to map contact id to calendar event id" << contact.id(); return; } if (not mCalendar->hasValidNotebook(calNotebookId)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Invalid notebook ID: " << calNotebookId; return; } // NOTE: if making changes beyond summary and date, increase event version number in controller. // NOTE: controller is assuming name as summary and uses that to detect changes // Retrieve birthday event. KCalendarCore::Event::Ptr event = calendarEvent(contact.id()); if (event.isNull()) { // Add a new event. event = KCalendarCore::Event::Ptr(new KCalendarCore::Event()); event->setUid(eventId); // Ensure events appear as birthdays in the calendar, NB#259710. event->setCategories(QStringList() << QLatin1String("BIRTHDAY")); if (not mCalendar->addEvent(event, calNotebookId)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Failed to add event to calendar"; return; } } else { // Update the existing event. event->setReadOnly(false); } event->startUpdates(); // Transfer birthday details from contact to calendar event. event->setSummary(displayLabel); // Event has only date information, no time. event->setDtStart(QDateTime(contactBirthday)); event->setAllDay(true); // Must always set the recurrence as it depends on the event date. KCalendarCore::Recurrence *const recurrence = event->recurrence(); if (contactBirthday.month() != 2 || contactBirthday.day() < 29) { // Simply setup yearly recurrence for trivial dates. recurrence->setStartDateTime(event->dtStart(), true); recurrence->setYearly(1); /* every year */ } else { // For birthdays on February 29th the event shall occur on the // last day of February. This is February 29th in leap years, // and February 28th in all other years. // // NOTE: Actually this recurrence pattern will fail badly for // people born on February 29th of the years 2100, 2200, 2300, // 2500, ... - but I seriously doubt we care. // // NOTE2: Using setByYearDays() instead of just setting proper // start dates, since libmkcal fails to store the start dates // of recurrence rules. KCalendarCore::RecurrenceRule *rule; // 1. Include February 29th in leap years. rule = new KCalendarCore::RecurrenceRule; rule->setStartDt(event->dtStart()); rule->setByYearDays(QList() << 60); // Feb 29th rule->setRecurrenceType(KCalendarCore::RecurrenceRule::rYearly); rule->setFrequency(4); // every 4th year recurrence->addRRule(rule); // 2. Include February 28th starting from year after birth. rule = new KCalendarCore::RecurrenceRule; rule->setStartDt(event->dtStart()); rule->setByYearDays(QList() << 59); // Feb 28th rule->setRecurrenceType(KCalendarCore::RecurrenceRule::rYearly); rule->setFrequency(1); // every year recurrence->addRRule(rule); // 3. Exclude February 28th in leap years. rule = new KCalendarCore::RecurrenceRule; rule->setStartDt(event->dtStart()); rule->setByYearDays(QList() << 59); // Feb 28th rule->setRecurrenceType(KCalendarCore::RecurrenceRule::rYearly); rule->setFrequency(4); // every 4th year recurrence->addExRule(rule); } // Clear the alarms on the event event->clearAlarms(); // We don't want any alarms for birthday events. // To set an alarm for birthday events, uncomment the code below. //KCalendarCore::Alarm::Ptr alarm = event->newAlarm(); //alarm->setType(KCalendarCore::Alarm::Audio); //alarm->setEnabled(true); //alarm->setDisplayAlarm(event->summary()); //alarm->setStartOffset(KCalendarCore::Duration(-36 * 3600 /* seconds */)); event->setReadOnly(true); event->endUpdates(); qCDebug(lcContactsd) << "Updated birthday event in calendar, local ID: " << contact.id(); } void CDBirthdayCalendar::deleteBirthday(const QContactId &contactId) { KCalendarCore::Event::Ptr event = calendarEvent(contactId); if (event.isNull()) { qCDebug(lcContactsd) << Q_FUNC_INFO << "Not found in calendar:" << contactId; return; } mCalendar->deleteEvent(event); qCDebug(lcContactsd) << "Deleted birthday event in calendar, local ID: " << event->uid(); } void CDBirthdayCalendar::save() { if (not mStorage->save()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Failed to update birthdays in calendar"; } } CalendarBirthday CDBirthdayCalendar::birthday(const QContactId &contactId) { KCalendarCore::Event::Ptr event = calendarEvent(contactId); if (event.isNull()) { return CalendarBirthday(); } return CalendarBirthday(event->dtStart().date(), event->summary()); } QContactId CDBirthdayCalendar::localContactId(const QString &calendarEventId) { quint32 numericId = 0; if (calendarEventId.startsWith(calIdExtension)) { numericId = calendarEventId.mid(calIdExtension.length()).toUInt(); } return SeasideCache::apiId(numericId); } QString CDBirthdayCalendar::calendarEventId(const QContactId &contactId) { quint32 id = SeasideCache::internalId(contactId); if (id == 0) { return QString(); } return calIdExtension + QString::number(id); } KCalendarCore::Event::Ptr CDBirthdayCalendar::calendarEvent(const QContactId &contactId) { const QString eventId = calendarEventId(contactId); if (eventId.isEmpty()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Failed to map contact to event id" << contactId.toString(); return KCalendarCore::Event::Ptr(); } if (not mStorage->load(eventId)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Unable to load event from calendar"; return KCalendarCore::Event::Ptr(); } KCalendarCore::Event::Ptr event = mCalendar->event(eventId); if (event.isNull()) { qCDebug(lcContactsd) << Q_FUNC_INFO << "Not found in calendar:" << contactId; } return event; } void CDBirthdayCalendar::onLocaleChanged() { mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId); if (notebook.isNull()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Calendar not found while changing locale"; return; } const QString name = qtTrId("qtn_caln_birthdays"); qCDebug(lcContactsd) << Q_FUNC_INFO << "Updating calendar name to" << name; notebook->setName(name); if (not mStorage->updateNotebook(notebook)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Could not save calendar"; } } contactsd-1.4.14/plugins/birthday/cdbirthdaycalendar.h000066400000000000000000000051741440357512200230440ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDBIRTHDAYCALENDAR_H #define CDBIRTHDAYCALENDAR_H #include #include #include #include #include QTCONTACTS_USE_NAMESPACE class CalendarBirthday { public: CalendarBirthday(const QDate &date, const QString &summary) : mDate(date), mSummary(summary) {} CalendarBirthday() {} public: const QDate & date() const { return mDate; } const QString & summary() const { return mSummary; } private: QDate mDate; QString mSummary; }; class CDBirthdayCalendar : public QObject { Q_OBJECT public: enum SyncMode { KeepOldDB, DropOldDB }; //! Constructor. explicit CDBirthdayCalendar(SyncMode syncMode, QObject *parent = 0); ~CDBirthdayCalendar(); //! Updates and saves Birthday of \a contact to calendar. void updateBirthday(const QContact &contact); //! Deletes \a contact birthday from calendar. void deleteBirthday(const QContactId &contactId); //! Actually save the events in the calendar database void save(); CalendarBirthday birthday(const QContactId &contactId); QHash birthdays(); private: mKCal::Notebook::Ptr createNotebook(); static QContactId localContactId(const QString &calendarEventId); static QString calendarEventId(const QContactId &contactId); KCalendarCore::Event::Ptr calendarEvent(const QContactId &contactId); private Q_SLOTS: void onLocaleChanged(); private: mKCal::ExtendedCalendar::Ptr mCalendar; mKCal::ExtendedStorage::Ptr mStorage; }; #endif // CDBIRTHDAYCALENDAR_H contactsd-1.4.14/plugins/birthday/cdbirthdaycontroller.cpp000066400000000000000000000306321440357512200240060ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2012 - 2019 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "cdbirthdaycontroller.h" #include "cdbirthdaycalendar.h" #include "cdbirthdayplugin.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE using namespace Contactsd; namespace { // version number for current type of birthday events. Increase number when changing event details. const int CURRENT_BIRTHDAY_VERSION = 1; const int UPDATE_TIMEOUT = 1000; // ms template QContactDetailFilter detailFilter(int field = -1) { QContactDetailFilter filter; filter.setDetailType(DetailType::Type, field); return filter; } QMap managerParameters() { // We don't need to handle presence changes, so report them separately and ignore them QMap parameters; parameters.insert(QString::fromLatin1("mergePresenceChanges"), QString::fromLatin1("false")); return parameters; } } CDBirthdayController::CDBirthdayController(QObject *parent) : QObject(parent) , mCalendar(stampFileUpToDate() ? CDBirthdayCalendar::KeepOldDB : CDBirthdayCalendar::DropOldDB) , mManager(QStringLiteral("org.nemomobile.contacts.sqlite"), managerParameters()) , mRequest(new QContactFetchRequest) , mSyncMode(Incremental) , mUpdateAllPending(false) { connect(&mManager, &QContactManager::contactsAdded, this, &CDBirthdayController::contactsChanged); connect(&mManager, &QContactManager::contactsChanged, this, &CDBirthdayController::contactsChanged); connect(&mManager, &QContactManager::contactsRemoved, this, &CDBirthdayController::contactsRemoved); connect(&mManager, SIGNAL(dataChanged()), SLOT(updateAllBirthdays())); updateAllBirthdays(); mUpdateTimer.setInterval(UPDATE_TIMEOUT); mUpdateTimer.setSingleShot(true); connect(&mUpdateTimer, SIGNAL(timeout()), SLOT(onUpdateQueueTimeout())); } CDBirthdayController::~CDBirthdayController() { } void CDBirthdayController::contactsChanged(const QList& contacts) { foreach (const QContactId &id, contacts) mUpdatedContacts.insert(id); // Just restart the timer - if it doesn't expire, we can afford to wait mUpdateTimer.start(); } void CDBirthdayController::contactsRemoved(const QList& contacts) { foreach (const QContactId &id, contacts) mCalendar.deleteBirthday(id); mCalendar.save(); } /////////////////////////////////////////////////////////////////////////////////////////////////// // Full sync logic /////////////////////////////////////////////////////////////////////////////////////////////////// bool CDBirthdayController::stampFileUpToDate() { QFile cacheFile(stampFilePath()); if (!cacheFile.exists()) { return false; } if (!cacheFile.open(QIODevice::ReadOnly)) { return false; } QByteArray content = cacheFile.read(100); bool ok; int value = content.toInt(&ok); return (ok && value == CURRENT_BIRTHDAY_VERSION); } void CDBirthdayController::createStampFile() { QFile cacheFile(stampFilePath()); if (not cacheFile.open(QIODevice::WriteOnly)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Unable to create birthday plugin stamp file " << cacheFile.fileName() << " with error " << cacheFile.errorString(); } cacheFile.write(QByteArray::number(CURRENT_BIRTHDAY_VERSION)); } QString CDBirthdayController::stampFilePath() { return BasePlugin::cacheFileName(QLatin1String("calendar.stamp")); } void CDBirthdayController::updateAllBirthdays() { if (mRequest->isActive()) { mUpdateAllPending = true; } else { // Fetch every contact with a birthday. fetchContacts(detailFilter(), FullSync); } } /////////////////////////////////////////////////////////////////////////////////////////////////// // Incremental sync logic /////////////////////////////////////////////////////////////////////////////////////////////////// void CDBirthdayController::onUpdateQueueTimeout() { if (mRequest->isActive()) { // The timer will be restarted by completion of the active request return; } QList contactIds(mUpdatedContacts.toList()); // If we request too many contact IDs, we will exceed the SQLite bound variable limit const int batchSize = 200; if (contactIds.count() > batchSize) { mUpdatedContacts = contactIds.mid(batchSize).toSet(); contactIds = contactIds.mid(0, batchSize); } else { mUpdatedContacts.clear(); } QContactIdFilter fetchFilter; fetchFilter.setIds(contactIds); fetchContacts(fetchFilter, Incremental); } /////////////////////////////////////////////////////////////////////////////////////////////////// // Common sync logic /////////////////////////////////////////////////////////////////////////////////////////////////// void CDBirthdayController::fetchContacts(const QContactFilter &filter, SyncMode mode) { // Set up the fetch request object mRequest->setManager(&mManager); QContactFetchHint fetchHint; fetchHint.setDetailTypesHint(QList() << QContactBirthday::Type << QContactDisplayLabel::Type); fetchHint.setOptimizationHints(QContactFetchHint::NoRelationships | QContactFetchHint::NoActionPreferences | QContactFetchHint::NoBinaryBlobs); mRequest->setFetchHint(fetchHint); // Only fetch aggregate contacts QContactCollectionFilter aggregateFilter; aggregateFilter.setCollectionId(QtContactsSqliteExtensions::aggregateCollectionId(mManager.managerUri())); mRequest->setFilter(filter & aggregateFilter); connect(mRequest.data(), SIGNAL(stateChanged(QContactAbstractRequest::State)), SLOT(onRequestStateChanged(QContactAbstractRequest::State))); if (!mRequest->start()) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Unable to start birthday contact fetch request"; } else { qCDebug(lcContactsd) << "Birthday contacts fetch request started"; mSyncMode = mode; } } void CDBirthdayController::onRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) { qCDebug(lcContactsd) << "Birthday contacts fetch request finished"; if (mRequest->error() != QContactManager::NoError) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Error during birthday contact fetch request, code:" << mRequest->error(); } else { if (mSyncMode == FullSync) { syncBirthdays(mRequest->contacts()); // Create the stamp file only after a successful full sync. createStampFile(); } else { updateBirthdays(mRequest->contacts()); } } // We're finished with this request, clear it out to drop any contact data // (although don't delete it directly, as we're currently handling a signal from it) mRequest.take()->deleteLater(); mRequest.reset(new QContactFetchRequest); } else if (newState == QContactAbstractRequest::CanceledState) { qCDebug(lcContactsd) << "Birthday contacts fetch request canceled"; } else { // Request still in progress return; } // Save the calendar in any case (success or not), since this "save" call // also applies for the deleteBirthday() calls in processNotificationQueues() mCalendar.save(); if (mUpdateAllPending) { // We need to update all birthdays mUpdateAllPending = false; updateAllBirthdays(); } else if (!mUpdatedContacts.isEmpty() && !mUpdateTimer.isActive()) { // If some updated contacts weren't requested, we need to go again mUpdateTimer.start(); } } void CDBirthdayController::updateBirthdays(const QList &changedBirthdays) { foreach (const QContact &contact, changedBirthdays) { const QContactBirthday contactBirthday = contact.detail(); const QString contactDisplayLabel = contact.detail().label(); const CalendarBirthday calendarBirthday = mCalendar.birthday(contact.id()); // Display label or birthdate was removed from the contact, so delete it from the calendar. if (contactDisplayLabel.isEmpty() || contactBirthday.date().isNull()) { if (!calendarBirthday.date().isNull()) { qCDebug(lcContactsd) << "Contact: " << contact.id() << " removed birthday or displayLabel, so delete the calendar event"; mCalendar.deleteBirthday(contact.id()); } // Display label or birthdate was changed on the contact, so update the calendar. } else if ((contactDisplayLabel != calendarBirthday.summary()) || (contactBirthday.date() != calendarBirthday.date())) { qCDebug(lcContactsd) << "Contact with calendar birthday: " << calendarBirthday.date() << " and calendar displayLabel: " << calendarBirthday.summary() << " changed details to: " << contactBirthday.date() << contactDisplayLabel << ", so update the calendar event"; mCalendar.updateBirthday(contact); } } } void CDBirthdayController::syncBirthdays(const QList &birthdayContacts) { QHash oldBirthdays = mCalendar.birthdays(); // Check all birthdays from the contacts if the stored calendar item is up-to-date foreach (const QContact &contact, birthdayContacts) { const QString contactDisplayLabel = contact.detail().label(); if (contactDisplayLabel.isEmpty()) { qCDebug(lcContactsd) << "Contact: " << contact << " has no displayLabel, so not syncing to calendar"; continue; } QHash::Iterator it = oldBirthdays.find(contact.id()); if (oldBirthdays.end() != it) { const QContactBirthday contactBirthday = contact.detail(); const CalendarBirthday &calendarBirthday = *it; // Display label or birthdate was changed on the contact, so update the calendar. if ((contactDisplayLabel != calendarBirthday.summary()) || (contactBirthday.date() != calendarBirthday.date())) { qCDebug(lcContactsd) << "Contact with calendar birthday: " << contactBirthday.date() << " and calendar displayLabel: " << calendarBirthday.summary() << " changed details to: " << contact << ", so update the calendar event"; mCalendar.updateBirthday(contact); } // Birthday exists, so not a garbage one oldBirthdays.erase(it); } else { // Create new birthday mCalendar.updateBirthday(contact); } } // Remaining old birthdays in the calendar db do not did not match any contact, so remove them. foreach (const QContactId &id, oldBirthdays.keys()) { qCDebug(lcContactsd) << "Birthday with contact id" << id << "no longer has a matching contact, trashing it"; mCalendar.deleteBirthday(id); } } contactsd-1.4.14/plugins/birthday/cdbirthdaycontroller.h000066400000000000000000000046351440357512200234570ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDBIRTHDAYCONTROLLER_H #define CDBIRTHDAYCONTROLLER_H #include "cdbirthdaycalendar.h" #include #include #include #include #include #include #include class CDBirthdayCalendar; QTCONTACTS_USE_NAMESPACE class CDBirthdayController : public QObject { Q_OBJECT enum SyncMode { Incremental, FullSync }; public: explicit CDBirthdayController(QObject *parent = 0); ~CDBirthdayController(); private Q_SLOTS: void contactsChanged(const QList &contacts); void contactsRemoved(const QList &contacts); void onRequestStateChanged(QContactAbstractRequest::State newState); void updateAllBirthdays(); void onUpdateQueueTimeout(); private: static void createStampFile(); static QString stampFilePath(); static bool stampFileUpToDate(); void fetchContacts(const QContactFilter &filter, SyncMode mode); void updateBirthdays(const QList &changedBirthdays); void syncBirthdays(const QList &birthdayContacts); private: CDBirthdayCalendar mCalendar; QContactManager mManager; QScopedPointer mRequest; QSet mUpdatedContacts; QTimer mUpdateTimer; SyncMode mSyncMode; bool mUpdateAllPending; }; #endif // CDBIRTHDAYCONTROLLER_H contactsd-1.4.14/plugins/birthday/cdbirthdayplugin.cpp000066400000000000000000000033371440357512200231230ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "cdbirthdaycontroller.h" #include "cdbirthdayplugin.h" #include "debug.h" using namespace Contactsd; CDBirthdayPlugin::CDBirthdayPlugin() : mController(0) { } CDBirthdayPlugin::~CDBirthdayPlugin() { delete mController; } void CDBirthdayPlugin::init() { qCDebug(lcContactsd) << "Initializing contactsd birthday plugin"; mController = new CDBirthdayController(this); } CDBirthdayPlugin::MetaData CDBirthdayPlugin::metaData() { MetaData data; data[metaDataKeyName] = QVariant(QLatin1String("birthday")); data[metaDataKeyVersion] = QVariant(QLatin1String("0.1")); data[metaDataKeyComment] = QVariant(QLatin1String("contactsd birthday plugin")); return data; } contactsd-1.4.14/plugins/birthday/cdbirthdayplugin.h000066400000000000000000000030141440357512200225600ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDBIRTHDAYPLUGIN_H #define CDBIRTHDAYPLUGIN_H #include #include #include #include #include "base-plugin.h" class CDBirthdayController; class CDBirthdayPlugin : public Contactsd::BasePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.birthday") public: CDBirthdayPlugin(); ~CDBirthdayPlugin(); void init(); MetaData metaData(); private: CDBirthdayController *mController; }; #endif // CDBIRTHDAYPLUGIN_H contactsd-1.4.14/plugins/calendar/000077500000000000000000000000001440357512200170205ustar00rootroot00000000000000contactsd-1.4.14/plugins/calendar/calendar.pro000066400000000000000000000007161440357512200213170ustar00rootroot00000000000000include(../../lib.pri) TEMPLATE = lib QT -= gui QT += contacts-private CONFIG += plugin CONFIG += link_pkgconfig PKGCONFIG += accounts-qt5 KF5CalendarCore libmkcal-qt5 DEFINES -= QT_NO_CAST_TO_ASCII DEFINES -= QT_NO_CAST_FROM_ASCII HEADERS = \ cdcalendarcontroller.h \ cdcalendarplugin.h SOURCES = \ cdcalendarcontroller.cpp \ cdcalendarplugin.cpp TARGET = calendarplugin target.path = $$LIBDIR/contactsd-1.0/plugins INSTALLS += target contactsd-1.4.14/plugins/calendar/cdcalendarcontroller.cpp000066400000000000000000000162321440357512200237140ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2020 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include #include "cdcalendarcontroller.h" #include "cdcalendarplugin.h" #include "debug.h" using namespace Contactsd; using namespace Accounts; namespace { // Anonymous namespace /*! \brief Enable/disable all mKCal notebooks related to the account id Sets the \a enabled status for all notebooks associated with the given account \a id. */ static const QByteArray VISIBILITY_CHANGED_FLAG("hidden_by_account"); void updateNotebooks(AccountId id, bool enabled) { mKCal::ExtendedCalendar::Ptr calendar = mKCal::ExtendedCalendar::Ptr( new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mKCal::ExtendedStorage::Ptr storage = calendar->defaultStorage(calendar); storage->open(); const mKCal::Notebook::List notebooks = storage->notebooks(); for (const mKCal::Notebook::Ptr notebook : notebooks) { const QString accountStr = notebook->account(); bool ok = false; AccountId accountInt = accountStr.toULong(&ok); if (ok && accountInt == id) { bool visible = notebook->isVisible(); if (!enabled && visible) { notebook->setIsVisible(false); notebook->setCustomProperty(VISIBILITY_CHANGED_FLAG, QString::fromLatin1("true")); storage->updateNotebook(notebook); } else if (enabled && !visible && !notebook->customProperty(VISIBILITY_CHANGED_FLAG).isEmpty()) { notebook->setIsVisible(true); notebook->setCustomProperty(VISIBILITY_CHANGED_FLAG, QString()); storage->updateNotebook(notebook); } } } } } // Anonymous namespace /*! \brief Creates and connects an Account Manager Creates an Account Manager for the service type and attaches the manager's enabledEvent signal to the provided \a enabledEvent slot. */ Manager * CDCalendarController::SetupManager( const QString &service, void (CDCalendarController::*enabledEvent)(AccountId id)) { Manager * manager = new Manager(service, this); Error error = manager->lastError(); if (error.type() == Error::NoError) { connect(manager, &Manager::enabledEvent, this, enabledEvent); } else { qWarning() << "Accounts manager creation failed for" << service << "with error:" << error.message(); } return manager; } /*! \instantiates CDCalendarController \brief Synchronises mKCal notebook enable state with Accounts The mKCal notebook enabled/disabled state is interpreted to mean the calendar should be considered available. This plugin watches for changes in the Account or Account Service enabled status for Accounts with calendars and updates the relevant notebooks to match. */ CDCalendarController::CDCalendarController(QObject *parent) : QObject(parent) { // Managers are needed following the specific service types we're interested // in. Without a type, libaccounts-glib won't send any signals (if we do set // a type, it sends us signals for all account types!). m_manager_caldav = SetupManager(QStringLiteral("caldav"), &CDCalendarController::enabledEventCalDav); m_manager_sync = SetupManager(QStringLiteral("sync"), &CDCalendarController::enabledEventSync); } CDCalendarController::~CDCalendarController() { } /*! \brief Responds to changes in the enabled state for CalDav services This is called when the enabled status for account with \a id changes. If the account has a relevant service, the notebooks associated with it have their enabled state set using the following recipe: "If the account is enabled and at least one calendar service is enabled, then the notebooks will be enabled. Otherwise they will be disabled." */ void CDCalendarController::enabledEventCalDav(AccountId id) { Account *account = m_manager_caldav->account(id); const ServiceList serviceList = account->services(); if (serviceList.isEmpty()) { // Even when the Account Manager is configured to be only interested // in accounts with a specific service type, libaccounts-glib will still // send events for other accounts, but their service list will be empty. // We want to ignore these events. return; } bool enabled = account->enabled(); if (enabled) { // The account is enabled, but the calendar service may not be enabled = false; ServiceList::const_iterator it; for (it = serviceList.constBegin(); !enabled && it != serviceList.constEnd(); ++it) { account->selectService(*it); enabled = account->enabled(); } account->selectService(); } // Update the mKCal notebook with the result updateNotebooks(id, enabled); } /*! \brief Responds to changes in the enabled state for Google services This is called when the enabled status for account with \a id changes. If this is a Google account with a relevant service, the notebooks associated with it have their enabled state set using the following recipe: "If the account is enabled and at least one calendar service is enabled, then the notebooks will be enabled. Otherwise they will be disabled." */ void CDCalendarController::enabledEventSync(AccountId id) { Account *account = m_manager_sync->account(id); if (account->providerName() != QLatin1String("google")) { // We're only interested in Google accounts here return; } const ServiceList serviceList = account->services(); if (serviceList.isEmpty()) { // Even when the Account Manager is configured to be only interested // in accounts with a specific service type, libaccounts-glib will still // send events for other accounts, but their service list will be empty. // We want to ignore these events. return; } bool enabled = account->enabled(); if (enabled) { // The account is enabled, but the calendar service may not be enabled = false; ServiceList::const_iterator it; for (it = serviceList.constBegin(); !enabled && it != serviceList.constEnd(); ++it) { // This seems to be the only way to check whether it's a calendar if ((*it).name() == QLatin1String("google-calendars")) { account->selectService(*it); enabled = account->enabled(); } } account->selectService(); } // Update the mKCal notebook with the result updateNotebooks(id, enabled); } contactsd-1.4.14/plugins/calendar/cdcalendarcontroller.h000066400000000000000000000024361440357512200233620ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2020 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef CDCALENDARCONTROLLER_H #define CDCALENDARCONTROLLER_H #include #include #include class CDCalendarController : public QObject { Q_OBJECT public: explicit CDCalendarController(QObject *parent = 0); ~CDCalendarController(); public Q_SLOTS: void enabledEventCalDav(Accounts::AccountId id); void enabledEventSync(Accounts::AccountId id); private: Accounts::Manager * SetupManager( const QString &service, void (CDCalendarController::*enabledEvent)(Accounts::AccountId id)); private: Accounts::Manager *m_manager_caldav; Accounts::Manager *m_manager_sync; }; #endif // CDCALENDARCONTROLLER_H contactsd-1.4.14/plugins/calendar/cdcalendarplugin.cpp000066400000000000000000000024131440357512200230230ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2020 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include "cdcalendarcontroller.h" #include "cdcalendarplugin.h" #include "debug.h" using namespace Contactsd; CDCalendarPlugin::CDCalendarPlugin() : mController(0) { } CDCalendarPlugin::~CDCalendarPlugin() { } void CDCalendarPlugin::init() { qCDebug(lcContactsd) << "Initializing contactsd Calendar plugin"; mController = new CDCalendarController(this); } CDCalendarPlugin::MetaData CDCalendarPlugin::metaData() { MetaData data; data[metaDataKeyName] = QVariant(QLatin1String("Calendar")); data[metaDataKeyVersion] = QVariant(QLatin1String("0.1")); data[metaDataKeyComment] = QVariant(QLatin1String("contactsd Calendar plugin")); return data; } contactsd-1.4.14/plugins/calendar/cdcalendarplugin.h000066400000000000000000000021201440357512200224630ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2020 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef CDCALENDARPLUGIN_H #define CDCALENDARPLUGIN_H #include #include #include #include #include "base-plugin.h" class CDCalendarController; class CDCalendarPlugin : public Contactsd::BasePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.Calendar") public: CDCalendarPlugin(); ~CDCalendarPlugin(); void init(); MetaData metaData(); private: CDCalendarController *mController; }; #endif // CDCALENDARPLUGIN_H contactsd-1.4.14/plugins/contacts-extensions.pri000066400000000000000000000005311440357512200217750ustar00rootroot00000000000000QT += contacts-private CONFIG += link_pkgconfig PKGCONFIG += qtcontacts-sqlite-qt5-extensions # We need the moc output for ContactManagerEngine from sqlite-extensions extensionsIncludePath = $$system(pkg-config --cflags-only-I qtcontacts-sqlite-qt5-extensions) VPATH += $$replace(extensionsIncludePath, -I, ) HEADERS += contactmanagerengine.h contactsd-1.4.14/plugins/dbusplugin/000077500000000000000000000000001440357512200174235ustar00rootroot00000000000000contactsd-1.4.14/plugins/dbusplugin/dbusplugin.cpp000066400000000000000000000031361440357512200223060ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "dbusplugin.h" #include #include DbusPlugin::DbusPlugin() { fakeSignals(); } DbusPlugin::~DbusPlugin() { } void DbusPlugin::init() { } DbusPlugin::MetaData DbusPlugin::metaData() { MetaData data; data[metaDataKeyName] = QVariant(QString("dbusplugin")); return data; } void DbusPlugin::fakeSignals() { Q_EMIT importStarted(QString("IM Service"), QString("/fake/account/")); Q_EMIT importEnded(QString("IM Service"), QString("/fake/account/"), 10, 30, 1); } Q_EXPORT_PLUGIN2(dbusplugin, DbusPlugin) contactsd-1.4.14/plugins/dbusplugin/dbusplugin.h000066400000000000000000000025751440357512200217610ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef DBUSPLUGIN_H #define DBUSPLUGIN_H #include #include #include #include #include "base-plugin.h" class DbusPlugin : public Contactsd::BasePlugin { Q_OBJECT public: DbusPlugin(); ~DbusPlugin(); void init(); MetaData metaData(); private: void fakeSignals(); }; #endif // DBUSPLUGIN_H contactsd-1.4.14/plugins/dbusplugin/dbusplugin.pro000066400000000000000000000025661440357512200223320ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../../lib.pri) TEMPLATE = lib QT += dbus CONFIG += plugin CONFIG(coverage):{ QMAKE_CXXFLAGS += -c -g --coverage -ftest-coverage -fprofile-arcs LIBS += -lgcov } CONFIG += link_pkgconfig PKGCONFIG += TelepathyQt4 INCLUDEPATH += $$TOP_SOURCEDIR/src HEADERS = dbusplugin.h SOURCES = dbusplugin.cpp TARGET = dbusplugin target.path = $$LIBDIR/contactsd-1.0/plugins INSTALLS += target contactsd-1.4.14/plugins/exporter/000077500000000000000000000000001440357512200171175ustar00rootroot00000000000000contactsd-1.4.14/plugins/exporter/cdexportercontroller.cpp000066400000000000000000000070631440357512200241140ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2014 - 2019 Jolla Ltd ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include "cdexportercontroller.h" #include #include #include #include #include #include #include #include #include namespace { QMap privilegedManagerParameters() { QMap rv; rv.insert(QStringLiteral("mergePresenceChanges"), QStringLiteral("false")); return rv; } QString managerName() { return QStringLiteral("org.nemomobile.contacts.sqlite"); } } CDExporterController::CDExporterController(QObject *parent) : QObject(parent) , m_privilegedManager(managerName(), privilegedManagerParameters()) { QtContactsSqliteExtensions::ContactManagerEngine *engine = QtContactsSqliteExtensions::contactManagerEngine(m_privilegedManager); connect(engine, &QtContactsSqliteExtensions::ContactManagerEngine::collectionContactsChanged, this, &CDExporterController::collectionContactsChanged); } CDExporterController::~CDExporterController() { } void CDExporterController::collectionContactsChanged(const QList &collectionIds) { // If the collection originates from an account (e.g. an address book synced from a remote cloud // account) then sync those changes upstream to that account. QStringList accountProviders; for (const QContactCollectionId &collectionId : collectionIds) { const QContactCollection collection = m_privilegedManager.collection(collectionId); const Accounts::AccountId accountId = collection.extendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_ACCOUNTID).toInt(); if (accountId > 0) { if (!m_manager) { m_manager = new Accounts::Manager(this); } Accounts::Account *account = m_manager->account(accountId); if (account) { accountProviders.append(account->providerName()); } else { qWarning() << "CDExport: got change notification for contact collection" << collectionId << "matching account id" << accountId << "but cannot find matching account!"; } } } if (!accountProviders.isEmpty()) { qWarning() << "CDExport: triggering contacts remote sync:" << accountProviders; QDBusMessage message = QDBusMessage::createMethodCall( QStringLiteral("com.nokia.contactsd"), QStringLiteral("/SyncTrigger"), QStringLiteral("com.nokia.contactsd"), QStringLiteral("triggerSync")); message.setArguments(QVariantList() << QVariant::fromValue(accountProviders) << QVariant::fromValue(1) // only if AlwaysUpToDate set in profile << QVariant::fromValue(1)); // only if Upsync or TwoWay direction QDBusConnection::sessionBus().asyncCall(message); } } contactsd-1.4.14/plugins/exporter/cdexportercontroller.h000066400000000000000000000022201440357512200235470ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2014 - 2019 Jolla Ltd ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef CDEXPORTERCONTROLLER_H #define CDEXPORTERCONTROLLER_H #include #include namespace Accounts { class Manager; } QTCONTACTS_USE_NAMESPACE class CDExporterController : public QObject { Q_OBJECT public: explicit CDExporterController(QObject *parent = nullptr); ~CDExporterController(); private: void collectionContactsChanged(const QList &collectionIds); QContactManager m_privilegedManager; Accounts::Manager *m_manager = nullptr; }; #endif // CDEXPORTERCONTROLLER_H contactsd-1.4.14/plugins/exporter/cdexporterplugin.cpp000066400000000000000000000024241440357512200232230ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2014 Jolla Ltd. ** ** Contact: Matt Vogt ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include "cdexportercontroller.h" #include "cdexporterplugin.h" #include "debug.h" using namespace Contactsd; CDExporterPlugin::CDExporterPlugin() : mController(0) { } CDExporterPlugin::~CDExporterPlugin() { } void CDExporterPlugin::init() { qCDebug(lcContactsd) << "Initializing contactsd exporter plugin"; mController = new CDExporterController(this); } CDExporterPlugin::MetaData CDExporterPlugin::metaData() { MetaData data; data[metaDataKeyName] = QVariant(QLatin1String("exporter")); data[metaDataKeyVersion] = QVariant(QLatin1String("0.1")); data[metaDataKeyComment] = QVariant(QLatin1String("contactsd exporter plugin")); return data; } contactsd-1.4.14/plugins/exporter/cdexporterplugin.h000066400000000000000000000021311440357512200226630ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2014 Jolla Ltd. ** ** Contact: Matt Vogt ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef CDEXPORTERPLUGIN_H #define CDEXPORTERPLUGIN_H #include #include #include #include #include "base-plugin.h" class CDExporterController; class CDExporterPlugin : public Contactsd::BasePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.exporter") public: CDExporterPlugin(); ~CDExporterPlugin(); void init(); MetaData metaData(); private: CDExporterController *mController; }; #endif // CDEXPORTERPLUGIN_H contactsd-1.4.14/plugins/exporter/exporter.pro000066400000000000000000000010221440357512200215040ustar00rootroot00000000000000include(../contacts-extensions.pri) include(../../lib.pri) TEMPLATE = lib QT -= gui QT += dbus CONFIG += plugin PKGCONFIG += buteosyncfw5 accounts-qt5 DEFINES *= QTCONTACTS_SQLITE_PERFORM_AGGREGATION DEFINES -= QT_NO_CAST_TO_ASCII DEFINES -= QT_NO_CAST_FROM_ASCII INCLUDEPATH += $$TOP_SOURCEDIR/src HEADERS += \ cdexportercontroller.h \ cdexporterplugin.h SOURCES = \ cdexportercontroller.cpp \ cdexporterplugin.cpp TARGET = exporterplugin target.path = $$LIBDIR/contactsd-1.0/plugins INSTALLS += target contactsd-1.4.14/plugins/fakeplugin/000077500000000000000000000000001440357512200173745ustar00rootroot00000000000000contactsd-1.4.14/plugins/fakeplugin/fakeplugin.cpp000066400000000000000000000024351440357512200222310ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "fakeplugin.h" #include FakePlugin::FakePlugin() { } FakePlugin::~FakePlugin() { } void FakePlugin::init() { } FakePlugin::MetaData FakePlugin::metaData() { MetaData data; return data; } Q_EXPORT_PLUGIN2(fakeplugin, FakePlugin) contactsd-1.4.14/plugins/fakeplugin/fakeplugin.h000066400000000000000000000026001440357512200216700ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef FAKEPLUGIN_H #define FAKEPLUGIN_H #include #include #include #include #include "base-plugin.h" class FakePlugin : public Contactsd::BasePlugin { Q_OBJECT public: FakePlugin(); ~FakePlugin(); void init(); MetaData metaData(); private Q_SLOTS: void timeout(); }; #endif // FAKEPLUGIN_H contactsd-1.4.14/plugins/fakeplugin/fakeplugin.pro000066400000000000000000000025221440357512200222440ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../../lib.pri) TEMPLATE = lib QT += dbus CONFIG += plugin CONFIG(coverage):{ QMAKE_CXXFLAGS += -c -g --coverage -ftest-coverage -fprofile-arcs LIBS += -lgcov } CONFIG += link_pkgconfig PKGCONFIG += TelepathyQt5 HEADERS = fakeplugin.h SOURCES = fakeplugin.cpp TARGET = fakeplugin target.path = $$LIBDIR/contactsd-1.0/plugins INSTALLS += target contactsd-1.4.14/plugins/plugins.pro000066400000000000000000000020611440357512200174510ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. TEMPLATE = subdirs SUBDIRS += telepathy birthday sim exporter calendar contactsd-1.4.14/plugins/sim/000077500000000000000000000000001440357512200160375ustar00rootroot00000000000000contactsd-1.4.14/plugins/sim/cdsimcontroller.cpp000066400000000000000000000666151440357512200217640ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2013-2019 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include "cdsimcontroller.h" #include "cdsimplugin.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Contactsd; namespace { const QString CollectionKeyModemPath = QStringLiteral("ModemPath"); const QString CollectionKeyModemIdentifier = QStringLiteral("ModemIdentifier"); QMap contactManagerParameters() { QMap rv; rv.insert(QStringLiteral("mergePresenceChanges"), QStringLiteral("false")); return rv; } } CDSimModemData::CDSimModemData(CDSimController *controller, const QString &modemPath) : QObject(controller) , m_modemPath(modemPath) , m_voicemailConf(0) , m_ready(false) , m_retries(0) { connect(&m_simManager, SIGNAL(presenceChanged(bool)), SLOT(simStateChanged())); connect(&m_simManager, SIGNAL(cardIdentifierChanged(QString)), SLOT(simStateChanged())); connect(&m_phonebook, SIGNAL(importReady(QString)), SLOT(vcardDataAvailable(QString))); connect(&m_phonebook, SIGNAL(importFailed()), SLOT(vcardReadFailed())); // Resync the contacts list whenever the phonebook availability changes connect(&m_phonebook, SIGNAL(validChanged(bool)), SLOT(phonebookValidChanged(bool))); connect(&m_contactReader, SIGNAL(stateChanged(QVersitReader::State)), SLOT(readerStateChanged(QVersitReader::State))); connect(&m_messageWaiting, SIGNAL(voicemailMailboxNumberChanged(QString)), SLOT(voicemailConfigurationChanged())); // Don't activate ofono objects in test mode if (controller->m_active) { // Start reading the SIM contacts m_simManager.setModemPath(m_modemPath); m_simInfo.setModemPath(m_modemPath); m_phonebook.setModemPath(m_modemPath); m_messageWaiting.setModemPath(m_modemPath); } } CDSimModemData::~CDSimModemData() { delete m_voicemailConf; } CDSimController *CDSimModemData::controller() const { return qobject_cast(parent()); } QContactManager &CDSimModemData::manager() const { return controller()->contactManager(); } QString CDSimModemData::modemIdentifier() const { if (controller()->m_active) { return m_simManager.cardIdentifier(); } return m_modemPath; } QString CDSimModemData::modemPath() const { return m_modemPath; } QContactCollection CDSimModemData::contactCollection() const { return m_collection; } QList CDSimModemData::fetchContacts() const { QContactCollectionFilter filter; filter.setCollectionId(m_collection.id()); QContactFetchHint hint; hint.setOptimizationHints(QContactFetchHint::NoRelationships | QContactFetchHint::NoActionPreferences | QContactFetchHint::NoBinaryBlobs); return manager().contacts(filter, QList(), hint); } bool CDSimModemData::ready() const { return m_ready; } void CDSimModemData::setReady(bool ready) { if (m_ready != ready) { m_ready = ready; emit readyChanged(m_ready); if (m_ready) { initCollection(); updateVoicemailConfiguration(); if (m_phonebook.isValid()) { performTransientImport(); } } } } void CDSimModemData::updateBusy() { controller()->updateBusy(); } void CDSimModemData::timerEvent(QTimerEvent *event) { if (event->timerId() == m_retryTimer.timerId()) { m_retryTimer.stop(); if (m_ready) { performTransientImport(); } } } CDSimController::CDSimController(QObject *parent, bool active) : QObject(parent) , m_manager(QStringLiteral("org.nemomobile.contacts.sqlite"), contactManagerParameters()) , m_transientImport(true) , m_busy(false) , m_active(active) , m_transientImportConf(QString::fromLatin1("/org/nemomobile/contacts/sim/transient_import")) { QVariant transientImport = m_transientImportConf.value(); if (transientImport.isValid()) m_transientImport = (transientImport.toInt() == 1); connect(&m_transientImportConf, SIGNAL(valueChanged()), this, SLOT(transientImportConfigurationChanged())); } CDSimController::~CDSimController() { } static QContactFilter deactivatedFilter() { return QContactStatusFlags::matchFlag(QContactStatusFlags::IsDeactivated, QContactFilter::MatchContains); } QContactManager &CDSimController::contactManager() { return m_manager; } QContactCollection CDSimController::contactCollection(const QString &modemPath) const { for (auto it = m_modems.constBegin(); it != m_modems.constEnd(); ++it) { CDSimModemData *data = it.value(); if (data->modemPath() == modemPath) { return data->contactCollection(); } } return QContactCollection(); } int CDSimController::modemIndex(const QString &modemPath) const { return m_availableModems.indexOf(modemPath); } void CDSimController::setModemPaths(const QStringList &paths) { qWarning() << "Managing SIM contacts for modem paths:" << paths; m_availableModems = paths; QMap::iterator it = m_modems.begin(); while (it != m_modems.end()) { if (paths.contains(it.key())) { ++it; } else { // This modem is no longer active; remove it delete it.value(); it = m_modems.erase(it); } } QSet absentModemPaths; foreach (const QString &path, paths) { CDSimModemData *modemData = m_modems.value(path); if (!modemData) { modemData = new CDSimModemData(this, path); connect(modemData, SIGNAL(readyChanged(bool)), SLOT(modemReadyChanged(bool))); m_modems.insert(path, modemData); } if (!modemData->ready()) { absentModemPaths.insert(path); } } if (absentModemPaths.isEmpty()) { // Remove any SIM contacts that don't originate from our current modems removeObsoleteSimCollections(); } else { // Check for obsolete contacts after all modems are ready m_absentModemPaths = absentModemPaths; m_readyTimer.start(30 * 1000, this); } } bool CDSimController::busy() const { return m_busy; } void CDSimController::updateBusy() { bool busy = false; QMap::const_iterator mit = m_modems.constBegin(), mend = m_modems.constEnd(); for ( ; !busy && mit != mend; ++mit) { busy |= ((*mit)->m_phonebook.importing() || (*mit)->m_contactReader.state() == QVersitReader::ActiveState); } if (m_busy != busy) { m_busy = busy; emit busyChanged(m_busy); } } void CDSimController::timerEvent(QTimerEvent *event) { if (event->timerId() == m_readyTimer.timerId()) { m_readyTimer.stop(); if (!m_absentModemPaths.isEmpty()) { // Stop waiting for these modems to report presence m_absentModemPaths.clear(); removeObsoleteSimCollections(); } } } void CDSimModemData::performTransientImport() { if (modemIdentifier().isEmpty()) { qWarning() << "No identifier available for modem:" << m_simManager.modemPath(); return; } if (m_phonebook.isValid() && controller()->m_transientImport) { // Read all contacts from the SIM m_phonebook.beginImport(); } else { m_simContacts.clear(); deactivateAllSimContacts(); } updateBusy(); } void CDSimController::transientImportConfigurationChanged() { bool importEnabled(true); QVariant transientImport = m_transientImportConf.value(); if (transientImport.isValid()) importEnabled = (transientImport.toInt() == 1); if (m_transientImport != importEnabled) { m_transientImport = importEnabled; QMap::const_iterator mit = m_modems.constBegin(), mend = m_modems.constEnd(); for ( ; mit != mend; ++mit) { (*mit)->performTransientImport(); } } } void CDSimController::modemReadyChanged(bool ready) { CDSimModemData *modem = qobject_cast(sender()); if (ready && m_absentModemPaths.contains(modem->m_simManager.modemPath())) { m_absentModemPaths.remove(modem->m_simManager.modemPath()); if (m_absentModemPaths.isEmpty()) { m_readyTimer.stop(); // Remove any SIM contacts that don't originate from our current modems removeObsoleteSimCollections(); } } } void CDSimModemData::simStateChanged() { setReady(m_simManager.present() && !m_simManager.cardIdentifier().isEmpty()); } void CDSimModemData::phonebookValidChanged(bool) { if (m_ready) { performTransientImport(); } } void CDSimModemData::vcardDataAvailable(const QString &vcardData) { // Create contact records from the SIM VCard data m_simContacts.clear(); m_contactReader.setData(vcardData.toUtf8()); m_contactReader.startReading(); updateBusy(); m_retries = 0; } void CDSimModemData::vcardReadFailed() { qWarning() << "Unable to read VCard data from SIM:" << m_phonebook.modemPath(); updateBusy(); const int maxRetries = 5; if (m_retries < maxRetries) { ++m_retries; // Wait a short period before trying again; the phonebook often reports // a failure to read contact data on attempts early after boot m_retryTimer.start(10 * 1000, this); } } void CDSimModemData::readerStateChanged(QVersitReader::State state) { if (state != QVersitReader::FinishedState) return; QList results = m_contactReader.results(); if (results.isEmpty()) { m_simContacts.clear(); removeAllSimContacts(); } else { QVersitContactImporter importer; importer.importDocuments(results); m_simContacts = importer.contacts(); if (m_simContacts.isEmpty()) { removeAllSimContacts(); } else { // import or remove contacts from local storage as necessary. ensureSimContactsPresent(); } } updateBusy(); } void CDSimModemData::deactivateAllSimContacts() { const QList contacts = fetchContacts(); if (contacts.size()) { QList deactivatedContacts; foreach (QContact contact, contacts) { QContactDeactivated deactivated; contact.saveDetail(&deactivated); deactivatedContacts.append(contact); } if (!manager().saveContacts(&deactivatedContacts)) { qWarning() << "Error deactivating sim contacts"; } } } void CDSimModemData::removeAllSimContacts() { if (m_collection.id().isNull()) { return; } QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(m_collection.id()); const QList contactIds = manager().contactIds(collectionFilter, QList()); if (contactIds.count()) { if (manager().removeContacts(contactIds)) { qDebug() << "Removed sim contacts for modem" << m_modemPath; } else { qWarning() << "Unable to remove sim contacts for modem" << m_modemPath; } } } void CDSimModemData::ensureSimContactsPresent() { // Ensure all contacts from the SIM are present in the store QContactFetchHint hint; hint.setDetailTypesHint(QList() << QContactNickname::Type << QContactPhoneNumber::Type); hint.setOptimizationHints(QContactFetchHint::NoRelationships | QContactFetchHint::NoActionPreferences | QContactFetchHint::NoBinaryBlobs); QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(m_collection.id()); QList storedSimContacts = manager().contacts(collectionFilter, QList(), hint); // Also find any deactivated SIM contacts storedSimContacts.append(manager().contacts(collectionFilter & deactivatedFilter(), QList(), hint)); QMap existingContacts; foreach (const QContact &contact, storedSimContacts) { // Identify imported SIM contacts by their nickname record const QString nickname(contact.detail().nickname().trimmed()); existingContacts.insert(nickname, contact); } // coalesce SIM contacts with the same display label. QList coalescedSimContacts; foreach (const QContact &simContact, m_simContacts) { const QContactDisplayLabel &displayLabel = simContact.detail(); const QList &phoneNumbers = simContact.details(); // search for a pre-existing match in the coalesced list. bool coalescedContactFound = false; for (int i = 0; i < coalescedSimContacts.size(); ++i) { QContact coalescedContact = coalescedSimContacts.at(i); if (coalescedContact.detail().label().trimmed() == displayLabel.label().trimmed()) { // found a match. Coalesce the phone numbers and update the contact in the list. QList coalescedPhoneNumbers = coalescedContact.details(); foreach (QContactPhoneNumber phn, phoneNumbers) { // check to see if the coalesced phone numbers list contains this number bool coalescedNumberFound = false; foreach (const QContactPhoneNumber &cphn, coalescedPhoneNumbers) { if (phn.number() == cphn.number() && phn.contexts() == cphn.contexts() && phn.subTypes() == cphn.subTypes()) { // already exists in the coalesced contact, no need to add it. coalescedNumberFound = true; break; } } // if not, add the number to the coalesced contact and to the coalesced list if (!coalescedNumberFound) { coalescedContact.saveDetail(&phn); coalescedPhoneNumbers.append(phn); } } coalescedSimContacts.replace(i, coalescedContact); coalescedContactFound = true; break; } } // no match? add to list. if (!coalescedContactFound) { coalescedSimContacts.append(simContact); } } QList importContacts; foreach (QContact simContact, coalescedSimContacts) { // SIM imports have their name in the display label QContactDisplayLabel displayLabel = simContact.detail(); // first, remove any duplicate phone number details from the sim contact QList existingSimPhoneNumbers; foreach (QContactPhoneNumber phoneNumber, simContact.details()) { bool foundDuplicateNumber = false; for (int i = 0; i < existingSimPhoneNumbers.size(); ++i) { if (existingSimPhoneNumbers.at(i).number() == phoneNumber.number() && existingSimPhoneNumbers.at(i).contexts() == phoneNumber.contexts() && existingSimPhoneNumbers.at(i).subTypes() == phoneNumber.subTypes()) { // an exact duplicate of this number already exists in the sim contact. foundDuplicateNumber = true; simContact.removeDetail(&phoneNumber); break; } } if (!foundDuplicateNumber) { existingSimPhoneNumbers.append(phoneNumber); } } // then, determine whether this contact is already represented in the device phonebook QMap::iterator it = existingContacts.find(displayLabel.label().trimmed()); if (it != existingContacts.end()) { // Ensure this contact has the right phone numbers QContact &dbContact(*it); QList existingNumbers(dbContact.details()); bool modified = false; foreach (QContactPhoneNumber phoneNumber, simContact.details()) { bool foundExistingNumber = false; for (int i = 0; i < existingNumbers.size(); ++i) { if (existingNumbers.at(i).number() == phoneNumber.number() && existingNumbers.at(i).contexts() == phoneNumber.contexts() && existingNumbers.at(i).subTypes() == phoneNumber.subTypes()) { // this number was not modified. We don't need to change it. foundExistingNumber = true; existingNumbers.removeAt(i); break; } } if (!foundExistingNumber) { // this number is new, or modified. We need to change it. dbContact.saveDetail(&phoneNumber); modified = true; } } // Remove any obsolete numbers foreach (QContactPhoneNumber phoneNumber, existingNumbers) { dbContact.removeDetail(&phoneNumber); modified = true; } // Reactivate this contact if necessary if (!dbContact.details().isEmpty()) { QContactDeactivated deactivated = dbContact.detail(); dbContact.removeDetail(&deactivated); modified = true; } if (modified) { // Add the modified contact to the import set importContacts.append(dbContact); } existingContacts.erase(it); } else { // We need to import this contact simContact.setCollectionId(m_collection.id()); // Convert the display label to a nickname; display label is managed by the backend QContactNickname nickname = simContact.detail(); nickname.setNickname(displayLabel.label().trimmed()); simContact.saveDetail(&nickname); simContact.removeDetail(&displayLabel); importContacts.append(simContact); } } if (!importContacts.isEmpty()) { // Import any contacts which were modified or are not currently present if (!manager().saveContacts(&importContacts)) { qWarning() << "Error while saving imported sim contacts"; } } if (!existingContacts.isEmpty()) { // Remove any imported contacts no longer on the SIM QList obsoleteIds; foreach (const QContact &contact, existingContacts.values()) { obsoleteIds.append(contact.id()); } if (!manager().removeContacts(obsoleteIds)) { qWarning() << "Error while removing obsolete sim contacts"; } } } void CDSimModemData::voicemailConfigurationChanged() { if (!m_voicemailConf || !m_simManager.present()) { // Wait until SIM is present return; } const QString voicemailTarget(QString::fromLatin1("voicemail")); QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(m_collection.id()); QContactIntersectionFilter filter; filter << collectionFilter; filter << QContactTag::match(voicemailTarget); QContact voicemailContact; for (const QContact &contact : manager().contacts(filter)) { voicemailContact = contact; break; } // If there is a manually configured number, prefer that QString voicemailNumber(m_voicemailConf->value().toString()); if (voicemailNumber.isEmpty()) { // Otherwise use the number provided for message waiting voicemailNumber = m_messageWaiting.voicemailMailboxNumber(); } if (voicemailNumber.isEmpty()) { // Remove the voicemail contact if present if (!voicemailContact.id().isNull()) { if (!manager().removeContact(voicemailContact.id())) { qWarning() << "Unable to remove voicemail contact"; } } } else { // Add/update the voicemail contact if necessary QContactPhoneNumber number = voicemailContact.detail(); if (number.number() == voicemailNumber) { // Nothing to change return; } // Update the number number.setNumber(voicemailNumber); voicemailContact.saveDetail(&number); QContactNickname nickname = voicemailContact.detail(); if (nickname.isEmpty()) { //: Name for the contact representing the voicemail mailbox //% "Voicemail System" QString name(qtTrId("qtn_sim_voicemail_contact")); const QString simName(m_simInfo.serviceProviderName()); if (!simName.isEmpty()) { name.append(QString(QStringLiteral(" (%1)")).arg(simName)); } nickname.setNickname(name); voicemailContact.saveDetail(&nickname); } const QList tags = voicemailContact.details(); bool foundTag = false; for (const QContactTag &tag : tags) { if (tag.tag() == voicemailTarget) { foundTag = true; } } if (!foundTag) { QContactTag tag; tag.setTag(voicemailTarget); voicemailContact.saveDetail(&tag); } voicemailContact.setCollectionId(m_collection.id()); if (!manager().saveContact(&voicemailContact)) { qWarning() << "Unable to save voicemail contact"; } } } void CDSimModemData::updateVoicemailConfiguration() { QString variablePath(QString::fromLatin1("/sailfish/voicecall/voice_mailbox/")); variablePath.append(modemIdentifier()); if (!m_voicemailConf || m_voicemailConf->key() != variablePath) { delete m_voicemailConf; m_voicemailConf = new MGConfItem(variablePath); connect(m_voicemailConf, SIGNAL(valueChanged()), this, SLOT(voicemailConfigurationChanged())); voicemailConfigurationChanged(); } } void CDSimController::removeObsoleteSimCollections() { QList obsoleteCollectionIds; const QList collections = contactManager().collections(); QSet currentModemPaths; for (CDSimModemData *modem : m_modems.values()) { currentModemPaths.insert(modem->modemPath()); } QSet matchedModemPaths; for (const QContactCollection &collection : collections) { const QString modemPath = collection.extendedMetaData(CollectionKeyModemPath).toString(); if (modemPath.isEmpty()) { continue; } if (matchedModemPaths.contains(modemPath)) { obsoleteCollectionIds.append(collection.id()); } else { if (currentModemPaths.contains(modemPath)) { matchedModemPaths.insert(modemPath); } else { obsoleteCollectionIds.append(collection.id()); } } } if (!obsoleteCollectionIds.isEmpty() && !CDSimModemData::removeCollections(&contactManager(), obsoleteCollectionIds)) { qWarning() << "Error removing sim contacts for collections:" << obsoleteCollectionIds; } } void CDSimModemData::initCollection() { const int modemIndex = controller()->modemIndex(m_modemPath); QString icon; if (modemIndex == 0) { icon = QStringLiteral("image://theme/icon-m-sim-1"); } else if (modemIndex == 1) { icon = QStringLiteral("image://theme/icon-m-sim-2"); } QContactCollection collection; collection.setMetaData(QContactCollection::KeyName, QStringLiteral("SIM")); collection.setMetaData(QContactCollection::KeyDescription, QStringLiteral("Contacts stored on SIM card")); collection.setMetaData(QContactCollection::KeyColor, QStringLiteral("green")); collection.setMetaData(QContactCollection::KeySecondaryColor, QStringLiteral("lightgreen")); collection.setMetaData(QContactCollection::KeyImage, icon); collection.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_APPLICATIONNAME, qApp->applicationName()); collection.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_READONLY, true); collection.setExtendedMetaData(CollectionKeyModemPath, m_modemPath); collection.setExtendedMetaData(CollectionKeyModemIdentifier, modemIdentifier()); const QList collections = manager().collections(); for (const QContactCollection &c : collections) { const QString modemPath = c.extendedMetaData(CollectionKeyModemPath).toString(); const QString modemIdentifier = c.extendedMetaData(CollectionKeyModemIdentifier).toString(); if (modemPath == m_modemPath && modemIdentifier == this->modemIdentifier()) { qDebug() << "Found existing SIM collection" << collection.id() << "for path" << m_modemPath << "and modem id" << modemIdentifier; m_collection = c; break; } } if (m_collection.id().isNull()) { if (manager().saveCollection(&collection)) { m_collection = collection; qDebug() << "Created new SIM collection" << collection.id() << "for path" << m_modemPath << "and modem id" << modemIdentifier(); } else { qWarning() << "Unable to save SIM collection for path" << m_modemPath << "and modem id" << modemIdentifier() << "error is:" << manager().error(); } } } bool CDSimModemData::removeCollections(QContactManager *manager, const QList &collectionIds) { if (collectionIds.isEmpty()) { return true; } QContactManager &m = *manager; QtContactsSqliteExtensions::ContactManagerEngine *cme = QtContactsSqliteExtensions::contactManagerEngine(m); QContactManager::Error error = QContactManager::NoError; bool ret = cme->storeChanges(nullptr, nullptr, collectionIds, QtContactsSqliteExtensions::ContactManagerEngine::PreserveLocalChanges, true, &error); if (!ret) { qWarning() << "Error removing sim contacts from device storage, error was:" << error; return false; } return true; } // Instantiate the extension functions #include #include contactsd-1.4.14/plugins/sim/cdsimcontroller.h000066400000000000000000000071101440357512200214120ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2013-2019 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef CDSIMCONTROLLER_H #define CDSIMCONTROLLER_H #include #include #include #include #include #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE class CDSimModemData; class CDSimController : public QObject { Q_OBJECT public: explicit CDSimController(QObject *parent = 0, bool active = true); ~CDSimController(); QContactManager &contactManager(); QContactCollection contactCollection(const QString &modemPath) const; int modemIndex(const QString &modemPath) const; bool busy() const; Q_SIGNALS: void busyChanged(bool); public Q_SLOTS: void setModemPaths(const QStringList &paths); void transientImportConfigurationChanged(); void modemReadyChanged(bool ready); private: void updateBusy(); void timerEvent(QTimerEvent *event); void removeObsoleteSimCollections(); private: friend class CDSimModemData; friend class TestSimPlugin; QContactManager m_manager; bool m_transientImport; bool m_busy; bool m_active; MGConfItem m_transientImportConf; QBasicTimer m_readyTimer; QMap m_modems; QSet m_absentModemPaths; QStringList m_availableModems; }; class CDSimModemData : public QObject { Q_OBJECT public: CDSimModemData(CDSimController *controller, const QString &modemPath); ~CDSimModemData(); CDSimController *controller() const; QContactManager &manager() const; QString modemIdentifier() const; QString modemPath() const; QContactCollection contactCollection() const; QList fetchContacts() const; bool ready() const; void setReady(bool ready); void updateBusy(); static bool removeCollections(QContactManager *manager, const QList &collectionIds); Q_SIGNALS: void readyChanged(bool ready); public Q_SLOTS: void simStateChanged(); void vcardDataAvailable(const QString &vcardData); void vcardReadFailed(); void readerStateChanged(QVersitReader::State state); void voicemailConfigurationChanged(); void phonebookValidChanged(bool valid); public: void deactivateAllSimContacts(); void removeAllSimContacts(); void ensureSimContactsPresent(); void updateVoicemailConfiguration(); void performTransientImport(); void initCollection(); void timerEvent(QTimerEvent *event); QString m_modemPath; QOfonoSimManager m_simManager; QOfonoPhonebook m_phonebook; QOfonoMessageWaiting m_messageWaiting; QOfonoExtSimInfo m_simInfo; MGConfItem *m_voicemailConf; QVersitReader m_contactReader; QList m_simContacts; QContactCollection m_collection; QBasicTimer m_retryTimer; bool m_ready; int m_retries; }; #endif // CDSIMCONTROLLER_H contactsd-1.4.14/plugins/sim/cdsimplugin.cpp000066400000000000000000000031711440357512200210630ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2013-2014 Jolla Ltd. ** ** Contact: Matt Vogt ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include "cdsimcontroller.h" #include "cdsimplugin.h" #include "debug.h" #include using namespace Contactsd; CDSimPlugin::CDSimPlugin() : mController(0) { } CDSimPlugin::~CDSimPlugin() { } void CDSimPlugin::init() { qCDebug(lcContactsd) << "Initializing contactsd sim plugin"; mController = new CDSimController(this); // Associate CDSimController with the default modem // NB: Expect this to happen asynchronously, as a result of receiving // defaultModemChanged signal. QOfonoExtModemManager* modemManager = new QOfonoExtModemManager(mController); connect(modemManager, &QOfonoExtModemManager::availableModemsChanged, mController, &CDSimController::setModemPaths); } CDSimPlugin::MetaData CDSimPlugin::metaData() { MetaData data; data[metaDataKeyName] = QVariant(QLatin1String("sim")); data[metaDataKeyVersion] = QVariant(QLatin1String("0.1")); data[metaDataKeyComment] = QVariant(QLatin1String("contactsd sim plugin")); return data; } contactsd-1.4.14/plugins/sim/cdsimplugin.h000066400000000000000000000020541440357512200205270ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2013 Jolla Ltd. ** ** Contact: Matt Vogt ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef CDSIMPLUGIN_H #define CDSIMPLUGIN_H #include #include #include #include #include "base-plugin.h" class CDSimController; class CDSimPlugin : public Contactsd::BasePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.sim") public: CDSimPlugin(); ~CDSimPlugin(); void init(); MetaData metaData(); private: CDSimController *mController; }; #endif // CDSIMPLUGIN_H contactsd-1.4.14/plugins/sim/sim.pro000066400000000000000000000007121440357512200173510ustar00rootroot00000000000000include(../contacts-extensions.pri) include(../../lib.pri) TEMPLATE = lib QT -= gui QT += dbus CONFIG += plugin CONFIG += link_pkgconfig PKGCONFIG += mlite5 Qt5Contacts Qt5Versit qofono-qt5 qofonoext DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII HEADERS = \ cdsimcontroller.h \ cdsimplugin.h SOURCES = \ cdsimcontroller.cpp \ cdsimplugin.cpp TARGET = simplugin target.path = $$LIBDIR/contactsd-1.0/plugins INSTALLS += target contactsd-1.4.14/plugins/telepathy/000077500000000000000000000000001440357512200172465ustar00rootroot00000000000000contactsd-1.4.14/plugins/telepathy/cdtpaccount.cpp000066400000000000000000000352161440357512200222700ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include "cdtpaccount.h" #include "cdtpaccountcacheloader.h" #include "cdtpaccountcachewriter.h" #include "cdtpcontact.h" #include "debug.h" static const int DisconnectGracePeriod = 30 * 1000; // ms CDTpAccount::CDTpAccount(const Tp::AccountPtr &account, const QStringList &toAvoid, bool newAccount, QObject *parent) : QObject(parent), mAccount(account), mContactsToAvoid(toAvoid), mReady(false), mHasRoster(false), mNewAccount(newAccount), mImporting(false) { // connect all signals we care about, so we can signal that the account // changed accordingly connect(mAccount.data(), SIGNAL(displayNameChanged(const QString &)), SLOT(onAccountDisplayNameChanged())); connect(mAccount.data(), SIGNAL(nicknameChanged(const QString &)), SLOT(onAccountNicknameChanged())); connect(mAccount.data(), SIGNAL(currentPresenceChanged(const Tp::Presence &)), SLOT(onAccountCurrentPresenceChanged())); connect(mAccount.data(), SIGNAL(avatarChanged(const Tp::Avatar &)), SLOT(onAccountAvatarChanged())); connect(mAccount.data(), SIGNAL(connectionChanged(const Tp::ConnectionPtr &)), SLOT(onAccountConnectionChanged(const Tp::ConnectionPtr &))); connect(mAccount.data(), SIGNAL(stateChanged(bool)), SLOT(onAccountStateChanged())); if (not newAccount) { CDTpAccountCacheLoader(this).run(); } setConnection(mAccount->connection()); // Interface instance is owned and freed by mAccount mAccountStorage = mAccount->interface(); connect(mAccountStorage->requestPropertyStorageSpecificInformation(), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onRequestedStorageSpecificInformation(Tp::PendingOperation*))); mDisconnectTimeout.setInterval(DisconnectGracePeriod); mDisconnectTimeout.setSingleShot(true); connect(&mDisconnectTimeout, SIGNAL(timeout()), SLOT(onDisconnectTimeout())); } CDTpAccount::~CDTpAccount() { if (not mCurrentConnection.isNull()) { makeRosterCache(); } CDTpAccountCacheWriter(this).run(); } QList CDTpAccount::contacts() const { QList contacts; Q_FOREACH (const CDTpContactPtr &contactWrapper, mContacts) { if (contactWrapper->isVisible()) { contacts << contactWrapper; } } return contacts; } QHash CDTpAccount::rosterChanges() const { QHash changes; QSet cachedAddresses = mRosterCache.keys().toSet(); QSet currentAddresses; Q_FOREACH (CDTpContactPtr contact, contacts()) { const QString contactId = contact->contact()->id(); const QHash::ConstIterator it = mRosterCache.find(contactId); currentAddresses.insert(contactId); if (it == mRosterCache.constEnd()) { qDebug() << "No cached contact for" << contactId; changes.insert(contactId, CDTpContact::Added); continue; } changes.insert(contactId, contact->info().diff(*it)); } cachedAddresses.subtract(currentAddresses); // The remaining addresses after the subtraction are those which were in // the cache but are not in the contact list anymore Q_FOREACH (const QString &id, cachedAddresses) { changes.insert(id, CDTpContact::Deleted); } return changes; } void CDTpAccount::setContactsToAvoid(const QStringList &contactIds) { mContactsToAvoid = contactIds; Q_FOREACH (const QString &id, contactIds) { CDTpContactPtr contactWrapper = mContacts.take(id); if (contactWrapper) { contactWrapper->setRemoved(true); } } } void CDTpAccount::onAccountDisplayNameChanged() { Q_EMIT changed(CDTpAccountPtr(this), DisplayName); } void CDTpAccount::onAccountNicknameChanged() { Q_EMIT changed(CDTpAccountPtr(this), Nickname); } void CDTpAccount::onAccountCurrentPresenceChanged() { Q_EMIT changed(CDTpAccountPtr(this), Presence); } void CDTpAccount::onAccountAvatarChanged() { Q_EMIT changed(CDTpAccountPtr(this), Avatar); } void CDTpAccount::onAccountStateChanged() { Q_EMIT changed(CDTpAccountPtr(this), Enabled); if (!isEnabled()) { setConnection(Tp::ConnectionPtr()); mRosterCache.clear(); CDTpAccountCacheWriter(this).run(); } else { /* Since contacts got removed when we disabled the account, we need * to threat this account as new now that it is enabled again */ mNewAccount = true; } } void CDTpAccount::onAccountConnectionChanged(const Tp::ConnectionPtr &connection) { bool oldHasRoster = mHasRoster; // If we got disconnected, but not on user request, give a grace period // before actually updating Tracker, in case the connection comes back // quickly if (not connection.isNull()) { mDisconnectTimeout.stop(); } else if (not mCurrentConnection.isNull() && mCurrentConnection->status() != Tp::ConnectionStatusDisconnected) { qCDebug(lcContactsd) << "Lost connection for account" << mAccount->objectPath() << ", giving a grace period of" << DisconnectGracePeriod << "ms"; mDisconnectTimeout.start(); return; } setConnection(connection); if (oldHasRoster != mHasRoster) { Q_EMIT rosterChanged(CDTpAccountPtr(this)); mNewAccount = false; } } void CDTpAccount::setConnection(const Tp::ConnectionPtr &connection) { qCDebug(lcContactsd) << "Account" << mAccount->objectPath() << "- has connection:" << (connection != 0); if (not mCurrentConnection.isNull()) { makeRosterCache(); } mContacts.clear(); mHasRoster = false; mCurrentConnection = connection; if (connection) { /* If the connection has no roster, no need to bother with sync signals */ if (not (connection->actualFeatures().contains(Tp::Connection::FeatureRoster))) { qCDebug(lcContactsd) << "Account" << mAccount->objectPath() << "has no roster, not emitting sync signals"; return; } /* If this is the first time account gets a connection, report we are * importing. Note that connection could still be CONNECTING so it is * not sure we'll actually import anything. */ if (mNewAccount) { mImporting = true; Q_EMIT syncStarted(mAccount); } connect(connection->contactManager().data(), SIGNAL(stateChanged(Tp::ContactListState)), SLOT(onContactListStateChanged(Tp::ContactListState))); setContactManager(connection->contactManager()); } else if (mImporting) { /* We lost the connection while importing, probably failed to connect, * report 0 contacts retrieved */ emitSyncEnded(0, 0); } } void CDTpAccount::onDisconnectTimeout() { // Reset the current connection, and call onAccountConnectionChanged with // a null connection again to actually put the account offline mCurrentConnection = Tp::ConnectionPtr(); onAccountConnectionChanged(Tp::ConnectionPtr()); } void CDTpAccount::setReady() { mReady = true; emit readyChanged(); } void CDTpAccount::onContactListStateChanged(Tp::ContactListState state) { Q_UNUSED(state); /* NB#240743 - It can happen that tpqt4 still emits signals on the * ContactManager after connection has been removed from the account. * In that case Tp::Account::connectionChanged() should be emitted soon */ if (!mAccount->connection()) { return; } bool oldHasRoster = mHasRoster; setContactManager(mAccount->connection()->contactManager()); if (oldHasRoster != mHasRoster) { Q_EMIT rosterChanged(CDTpAccountPtr(this)); mNewAccount = false; } } void CDTpAccount::setContactManager(const Tp::ContactManagerPtr &contactManager) { if (contactManager->state() != Tp::ContactListStateSuccess) { return; } if (mHasRoster) { qCWarning(lcContactsd) << "Account" << mAccount->objectPath() << "- already received the roster"; return; } qCDebug(lcContactsd) << "Account" << mAccount->objectPath() << "- received the roster"; mHasRoster = true; connect(contactManager.data(), SIGNAL(allKnownContactsChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)), SLOT(onAllKnownContactsChanged(const Tp::Contacts &, const Tp::Contacts &))); Q_FOREACH (const Tp::ContactPtr &contact, contactManager->allKnownContacts()) { if (mContactsToAvoid.contains(contact->id())) { continue; } insertContact(contact); if (mNewAccount) { maybeRequestExtraInfo(contact); } } } void CDTpAccount::emitSyncEnded(int contactsAdded, int contactsRemoved) { if (mImporting) { mImporting = false; Q_EMIT syncEnded(mAccount, contactsAdded, contactsRemoved); } } QHash CDTpAccount::rosterCache() const { return mRosterCache; } void CDTpAccount::setRosterCache(const QHash &cache) { mRosterCache = cache; } void CDTpAccount::onAllKnownContactsChanged(const Tp::Contacts &contactsAdded, const Tp::Contacts &contactsRemoved) { qCDebug(lcContactsd) << "Account" << mAccount->objectPath() << "roster contacts changed:"; qCDebug(lcContactsd) << " " << contactsAdded.size() << "contacts added"; qCDebug(lcContactsd) << " " << contactsRemoved.size() << "contacts removed"; QList added; Q_FOREACH (const Tp::ContactPtr &contact, contactsAdded) { if (mContacts.contains(contact->id())) { qCWarning(lcContactsd) << "Internal error, contact was already in roster"; continue; } if (mContactsToAvoid.contains(contact->id())) { continue; } maybeRequestExtraInfo(contact); CDTpContactPtr contactWrapper = insertContact(contact); if (contactWrapper->isVisible()) { added << contactWrapper; } } QList removed; Q_FOREACH (const Tp::ContactPtr &contact, contactsRemoved) { const QString id(contact->id()); if (!mContacts.contains(id)) { qCWarning(lcContactsd) << "Internal error, contact is not in the internal list" "but was removed from roster"; continue; } CDTpContactPtr contactWrapper = mContacts.take(id); if (contactWrapper->isVisible()) { removed << contactWrapper; } contactWrapper->setRemoved(true); } if (!added.isEmpty() || !removed.isEmpty()) { Q_EMIT rosterUpdated(CDTpAccountPtr(this), added, removed); } } void CDTpAccount::onAccountContactChanged(CDTpContactPtr contactWrapper, CDTpContact::Changes changes) { if ((changes & CDTpContact::Visibility) != 0) { // Visibility of this contact changed. Transform this update operation // to an add/remove operation qCDebug(lcContactsd) << "Visibility changed for contact" << contactWrapper->contact()->id(); QList added; QList removed; if (contactWrapper->isVisible()) { added << contactWrapper; } else { removed << contactWrapper; } Q_EMIT rosterUpdated(CDTpAccountPtr(this), added, removed); return; } // Forward changes only if contact is visible if (contactWrapper->isVisible()) { Q_EMIT rosterContactChanged(contactWrapper, changes); } } CDTpContactPtr CDTpAccount::insertContact(const Tp::ContactPtr &contact) { qCDebug(lcContactsd) << " creating wrapper for contact" << contact->id(); CDTpContactPtr contactWrapper = CDTpContactPtr(new CDTpContact(contact, this)); connect(contactWrapper.data(), SIGNAL(changed(CDTpContactPtr, CDTpContact::Changes)), SLOT(onAccountContactChanged(CDTpContactPtr, CDTpContact::Changes))); mContacts.insert(contact->id(), contactWrapper); return contactWrapper; } void CDTpAccount::maybeRequestExtraInfo(Tp::ContactPtr contact) { if (!contact->isAvatarTokenKnown()) { qCDebug(lcContactsd) << contact->id() << "first seen: request avatar"; contact->requestAvatarData(); } if (!contact->isContactInfoKnown()) { qCDebug(lcContactsd) << contact->id() << "first seen: refresh ContactInfo"; contact->refreshInfo(); } } void CDTpAccount::makeRosterCache() { mRosterCache.clear(); Q_FOREACH (const CDTpContactPtr &ptr, mContacts) { mRosterCache.insert(ptr->contact()->id(), ptr->info()); } } CDTpContactPtr CDTpAccount::contact(const QString &id) const { return mContacts.value(id); } QVariantMap CDTpAccount::storageInfo() const { return mStorageInfo; } void CDTpAccount::onRequestedStorageSpecificInformation(Tp::PendingOperation *op) { if (!op->isValid()) { qCDebug(lcContactsd) << "Cannot get storage specific information for account" << mAccount->objectPath(); mStorageInfo.clear(); } else { QDBusArgument arg = static_cast(op)->result().value(); mStorageInfo = qdbus_cast(arg); } if (isReady()) Q_EMIT changed(CDTpAccountPtr(this), StorageInfo); else setReady(); } contactsd-1.4.14/plugins/telepathy/cdtpaccount.h000066400000000000000000000112561440357512200217330ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPACCOUNT_H #define CDTPACCOUNT_H #include #include #include #include #include #include #include #include "types.h" #include "cdtpcontact.h" class CDTpAccount : public QObject, public Tp::RefCounted { Q_OBJECT public: enum Change { DisplayName = (1 << 0), Nickname = (1 << 1), Presence = (1 << 2), Avatar = (1 << 3), Enabled = (1 << 4), StorageInfo = (1 << 5), All = (1 << 6) -1 }; Q_DECLARE_FLAGS(Changes, Change) CDTpAccount(const Tp::AccountPtr &account, const QStringList &contactsToAvoid = QStringList(), bool newAccount = false, QObject *parent = 0); ~CDTpAccount(); Tp::AccountPtr account() const { return mAccount; } QList contacts() const; QHash rosterChanges() const; CDTpContactPtr contact(const QString &id) const; bool hasRoster() const { return mHasRoster; }; bool isNewAccount() const { return mNewAccount; }; bool isEnabled() const { return mAccount->isEnabled(); }; QStringList contactsToAvoid() const { return mContactsToAvoid; } void setContactsToAvoid(const QStringList &contactIds); void emitSyncEnded(int contactsAdded, int contactsRemoved); QHash rosterCache() const; void setRosterCache(const QHash &rosterCache); bool isReady() const { return mReady; } QVariantMap storageInfo() const; Q_SIGNALS: void changed(CDTpAccountPtr accountWrapper, CDTpAccount::Changes changes); void rosterChanged(CDTpAccountPtr accountWrapper); void rosterUpdated(CDTpAccountPtr acconutWrapper, const QList &contactsAdded, const QList &contactsRemoved); void rosterContactChanged(CDTpContactPtr contactWrapper, CDTpContact::Changes changes); void syncStarted(Tp::AccountPtr account); void syncEnded(Tp::AccountPtr account, int contactsAdded, int contactsRemoved); void readyChanged(); private Q_SLOTS: void onAccountDisplayNameChanged(); void onAccountNicknameChanged(); void onAccountCurrentPresenceChanged(); void onAccountAvatarChanged(); void onAccountStateChanged(); void onAccountConnectionChanged(const Tp::ConnectionPtr &connection); void onContactListStateChanged(Tp::ContactListState); void onAccountContactChanged(CDTpContactPtr contactWrapper, CDTpContact::Changes changes); void onAllKnownContactsChanged(const Tp::Contacts &contactsAdded, const Tp::Contacts &contactsRemoved); void onDisconnectTimeout(); void onRequestedStorageSpecificInformation(Tp::PendingOperation *op); private: void setConnection(const Tp::ConnectionPtr &connection); void setContactManager(const Tp::ContactManagerPtr &contactManager); CDTpContactPtr insertContact(const Tp::ContactPtr &contact); void maybeRequestExtraInfo(Tp::ContactPtr contact); void makeRosterCache(); void setReady(); private: Tp::AccountPtr mAccount; Tp::ConnectionPtr mCurrentConnection; Tp::Client::AccountInterfaceStorageInterface *mAccountStorage; QVariantMap mStorageInfo; QHash mContacts; QHash mRosterCache; QStringList mContactsToAvoid; QTimer mDisconnectTimeout; bool mReady; bool mHasRoster; bool mNewAccount; bool mImporting; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CDTpAccount::Changes) #endif // CDTPACCOUNT_H contactsd-1.4.14/plugins/telepathy/cdtpaccountcache.h000066400000000000000000000027051440357512200227160ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPACCOUNTCACHE_H #define CDTPACCOUNTCACHE_H #include #include "cdtpaccount.h" #include "base-plugin.h" namespace CDTpAccountCache { static int Version = 1; static QString cacheFilePath(const CDTpAccount *account) { return Contactsd::BasePlugin::cacheDir().absoluteFilePath(account->account()->objectPath().replace(QLatin1Char('/'), QLatin1Char('_'))); } } #endif // CDTPACCOUNTCACHE_H contactsd-1.4.14/plugins/telepathy/cdtpaccountcacheloader.cpp000066400000000000000000000050001440357512200244270ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "cdtpaccountcacheloader.h" #include "cdtpaccountcache.h" #include using namespace Contactsd; CDTpAccountCacheLoader::CDTpAccountCacheLoader(CDTpAccount *account, QObject *parent) : QObject(parent) , mAccount(account) { } void CDTpAccountCacheLoader::run() { const QString accountPath = mAccount->account()->objectPath(); QFile cacheFile(CDTpAccountCache::cacheFilePath(mAccount)); if (not cacheFile.exists()) { qCDebug(lcContactsd) << Q_FUNC_INFO << "Account" << accountPath << "has no cache file"; return; } if (not cacheFile.open(QIODevice::ReadOnly)) { qCWarning(lcContactsd) << Q_FUNC_INFO << "Can't open" << cacheFile.fileName() << "for reading:" << cacheFile.error(); return; } QByteArray cacheData = cacheFile.readAll(); cacheFile.close(); QDataStream stream(cacheData); if (stream.atEnd()) { qCDebug(lcContactsd) << Q_FUNC_INFO << "Empty cache file" << cacheFile.fileName(); cacheFile.remove(); return; } int cacheVersion; stream >> cacheVersion; if (cacheVersion != CDTpAccountCache::Version) { qCWarning(lcContactsd) << "Wrong cache version for file" << cacheFile.fileName(); cacheFile.remove(); } QHash cache; stream >> cache; mAccount->setRosterCache(cache); qCDebug(lcContactsd) << "Loaded" << cache.size() << "contacts from cache for account" << accountPath; } contactsd-1.4.14/plugins/telepathy/cdtpaccountcacheloader.h000066400000000000000000000025221440357512200241020ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPACCOUNTCACHELOADER_H #define CDTPACCOUNTCACHELOADER_H #include "cdtpaccount.h" class CDTpAccountCacheLoader : public QObject { public: CDTpAccountCacheLoader(CDTpAccount *account, QObject *parent = 0); void run(); private: CDTpAccount *mAccount; }; #endif // CDTPACCOUNTCACHELOADER_H contactsd-1.4.14/plugins/telepathy/cdtpaccountcachewriter.cpp000066400000000000000000000063561440357512200245140ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "cdtpaccountcachewriter.h" #include #include #include #include #include #include "cdtpaccountcache.h" using namespace Contactsd; /////////////////////////////////////////////////////////////////////////////// CDTpAccountCacheWriter::CDTpAccountCacheWriter(const CDTpAccount *account, QObject *parent) : QObject(parent) , mAccount(account) { } void CDTpAccountCacheWriter::run() { const QString accountPath = mAccount->account()->objectPath(); const QString rosterFileName = CDTpAccountCache::cacheFilePath(mAccount); const QHash cache = mAccount->rosterCache(); if (cache.isEmpty()) { QFile(rosterFileName).remove(); return; } QTemporaryFile tempFile(rosterFileName); tempFile.setAutoRemove(false); if (not tempFile.open()) { qCWarning(lcContactsd) << "Could not open file" << tempFile.fileName() << "for writing:" << tempFile.errorString(); tempFile.setAutoRemove(true); return; } QByteArray data; QBuffer buffer(&data); buffer.open(QIODevice::WriteOnly); QDataStream stream(&buffer); stream << CDTpAccountCache::Version; stream << cache; buffer.close(); if (tempFile.write(data) != data.size()) { qCWarning(lcContactsd) << "Could not write roster cache for account" << accountPath << ":" << tempFile.errorString(); tempFile.setAutoRemove(true); return; } if (not tempFile.flush() || (::fsync(tempFile.handle()) != 0) || (tempFile.close(), false)) { qCWarning(lcContactsd) << "Could not finalize roster cache for account" << accountPath << ":" << tempFile.errorString(); tempFile.setAutoRemove(true); return; } if (::rename(tempFile.fileName().toLocal8Bit(), rosterFileName.toLocal8Bit()) != 0) { qCWarning(lcContactsd) << "Could not write roster cache for account" << accountPath << ":" << strerror(errno); tempFile.setAutoRemove(true); return; } qCDebug(lcContactsd) << "Wrote" << mAccount->rosterCache().size() << "contacts to cache for account" << accountPath; } contactsd-1.4.14/plugins/telepathy/cdtpaccountcachewriter.h000066400000000000000000000025721440357512200241550ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPACCOUNTCACHEWRITER_H #define CDTPACCOUNTCACHEWRITER_H #include "cdtpaccount.h" class CDTpAccountCacheWriter : public QObject { public: CDTpAccountCacheWriter(const CDTpAccount *account, QObject *parent = 0); void run(); private: const CDTpAccount *mAccount; const QString mFilename; }; #endif // CDTPACCOUNTCACHEWRITER_H contactsd-1.4.14/plugins/telepathy/cdtpavatarupdate.cpp000066400000000000000000000143441440357512200233140ustar00rootroot00000000000000/********************************************************************************* ** This file is part of QtContacts tracker storage plugin ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. *********************************************************************************/ #include "cdtpavatarupdate.h" #include "cdtpplugin.h" #include "debug.h" using namespace Contactsd; const QString CDTpAvatarUpdate::Large = QLatin1String("large"); const QString CDTpAvatarUpdate::Square = QLatin1String("square"); void CDTpAvatarUpdate::updateContact(CDTpContact *contactWrapper, QNetworkReply *networkReply, const QString &filename, const QString &avatarType) { (void) new CDTpAvatarUpdate(networkReply, contactWrapper, filename, avatarType); } CDTpAvatarUpdate::CDTpAvatarUpdate(QNetworkReply *networkReply, CDTpContact *contactWrapper, const QString &filename, const QString &avatarType) : QObject() , mNetworkReply(0) , mContactWrapper(contactWrapper) , mFilename(filename) , mAvatarType(avatarType) { setNetworkReply(networkReply); } void CDTpAvatarUpdate::setNetworkReply(QNetworkReply *networkReply) { if (mNetworkReply) { mNetworkReply->disconnect(this); mNetworkReply->deleteLater(); } mNetworkReply = networkReply; if (mNetworkReply) { if (mNetworkReply->isRunning()) { connect(mNetworkReply, SIGNAL(finished()), this, SLOT(onRequestDone())); connect(mNetworkReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestDone())); } else { onRequestDone(); } } else { // This operation is complete this->deleteLater(); } } void CDTpAvatarUpdate::onRequestDone() { QNetworkReply *newReply = 0; if (mNetworkReply && mNetworkReply->error() == QNetworkReply::NoError) { newReply = updateContactAvatar(); } setNetworkReply(newReply); } QString CDTpAvatarUpdate::writeAvatarFile(QFile &avatarFile, const QDir &cacheDir) { if (not cacheDir.exists() && not QDir::root().mkpath(cacheDir.absolutePath())) { qCWarning(lcContactsd) << "Could not create large avatar cache dir:" << cacheDir.path(); return QString(); } QTemporaryFile tempFile(cacheDir.absoluteFilePath(QLatin1String("pinkpony"))); const QByteArray data = mNetworkReply->readAll(); if (tempFile.open() && data.count() == tempFile.write(data)) { tempFile.close(); if (avatarFile.exists()) { avatarFile.remove(); } if (tempFile.rename(avatarFile.fileName())) { tempFile.setAutoRemove(false); return avatarFile.fileName(); } } return QString(); } static bool acceptFileSize(qint64 actualFileSize, qint64 expectedFileSize) { if (expectedFileSize > 0) { return (actualFileSize == expectedFileSize); } return actualFileSize > 0; } QNetworkReply *CDTpAvatarUpdate::updateContactAvatar() { // Build filename from the image URL's SHA1 hash. const QUrl redirectionTarget = mNetworkReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); const QString avatarUrl = (not redirectionTarget.isEmpty() ? mNetworkReply->url().resolved(redirectionTarget) : mNetworkReply->url()).toString(); QString filename(mFilename); if (filename.isEmpty()) { QByteArray avatarHash = QCryptographicHash::hash(avatarUrl.toUtf8(), QCryptographicHash::Sha1); filename = QString::fromLatin1(avatarHash.toHex()); } const QDir cacheDir(CDTpPlugin::cacheFileName(QLatin1String("avatars"))); QFile avatarFile(cacheDir.absoluteFilePath(filename)); // Check for existing avatar file and its size to see if we need to fetch from network. const qint64 contentLength = mNetworkReply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); QString avatarPath; if (avatarFile.exists() && acceptFileSize(avatarFile.size(), contentLength)) { // Seems we can reuse the existing avatar file. avatarPath = avatarFile.fileName(); } else { // Follow redirections as done by Facebook's graph API. if (not redirectionTarget.isEmpty()) { return mNetworkReply->manager()->get(QNetworkRequest(redirectionTarget)); } // Facebook delivers a distinct gif image if no avatar is set. Ignore that bugger. const QString contentType = mNetworkReply->header(QNetworkRequest::ContentTypeHeader).toString(); static const QLatin1String contentTypeImageGif = QLatin1String("image/gif"); static const QLatin1String contentTypeImage = QLatin1String("image/"); if (contentType.startsWith(contentTypeImage) && contentType != contentTypeImageGif) { avatarPath = writeAvatarFile(avatarFile, cacheDir); } } // Update the contact if a new avatar is available. if (not avatarPath.isEmpty() && not mContactWrapper.isNull()) { if (mAvatarType == Square) { mContactWrapper->setSquareAvatarPath(avatarPath); } else if (mAvatarType == Large) { mContactWrapper->setLargeAvatarPath(avatarPath); } } // No further work to do return 0; } contactsd-1.4.14/plugins/telepathy/cdtpavatarupdate.h000066400000000000000000000042531440357512200227570ustar00rootroot00000000000000/********************************************************************************* ** This file is part of QtContacts tracker storage plugin ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. *********************************************************************************/ #ifndef CDTPAVATARREQUEST_H #define CDTPAVATARREQUEST_H #include #include #include #include "cdtpcontact.h" class CDTpAvatarUpdate : public QObject { Q_OBJECT public: static const QString Large; static const QString Square; static void updateContact(CDTpContact *contactWrapper, QNetworkReply *networkReply, const QString &filename, const QString &avatarType = Large); private slots: void onRequestDone(); private: CDTpAvatarUpdate(QNetworkReply *networkReply, CDTpContact *contactWrapper, const QString &filename, const QString &avatarType); void setNetworkReply(QNetworkReply *networkReply); QString writeAvatarFile(QFile &avatarFile, const QDir &cacheDir); QNetworkReply *updateContactAvatar(); private: QPointer mNetworkReply; QPointer mContactWrapper; const QString mFilename; const QString mAvatarType; }; #endif // CDTPAVATARREQUEST_H contactsd-1.4.14/plugins/telepathy/cdtpcontact.cpp000066400000000000000000000301411440357512200222570ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include "cdtpaccount.h" #include "cdtpcontact.h" #include "debug.h" /////////////////////////////////////////////////////////////////////////////// class CDTpContact::InfoData : public QSharedData { public: InfoData(); QString alias; Tp::Presence presence; int capabilities; QString avatarPath; QString largeAvatarPath; QString squareAvatarPath; Tp::Contact::PresenceState subscriptionState; Tp::Contact::PresenceState publishState; Tp::ContactInfoFieldList infoFields; bool isSubscriptionStateKnown : 1; bool isPublishStateKnown : 1; bool isContactInfoKnown : 1; bool isVisible : 1; }; CDTpContact::InfoData::InfoData() : isSubscriptionStateKnown(false) , isPublishStateKnown(false) , isContactInfoKnown(false) , isVisible(false) {} /////////////////////////////////////////////////////////////////////////////// static CDTpContact::Info::Capabilities makeInfoCaps(const Tp::CapabilitiesBase &capabilities) { CDTpContact::Info::Capabilities caps = 0; if (capabilities.textChats()) { caps |= CDTpContact::Info::TextChats; } if (capabilities.streamedMediaCalls()) { caps |= CDTpContact::Info::StreamedMediaCalls; } if (capabilities.streamedMediaAudioCalls()) { caps |= CDTpContact::Info::StreamedMediaAudioCalls; } if (capabilities.streamedMediaVideoCalls()) { caps |= CDTpContact::Info::StreamedMediaAudioVideoCalls; } if (capabilities.upgradingStreamedMediaCalls()) { caps |= CDTpContact::Info::UpgradingStreamMediaCalls; } if (capabilities.fileTransfers()) { caps |= CDTpContact::Info::FileTransfers; } return caps; } CDTpContact::Info::Info() :d(new CDTpContact::InfoData) { } CDTpContact::Info::Info(const CDTpContact *contact) : d(new CDTpContact::InfoData) { const Tp::ContactPtr c = contact->contact(); d->alias = c->alias(); d->presence = c->presence(); d->capabilities = makeInfoCaps(c->capabilities()); d->avatarPath = c->avatarData().fileName; d->subscriptionState = c->subscriptionState(); d->publishState = c->publishState(); d->infoFields = c->infoFields().allFields(); d->isSubscriptionStateKnown = c->isSubscriptionStateKnown(); d->isPublishStateKnown = c->isPublishStateKnown(); d->isContactInfoKnown = c->isContactInfoKnown(); d->isVisible = contact->isVisible(); } CDTpContact::Info::Info(const CDTpContact::Info &other) : d(other.d) { } CDTpContact::Info& CDTpContact::Info::operator=(const CDTpContact::Info &other) { return (d = other.d, *this); } CDTpContact::Info::~Info() { } CDTpContact::Changes CDTpContact::Info::diff(const CDTpContact::Info &other) const { Changes changes = 0; if (d->alias != other.d->alias) changes |= CDTpContact::Alias; // We only compare the relevant fields (status is not saved in Tracker, and isValid is irrelevant) if (d->presence.type() != other.d->presence.type() || d->presence.statusMessage() != other.d->presence.statusMessage()) changes |= CDTpContact::Presence; if (d->capabilities != other.d->capabilities) changes |= CDTpContact::Capabilities; if (d->avatarPath != other.d->avatarPath) changes |= CDTpContact::DefaultAvatar; if (d->largeAvatarPath != other.d->largeAvatarPath) changes |= CDTpContact::LargeAvatar; if (d->squareAvatarPath != other.d->squareAvatarPath) changes |= CDTpContact::SquareAvatar; if (d->isSubscriptionStateKnown != other.d->isSubscriptionStateKnown || d->isPublishStateKnown != other.d->isPublishStateKnown || d->subscriptionState != other.d->subscriptionState || d->publishState != other.d->publishState) changes |= CDTpContact::Authorization; if (other.d->isContactInfoKnown && d->infoFields != other.d->infoFields) changes |= CDTpContact::Information; if (d->isVisible != other.d->isVisible) changes |= CDTpContact::Visibility; return changes; } /////////////////////////////////////////////////////////////////////////////// CDTpContact::CDTpContact(Tp::ContactPtr contact, CDTpAccount *accountWrapper) : QObject(), mContact(contact), mAccountWrapper(accountWrapper), mRemoved(false), mQueuedChanges(0) { mQueuedChangesTimer.setInterval(0); mQueuedChangesTimer.setSingleShot(true); connect(&mQueuedChangesTimer, SIGNAL(timeout()), SLOT(onQueuedChangesTimeout())); updateVisibility(); connect(contact.data(), SIGNAL(aliasChanged(const QString &)), SLOT(onContactAliasChanged())); connect(contact.data(), SIGNAL(presenceChanged(const Tp::Presence &)), SLOT(onContactPresenceChanged())); connect(contact.data(), SIGNAL(capabilitiesChanged(const Tp::ContactCapabilities &)), SLOT(onContactCapabilitiesChanged())); connect(contact.data(), SIGNAL(avatarDataChanged(const Tp::AvatarData &)), SLOT(onContactAvatarDataChanged())); connect(contact.data(), SIGNAL(subscriptionStateChanged(Tp::Contact::PresenceState)), SLOT(onContactAuthorizationChanged())); connect(contact.data(), SIGNAL(publishStateChanged(Tp::Contact::PresenceState, const QString &)), SLOT(onContactAuthorizationChanged())); connect(contact.data(), SIGNAL(infoFieldsChanged(const Tp::Contact::InfoFields &)), SLOT(onContactInfoChanged())); connect(contact.data(), SIGNAL(blockStatusChanged(bool)), SLOT(onBlockStatusChanged())); } CDTpContact::~CDTpContact() { } CDTpAccountPtr CDTpContact::accountWrapper() const { return CDTpAccountPtr(mAccountWrapper.data()); } bool CDTpContact::isAvatarKnown() const { if (!mContact->isAvatarTokenKnown()) { return false; } /* If we have a token but not an avatar filename, that probably means the * avatar data is being requested and we'll get an update later. */ if (!mContact->avatarToken().isEmpty() && mContact->avatarData().fileName.isEmpty()) { return false; } return true; } bool CDTpContact::isInformationKnown() const { return mContact->isContactInfoKnown(); } CDTpContact::Info CDTpContact::info() const { return Info(this); } void CDTpContact::setLargeAvatarPath(const QString &path) { mLargeAvatarPath = path; emitChanged(LargeAvatar); } void CDTpContact::setSquareAvatarPath(const QString &path) { mSquareAvatarPath = path; emitChanged(SquareAvatar); } void CDTpContact::onContactAliasChanged() { emitChanged(Alias); } void CDTpContact::onContactPresenceChanged() { emitChanged(Presence); } void CDTpContact::onContactCapabilitiesChanged() { emitChanged(Capabilities); } void CDTpContact::onContactAvatarDataChanged() { emitChanged(DefaultAvatar); } void CDTpContact::onContactAuthorizationChanged() { emitChanged(Authorization); } void CDTpContact::onContactInfoChanged() { emitChanged(Information); } void CDTpContact::onBlockStatusChanged() { emitChanged(Blocked); } void CDTpContact::emitChanged(CDTpContact::Changes changes) { mQueuedChanges |= changes; if (not mQueuedChangesTimer.isActive()) { mQueuedChangesTimer.start(); } } void CDTpContact::onQueuedChangesTimeout() { // Check if this change also modified the visibility bool wasVisible = mVisible; updateVisibility(); if (mVisible != wasVisible) { mQueuedChanges |= Visibility; } Q_EMIT changed(CDTpContactPtr(this), mQueuedChanges); mQueuedChanges = 0; } void CDTpContact::updateVisibility() { /* Don't import contacts blocked, removed or incoming auth requests (because * user never asked for them). Note that we still import contacts that have * publishState==subscribeState==No, because that case happens if we sent an * auth request but it got rejected. In the case we received an auth request * and we rejected it, with our implementation, the contact is completely * removed from the roster so it won't appear here at all (some other IM * clients could still keep the contact in the roster with * publishState==subscribeState==No, but that's really corner case so we * don't care). */ mVisible = !mRemoved && !mContact->isBlocked() && (mContact->publishState() != Tp::Contact::PresenceStateAsk || mContact->subscriptionState() != Tp::Contact::PresenceStateNo); } void CDTpContact::setRemoved(bool value) { mRemoved = value; updateVisibility(); } QDataStream& operator<<(QDataStream &stream, const Tp::Presence &presence) { stream << presence.type(); stream << presence.status(); stream << presence.statusMessage(); return stream; } QDataStream& operator<<(QDataStream &stream, const Tp::ContactInfoField &field) { stream << field.fieldName; stream << field.parameters; stream << field.fieldValue; return stream; } QDataStream& operator<<(QDataStream &stream, const CDTpContact::Info &info) { stream << info.d->alias; stream << info.d->presence; stream << info.d->capabilities; stream << info.d->avatarPath; stream << info.d->largeAvatarPath; stream << info.d->squareAvatarPath; stream << info.d->isSubscriptionStateKnown; stream << uint(info.d->subscriptionState); stream << info.d->isPublishStateKnown; stream << uint(info.d->publishState); stream << info.d->isContactInfoKnown; stream << info.d->infoFields; stream << info.d->isVisible; return stream; } QDataStream& operator>>(QDataStream &stream, Tp::Presence &presence) { uint type; QString status; QString statusMessage; stream >> type; stream >> status; stream >> statusMessage; presence.setStatus((Tp::ConnectionPresenceType)type, status, statusMessage); return stream; } QDataStream& operator>>(QDataStream &stream, Tp::ContactInfoField &field) { stream >> field.fieldName; stream >> field.parameters; stream >> field.fieldValue; return stream; } static QDataStream& operator>>(QDataStream &stream, Tp::Contact::PresenceState &presenceState) { uint value; stream >> value; presenceState = Tp::Contact::PresenceState(value); return stream; } QDataStream& operator>>(QDataStream &stream, CDTpContact::Info &info) { bool isSubscriptionStateKnown; bool isPublishStateKnown; bool isContactInfoKnown; bool isVisible; stream >> info.d->alias; stream >> info.d->presence; stream >> info.d->capabilities; stream >> info.d->avatarPath; stream >> info.d->largeAvatarPath; stream >> info.d->squareAvatarPath; stream >> isSubscriptionStateKnown; stream >> info.d->subscriptionState; stream >> isPublishStateKnown; stream >> info.d->publishState; stream >> isContactInfoKnown; stream >> info.d->infoFields; stream >> isVisible; info.d->isSubscriptionStateKnown = isSubscriptionStateKnown; info.d->isPublishStateKnown = isPublishStateKnown; info.d->isContactInfoKnown = isContactInfoKnown; info.d->isVisible = isVisible; return stream; } contactsd-1.4.14/plugins/telepathy/cdtpcontact.h000066400000000000000000000115561440357512200217350ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPCONTACT_H #define CDTPCONTACT_H #include #include #include #include #include "types.h" class CDTpContact : public QObject, public Tp::RefCounted { Q_OBJECT public: enum Change { Alias = (1 << 0), Presence = (1 << 1), Capabilities = (1 << 2), DefaultAvatar = (1 << 3), Authorization = (1 << 4), Information = (1 << 5), Blocked = (1 << 6), Visibility = (1 << 7), LargeAvatar = (1 << 8), SquareAvatar = (1 << 9), All = (1 << 10) - 1, // Special values Avatar = (DefaultAvatar | LargeAvatar | SquareAvatar), Added = (1 << 20) - 1, Deleted = (1 << 21) }; Q_DECLARE_FLAGS(Changes, Change) class InfoData; class Info { public: enum Capability { TextChats = (1 << 0), StreamedMediaCalls = (1 << 1), StreamedMediaAudioCalls = (1 << 2), StreamedMediaAudioVideoCalls = (1 << 3), UpgradingStreamMediaCalls = (1 << 4), FileTransfers = (1 << 5), StreamTubes = (1 << 6), DBusTubes = (1 << 7) }; // Q_DECLARE_FLAGS does not work for nested classes typedef int Capabilities; public: Info(); Info(const CDTpContact *contact); Info(const Info &other); Info& operator=(const Info &other); ~Info(); public: CDTpContact::Changes diff(const CDTpContact::Info &other) const; private: friend QDataStream& operator<<(QDataStream &stream, const CDTpContact::Info &info); friend QDataStream& operator>>(QDataStream &stream, CDTpContact::Info &info); QSharedDataPointer d; }; CDTpContact(Tp::ContactPtr contact, CDTpAccount *accountWrapper); ~CDTpContact(); Tp::ContactPtr contact() const { return mContact; } CDTpAccountPtr accountWrapper() const; bool isRemoved() const { return mRemoved; } bool isVisible() const { return mVisible; } bool isAvatarKnown() const; bool isInformationKnown() const; Info info() const; void setLargeAvatarPath(const QString &path); const QString & largeAvatarPath() const { return mLargeAvatarPath; } void setSquareAvatarPath(const QString &path); const QString & squareAvatarPath() const { return mSquareAvatarPath; } Q_SIGNALS: void changed(CDTpContactPtr contact, CDTpContact::Changes changes); private Q_SLOTS: void onContactAliasChanged(); void onContactPresenceChanged(); void onContactCapabilitiesChanged(); void onContactAvatarDataChanged(); void onContactAuthorizationChanged(); void onContactInfoChanged(); void onBlockStatusChanged(); void onQueuedChangesTimeout(); private: void emitChanged(CDTpContact::Changes changes); void updateVisibility(); void setRemoved(bool value); friend class CDTpAccount; Tp::ContactPtr mContact; QPointer mAccountWrapper; QString mLargeAvatarPath; QString mSquareAvatarPath; bool mRemoved; bool mVisible; Changes mQueuedChanges; QTimer mQueuedChangesTimer; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CDTpContact::Changes) QDataStream& operator<<(QDataStream &stream, const Tp::Presence &presence); QDataStream& operator<<(QDataStream &stream, const Tp::ContactInfoField &field); QDataStream& operator<<(QDataStream &stream, const CDTpContact::Info &info); QDataStream& operator>>(QDataStream &stream, Tp::Presence &presence); QDataStream& operator>>(QDataStream &stream, Tp::ContactInfoField &field); QDataStream& operator>>(QDataStream &stream, CDTpContact::Info &info); #endif // CDTPCONTACT_H contactsd-1.4.14/plugins/telepathy/cdtpcontroller.cpp000066400000000000000000000462461440357512200230240ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include #include #include #include "buddymanagementadaptor.h" #include "cdtpcontroller.h" #include "debug.h" const QLatin1String DBusObjectPath("/telepathy"); static const QString offlineRemovals = QString::fromLatin1("OfflineRemovals"); static const QString offlineInvitations = QString::fromLatin1("OfflineInvitations"); CDTpController::CDTpController(QObject *parent) : QObject(parent) , mOfflineRosterBuffer(QSettings::IniFormat, QSettings::UserScope, QLatin1String("Nokia"), QLatin1String("Contactsd")) { connect(&mStorage, SIGNAL(error(int, const QString &)), SIGNAL(error(int, const QString &))); qCDebug(lcContactsd) << "Creating account manager"; const QDBusConnection &bus = QDBusConnection::sessionBus(); Tp::AccountFactoryPtr accountFactory = Tp::AccountFactory::create(bus, Tp::Features() << Tp::Account::FeatureCore << Tp::Account::FeatureAvatar << Tp::Account::FeatureCapabilities); Tp::ConnectionFactoryPtr connectionFactory = Tp::ConnectionFactory::create(bus, Tp::Features() << Tp::Connection::FeatureConnected << Tp::Connection::FeatureCore << Tp::Connection::FeatureRoster); Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(bus); Tp::ContactFactoryPtr contactFactory = Tp::ContactFactory::create( Tp::Features() << Tp::Contact::FeatureAlias << Tp::Contact::FeatureAvatarToken << Tp::Contact::FeatureAvatarData << Tp::Contact::FeatureSimplePresence << Tp::Contact::FeatureInfo << Tp::Contact::FeatureLocation << Tp::Contact::FeatureCapabilities); mAM = Tp::AccountManager::create(bus, accountFactory, connectionFactory, channelFactory, contactFactory); // Wait for AM to become ready connect(mAM->becomeReady(Tp::AccountManager::FeatureCore), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountManagerReady(Tp::PendingOperation*))); if (registerDBusObject()) { (void) new BuddyManagementAdaptor(this); } } CDTpController::~CDTpController() { QDBusConnection::sessionBus().unregisterObject(DBusObjectPath); } void CDTpController::onAccountManagerReady(Tp::PendingOperation *op) { if (op->isError()) { qCDebug(lcContactsd) << "Could not make account manager ready:" << op->errorName() << "-" << op->errorMessage(); return; } qCDebug(lcContactsd) << "Account manager ready"; Tp::AccountPropertyFilterPtr propFilter; Tp::AccountFilterPtr notFilter; QList filters; propFilter = Tp::AccountPropertyFilter::create(); propFilter->addProperty(QString::fromLatin1("valid"), true); filters << propFilter; propFilter = Tp::AccountPropertyFilter::create(); propFilter->addProperty(QString::fromLatin1("normalizedName"), QString()); notFilter = Tp::NotFilter::create(propFilter); filters << notFilter; propFilter = Tp::AccountPropertyFilter::create(); propFilter->addProperty(QString::fromLatin1("cmName"), QLatin1String("ring")); notFilter = Tp::NotFilter::create(propFilter); filters << notFilter; propFilter = Tp::AccountPropertyFilter::create(); propFilter->addProperty(QString::fromLatin1("cmName"), QLatin1String("mmscm")); notFilter = Tp::NotFilter::create(propFilter); filters << notFilter; Tp::AccountFilterPtr filter = Tp::AndFilter::create(filters); mAccountSet = mAM->filterAccounts(filter); connect(mAccountSet.data(), SIGNAL(accountAdded(const Tp::AccountPtr &)), SLOT(onAccountAdded(const Tp::AccountPtr &))); connect(mAccountSet.data(), SIGNAL(accountRemoved(const Tp::AccountPtr &)), SLOT(onAccountRemoved(const Tp::AccountPtr &))); Q_FOREACH (const Tp::AccountPtr &account, mAccountSet->accounts()) { insertAccount(account, false); } mStorage.reportPresenceStates(); mStorage.syncAccounts(mAccounts.values()); } void CDTpController::onAccountAdded(const Tp::AccountPtr &account) { if (mAccounts.contains(account->objectPath())) { qCWarning(lcContactsd) << "Internal error, account was already in controller"; return; } CDTpAccountPtr accountWrapper = insertAccount(account, true); mStorage.createAccount(accountWrapper); } void CDTpController::onAccountRemoved(const Tp::AccountPtr &account) { CDTpAccountPtr accountWrapper(mAccounts.take(account->objectPath())); if (not accountWrapper) { qCWarning(lcContactsd) << "Internal error, account was not in controller"; return; } mStorage.removeAccount(accountWrapper); // Drop pending offline operations QString accountPath = accountWrapper->account()->objectPath(); mOfflineRosterBuffer.beginGroup(offlineRemovals); mOfflineRosterBuffer.remove(accountPath); mOfflineRosterBuffer.endGroup(); mOfflineRosterBuffer.beginGroup(offlineInvitations); mOfflineRosterBuffer.remove(accountPath); mOfflineRosterBuffer.endGroup(); mOfflineRosterBuffer.sync(); } CDTpAccountPtr CDTpController::insertAccount(const Tp::AccountPtr &account, bool newAccount) { qCDebug(lcContactsd) << "Creating wrapper for account" << account->objectPath(); // Get the list of contact ids waiting to be removed from server mOfflineRosterBuffer.beginGroup(offlineRemovals); QStringList idsToRemove = mOfflineRosterBuffer.value(account->objectPath()).toStringList(); mOfflineRosterBuffer.endGroup(); CDTpAccountPtr accountWrapper = CDTpAccountPtr(new CDTpAccount(account, idsToRemove, newAccount, this)); mAccounts.insert(account->objectPath(), accountWrapper); maybeStartOfflineOperations(accountWrapper); // Connect change notifications connect(accountWrapper.data(), SIGNAL(rosterChanged(CDTpAccountPtr)), SLOT(onRosterChanged(CDTpAccountPtr))); connect(accountWrapper.data(), SIGNAL(changed(CDTpAccountPtr, CDTpAccount::Changes)), &mStorage, SLOT(updateAccount(CDTpAccountPtr, CDTpAccount::Changes))); connect(accountWrapper.data(), SIGNAL(rosterUpdated(CDTpAccountPtr, const QList &, const QList &)), &mStorage, SLOT(syncAccountContacts(CDTpAccountPtr, const QList &, const QList &))); connect(accountWrapper.data(), SIGNAL(rosterContactChanged(CDTpContactPtr, CDTpContact::Changes)), &mStorage, SLOT(updateContact(CDTpContactPtr, CDTpContact::Changes))); connect(accountWrapper.data(), SIGNAL(syncStarted(Tp::AccountPtr)), SLOT(onSyncStarted(Tp::AccountPtr))); connect(accountWrapper.data(), SIGNAL(syncEnded(Tp::AccountPtr, int, int)), SLOT(onSyncEnded(Tp::AccountPtr, int, int))); return accountWrapper; } void CDTpController::onSyncStarted(Tp::AccountPtr account) { Q_EMIT importStarted(account->serviceName(), account->objectPath()); } void CDTpController::onSyncEnded(Tp::AccountPtr account, int contactsAdded, int contactsRemoved) { Q_EMIT importEnded(account->serviceName(), account->objectPath(), contactsAdded, contactsRemoved, 0); } void CDTpController::onRosterChanged(CDTpAccountPtr accountWrapper) { mStorage.syncAccountContacts(accountWrapper); maybeStartOfflineOperations(accountWrapper); } void CDTpController::maybeStartOfflineOperations(CDTpAccountPtr accountWrapper) { if (!accountWrapper->hasRoster()) { return; } Tp::AccountPtr account = accountWrapper->account(); // Start removal operation mOfflineRosterBuffer.beginGroup(offlineRemovals); QStringList idsToRemove = mOfflineRosterBuffer.value(account->objectPath()).toStringList(); mOfflineRosterBuffer.endGroup(); if (!idsToRemove.isEmpty()) { CDTpRemovalOperation *op = new CDTpRemovalOperation(accountWrapper, idsToRemove); connect(op, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onRemovalFinished(Tp::PendingOperation *))); } // Start invitation operation mOfflineRosterBuffer.beginGroup(offlineInvitations); QStringList idsToInvite = mOfflineRosterBuffer.value(account->objectPath()).toStringList(); mOfflineRosterBuffer.endGroup(); if (!idsToInvite.isEmpty()) { // FIXME: We should also save the localId for offline operations CDTpInvitationOperation *op = new CDTpInvitationOperation(mStorage, accountWrapper, idsToInvite, 0); connect(op, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onInvitationFinished(Tp::PendingOperation *))); } } void CDTpController::inviteBuddies(const QString &accountPath, const QStringList &imIds) { inviteBuddiesOnContact(accountPath, imIds, 0); } void CDTpController::inviteBuddiesOnContact(const QString &accountPath, const QStringList &imIds, uint localId) { qCDebug(lcContactsd) << "InviteBuddies:" << accountPath << imIds.join(QLatin1String(", ")); // Add ids to offlineInvitations, in case operation does not succeed now updateOfflineRosterBuffer(offlineInvitations, accountPath, imIds, QStringList()); CDTpAccountPtr accountWrapper = mAccounts[accountPath]; if (!accountWrapper) { qCDebug(lcContactsd) << "Account not found"; return; } // Start invitation operation if (accountWrapper->hasRoster()) { CDTpInvitationOperation *op = new CDTpInvitationOperation(mStorage, accountWrapper, imIds, localId); connect(op, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onInvitationFinished(Tp::PendingOperation *))); } } void CDTpController::onInvitationFinished(Tp::PendingOperation *op) { // If an error happend, ids stay in the OfflineRosterBuffer and operation // will be retried next time account connects. if (op->isError()) { qCDebug(lcContactsd) << "Error" << op->errorName() << ":" << op->errorMessage(); return; } CDTpInvitationOperation *iop = qobject_cast(op); qCDebug(lcContactsd) << "Contacts invited:" << iop->contactIds().join(QLatin1String(", ")); CDTpAccountPtr accountWrapper = iop->accountWrapper(); const QString accountPath = accountWrapper->account()->objectPath(); updateOfflineRosterBuffer(offlineInvitations, accountPath, QStringList(), iop->contactIds()); } void CDTpController::removeBuddies(const QString &accountPath, const QStringList &imIds) { qCDebug(lcContactsd) << "RemoveBuddies:" << accountPath << imIds.join(QLatin1String(", ")); // Add ids to offlineRemovals, in case it does not get removed right now from server QStringList currentList = updateOfflineRosterBuffer(offlineRemovals, accountPath, imIds, QStringList()); CDTpAccountPtr accountWrapper = mAccounts[accountPath]; if (!accountWrapper) { qCDebug(lcContactsd) << "Account not found"; return; } // Remove ids from storage mStorage.removeAccountContacts(accountWrapper, imIds); // Add contact to account's avoid list accountWrapper->setContactsToAvoid(currentList); // Start removal operation if (accountWrapper->hasRoster()) { CDTpRemovalOperation *op = new CDTpRemovalOperation(accountWrapper, imIds); connect(op, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onRemovalFinished(Tp::PendingOperation *))); } } void CDTpController::onRemovalFinished(Tp::PendingOperation *op) { // If an error happend, ids stay in the OfflineRosterBuffer and operation // will be retried next time account connects. if (op->isError()) { qCDebug(lcContactsd) << "Error" << op->errorName() << ":" << op->errorMessage(); return; } CDTpRemovalOperation *rop = qobject_cast(op); qCDebug(lcContactsd) << "Contacts removed from server:" << rop->contactIds().join(QLatin1String(", ")); CDTpAccountPtr accountWrapper = rop->accountWrapper(); const QString accountPath = accountWrapper->account()->objectPath(); QStringList currentList = updateOfflineRosterBuffer(offlineRemovals, accountPath, QStringList(), rop->contactIds()); // Update account's avoid list, in case they get added back accountWrapper->setContactsToAvoid(currentList); } QStringList CDTpController::updateOfflineRosterBuffer(const QString group, const QString accountPath, const QStringList idsToAdd, const QStringList idsToRemove) { mOfflineRosterBuffer.beginGroup(group); QStringList currentList = mOfflineRosterBuffer.value(accountPath).toStringList(); Q_FOREACH (const QString &id, idsToAdd) { if (!currentList.contains(id)) { currentList << id; } } Q_FOREACH (const QString id, idsToRemove) { currentList.removeOne(id); } if (currentList.isEmpty()) { mOfflineRosterBuffer.remove(accountPath); } else { mOfflineRosterBuffer.setValue(accountPath, currentList); } mOfflineRosterBuffer.endGroup(); mOfflineRosterBuffer.sync(); return currentList; } bool CDTpController::registerDBusObject() { QDBusConnection connection = QDBusConnection::sessionBus(); if (!connection.isConnected()) { qCWarning(lcContactsd) << "Could not connect to DBus:" << connection.lastError(); return false; } if (!connection.registerObject(DBusObjectPath, this)) { qCWarning(lcContactsd) << "Could not register DBus object '/':" << connection.lastError(); return false; } return true; } CDTpRemovalOperation::CDTpRemovalOperation(CDTpAccountPtr accountWrapper, const QStringList &contactIds) : PendingOperation(accountWrapper), mContactIds(contactIds), mAccountWrapper(accountWrapper) { qCDebug(lcContactsd) << "CDTpRemovalOperation: start"; if (accountWrapper->account()->connection().isNull()) { // If the connection is null, we make up an error and emit it // setFinishedWithError takes care of going through the event loop setFinishedWithError(QString::fromLatin1("nullConnection"), QString::fromLatin1("Account connection is null")); return; } Tp::ContactManagerPtr manager = accountWrapper->account()->connection()->contactManager(); QList contactsToRemove; Q_FOREACH(const QString contactId, mContactIds) { Q_FOREACH(const Tp::ContactPtr tpcontact, manager->allKnownContacts()) { if (tpcontact->id() == contactId) { contactsToRemove << tpcontact; } } } Tp::PendingOperation *call = manager->removeContacts(contactsToRemove); connect(call, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onContactsRemoved(Tp::PendingOperation *))); } void CDTpRemovalOperation::onContactsRemoved(Tp::PendingOperation *op) { if (op->isError()) { setFinishedWithError(op->errorName(), op->errorMessage()); return; } setFinished(); } CDTpInvitationOperation::CDTpInvitationOperation(CDTpStorage &storage, CDTpAccountPtr accountWrapper, const QStringList &contactIds, uint contactLocalId) : PendingOperation(accountWrapper) , mStorage(storage) , mContactIds(contactIds) , mAccountWrapper(accountWrapper) , mContactLocalId(contactLocalId) { qCDebug(lcContactsd) << "CDTpInvitationOperation: start"; if (accountWrapper->account()->connection().isNull()) { // If the connection is null, we make up an error and emit it // setFinishedWithError takes care of going through the event loop setFinishedWithError(QString::fromLatin1("nullConnection"), QString::fromLatin1("Account connection is null")); return; } Tp::ContactManagerPtr manager = accountWrapper->account()->connection()->contactManager(); Tp::PendingContacts *call = manager->contactsForIdentifiers(mContactIds); connect(call, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onContactsRetrieved(Tp::PendingOperation *))); } void CDTpInvitationOperation::onContactsRetrieved(Tp::PendingOperation *op) { if (op->isError()) { // We still create the IMAddress on the contact if the request fails, so // that user has a feedback if (mContactLocalId != 0) { mStorage.createAccountContacts(mAccountWrapper, mContactIds, mContactLocalId); } setFinishedWithError(op->errorName(), op->errorMessage()); return; } Tp::PendingContacts *pcontacts = qobject_cast(op); if (mContactLocalId != 0) { QStringList resolvedIds; foreach (const Tp::ContactPtr &c, pcontacts->contacts()) { resolvedIds.append(c->id()); } // Also create "failed" IMAddresses for invalid identifiers foreach (const QString &id, pcontacts->invalidIdentifiers().keys()) { resolvedIds.append(id); } mStorage.createAccountContacts(mAccountWrapper, resolvedIds, mContactLocalId); } PendingOperation *call = pcontacts->manager()->requestPresenceSubscription(pcontacts->contacts()); connect(call, SIGNAL(finished(Tp::PendingOperation *)), SLOT(onPresenceSubscriptionRequested(Tp::PendingOperation *))); } void CDTpInvitationOperation::onPresenceSubscriptionRequested(Tp::PendingOperation *op) { if (op->isError()) { setFinishedWithError(op->errorName(), op->errorMessage()); return; } setFinished(); } contactsd-1.4.14/plugins/telepathy/cdtpcontroller.h000066400000000000000000000102021440357512200224500ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPCONTROLLER_H #define CDTPCONTROLLER_H #include "cdtpaccount.h" #include "cdtpcontact.h" #include "cdtpstorage.h" #include #include #include #include class PendingOfflineRemoval; class CDTpController : public QObject { Q_OBJECT public: CDTpController(QObject *parent = 0); ~CDTpController(); Q_SIGNALS: void importStarted(const QString &service, const QString &account); void importEnded(const QString &service, const QString &account, int contactsAdded, int contactsRemoved, int contactsMerged); void error(int code, const QString &message); public Q_SLOTS: void inviteBuddies(const QString &accountPath, const QStringList &imIds); void inviteBuddiesOnContact(const QString &accountPath, const QStringList &imIds, uint localId); void removeBuddies(const QString &accountPath, const QStringList &imIds); void onRosterChanged(CDTpAccountPtr accountWrapper); private Q_SLOTS: void onAccountManagerReady(Tp::PendingOperation *op); void onAccountAdded(const Tp::AccountPtr &account); void onAccountRemoved(const Tp::AccountPtr &account); void onSyncStarted(Tp::AccountPtr account); void onSyncEnded(Tp::AccountPtr account, int contactsAdded, int contactsRemoved); void onInvitationFinished(Tp::PendingOperation *op); void onRemovalFinished(Tp::PendingOperation *op); private: CDTpAccountPtr insertAccount(const Tp::AccountPtr &account, bool newAccount); void removeAccount(const QString &accountObjectPath); void maybeStartOfflineOperations(CDTpAccountPtr accountWrapper); QStringList updateOfflineRosterBuffer(const QString group, const QString accountPath, const QStringList idsToAdd, const QStringList idsToRemove); bool registerDBusObject(); private: CDTpStorage mStorage; Tp::AccountManagerPtr mAM; Tp::AccountSetPtr mAccountSet; QHash mAccounts; QSettings mOfflineRosterBuffer; }; class CDTpRemovalOperation : public Tp::PendingOperation { Q_OBJECT public: CDTpRemovalOperation(CDTpAccountPtr accountWrapper, const QStringList &contactIds); QStringList contactIds() const { return mContactIds; } CDTpAccountPtr accountWrapper() const { return mAccountWrapper; } private Q_SLOTS: void onContactsRemoved(Tp::PendingOperation *op); private: QStringList mContactIds; CDTpAccountPtr mAccountWrapper; }; class CDTpInvitationOperation : public Tp::PendingOperation { Q_OBJECT public: CDTpInvitationOperation(CDTpStorage &storage, CDTpAccountPtr accountWrapper, const QStringList &contactIds, uint contactLocalId); QStringList contactIds() const { return mContactIds; } CDTpAccountPtr accountWrapper() const { return mAccountWrapper; } private Q_SLOTS: void onContactsRetrieved(Tp::PendingOperation *op); void onPresenceSubscriptionRequested(Tp::PendingOperation *op); private: CDTpStorage &mStorage; QStringList mContactIds; CDTpAccountPtr mAccountWrapper; uint mContactLocalId; }; #endif // CDTPCONTROLLER_H contactsd-1.4.14/plugins/telepathy/cdtpdevicepresence.cpp000066400000000000000000000045731440357512200236220ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2015 Jolla Ltd. ** ** Contact: Matt Vogt (matthew.vogt@jollamobile.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include "cdtpdevicepresence.h" #include "devicepresenceadaptor.h" namespace { const QString DEVICE_PRESENCE_OBJECT_PATH = QStringLiteral("/org/nemomobile/DevicePresence"); const QString DEVICE_PRESENCE_SERVICE_NAME = QStringLiteral("org.nemomobile.DevicePresence"); } CDTpDevicePresence::CDTpDevicePresence(QObject *parent) : QObject(parent) { if (!QDBusConnection::sessionBus().isConnected()) { qCritical() << Q_FUNC_INFO << "ERROR: No DBus session bus found!"; return; } if (!QDBusConnection::sessionBus().registerObject(DEVICE_PRESENCE_OBJECT_PATH, this)) { qWarning() << Q_FUNC_INFO << "Object registration failed:" << DEVICE_PRESENCE_OBJECT_PATH << QDBusConnection::sessionBus().lastError(); } else { if (!QDBusConnection::sessionBus().registerService(DEVICE_PRESENCE_SERVICE_NAME)) { qWarning() << Q_FUNC_INFO << "Unable to register account presence service:" << DEVICE_PRESENCE_SERVICE_NAME << QDBusConnection::sessionBus().lastError(); } else { new DevicePresenceAdaptor(this); } } } CDTpDevicePresence::~CDTpDevicePresence() { QDBusConnection::sessionBus().unregisterService(DEVICE_PRESENCE_SERVICE_NAME); QDBusConnection::sessionBus().unregisterObject(DEVICE_PRESENCE_OBJECT_PATH); } contactsd-1.4.14/plugins/telepathy/cdtpdevicepresence.h000066400000000000000000000037111440357512200232600ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2015 Jolla Ltd. ** ** Contact: Matt Vogt (matthew.vogt@jollamobile.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPDEVICEPRESENCE_H #define CDTPDEVICEPRESENCE_H #include class CDTpDevicePresence : public QObject { Q_OBJECT public: CDTpDevicePresence(QObject *parent = 0); ~CDTpDevicePresence(); signals: void requestUpdate(); void accountList(const QStringList &accountPaths); void globalUpdate(int presenceState); void update(const QString &accountPath, const QString &accountUri, const QString &serviceProvider, const QString &serviceProviderDisplayName, const QString &accountDisplayName, const QString &accountIconPath, int presenceState, const QString &presenceMessage, bool enabled); void selfUpdate(const QString &displayName, const QString &firstName, const QString &lastName, const QStringList &nicknames); }; #endif // CDTPDEVICEPRESENCE_H contactsd-1.4.14/plugins/telepathy/cdtpplugin.cpp000066400000000000000000000046051440357512200221300ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include "cdtpcontroller.h" #include "cdtpplugin.h" #include "debug.h" using namespace Contactsd; CDTpPlugin::CDTpPlugin() : mController(0) { } CDTpPlugin::~CDTpPlugin() { delete mController; } void CDTpPlugin::init() { qCDebug(lcContactsd) << "Initializing contactsd telepathy plugin"; Tp::registerTypes(); Tp::enableDebug(lcContactsd().isDebugEnabled()); Tp::enableWarnings(lcContactsd().isWarningEnabled()); qCDebug(lcContactsd) << "Creating controller"; mController = new CDTpController(this); // relay signals connect(mController, SIGNAL(importStarted(const QString &, const QString &)), SIGNAL(importStarted(const QString &, const QString &))); connect(mController, SIGNAL(importEnded(const QString &, const QString &, int, int, int)), SIGNAL(importEnded(const QString &, const QString &, int, int, int))); connect(mController, SIGNAL(error(int, const QString &)), SIGNAL(error(int, const QString &))); } CDTpPlugin::MetaData CDTpPlugin::metaData() { MetaData data; data[metaDataKeyName] = QVariant(QString::fromLatin1("telepathy")); data[metaDataKeyVersion] = QVariant(QString::fromLatin1("0.2")); data[metaDataKeyComment] = QVariant(QString::fromLatin1("contactsd telepathy plugin")); return data; } contactsd-1.4.14/plugins/telepathy/cdtpplugin.h000066400000000000000000000027351440357512200215770ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPPLUGIN_H #define CDTPPLUGIN_H #include #include #include #include #include "base-plugin.h" class CDTpController; class CDTpPlugin : public Contactsd::BasePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.telepathy") public: CDTpPlugin(); ~CDTpPlugin(); void init(); MetaData metaData(); private: CDTpController *mController; }; #endif // CDTPPLUGIN_H contactsd-1.4.14/plugins/telepathy/cdtpstorage.cpp000066400000000000000000003266411440357512200223050ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2012 - 2019 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cdtpstorage.h" #include "cdtpavatarupdate.h" #include "cdtpplugin.h" #include "cdtpdevicepresence.h" #include "debug.h" #include using namespace Contactsd; // Uncomment for masses of debug output: //#define DEBUG_OVERLOAD // The longer a single batch takes to write, the longer we are locking out other // writers (readers should be unaffected). Using a semaphore write mutex, we should // at least have FIFO semantics on lock release. #define BATCH_STORE_SIZE 5 typedef QList DetailList; namespace { const QString telepathyCollectionName = QStringLiteral("telepathy"); template const QString &sourceLocation(const char *f) { static const QString tmpl(QString::fromLatin1("%2:%1").arg(N)); static const QString loc(tmpl.arg(QString::fromLatin1(f))); return loc; } #define SRC_LOC sourceLocation<__LINE__>(__PRETTY_FUNCTION__) QString asString(bool f) { return QLatin1String(f ? "true" : "false"); } QString asString(const Tp::ContactInfoField &field, int i) { if (i >= field.fieldValue.count()) { return QLatin1String(""); } return field.fieldValue[i]; } QStringList asStringList(const Tp::ContactInfoField &field, int i) { QStringList rv; while (i < field.fieldValue.count()) { rv.append(field.fieldValue[i].trimmed()); ++i; } return rv; } QString asString(CDTpContact::Info::Capability c) { switch (c) { case CDTpContact::Info::TextChats: return QLatin1String("TextChats"); case CDTpContact::Info::StreamedMediaCalls: return QLatin1String("StreamedMediaCalls"); case CDTpContact::Info::StreamedMediaAudioCalls: return QLatin1String("StreamedMediaAudioCalls"); case CDTpContact::Info::StreamedMediaAudioVideoCalls: return QLatin1String("StreamedMediaAudioVideoCalls"); case CDTpContact::Info::UpgradingStreamMediaCalls: return QLatin1String("UpgradingStreamMediaCalls"); case CDTpContact::Info::FileTransfers: return QLatin1String("FileTransfers"); case CDTpContact::Info::StreamTubes: return QLatin1String("StreamTubes"); case CDTpContact::Info::DBusTubes: return QLatin1String("DBusTubes"); default: break; } return QString(); } QString asString(CDTpAccount::Changes changes) { QStringList rv; if (changes & CDTpAccount::DisplayName) { rv.append(QLatin1String("DisplayName")); } if (changes & CDTpAccount::Nickname) { rv.append(QLatin1String("Nickname")); } if (changes & CDTpAccount::Presence) { rv.append(QLatin1String("Presence")); } if (changes & CDTpAccount::Avatar) { rv.append(QLatin1String("Avatar")); } if (changes & CDTpAccount::Enabled) { rv.append(QLatin1String("Enabled")); } if (changes & CDTpAccount::StorageInfo) { rv.append(QLatin1String("StorageInfo")); } return rv.join(QLatin1Char(':')); } QString asString(const QContactId &id) { return id.toString(); } } template QString stringValue(const QContactDetail &detail, F field) { return detail.value(field); } namespace { const int UPDATE_TIMEOUT = 250; // ms const int UPDATE_MAXIMUM_TIMEOUT = 2000; // ms QMap managerParameters() { QMap parameters; parameters.insert(QString::fromLatin1("mergePresenceChanges"), QString::fromLatin1("false")); return parameters; } QContactManager *manager() { static QContactManager manager(QStringLiteral("org.nemomobile.contacts.sqlite"), managerParameters()); return &manager; } template DetailList::value_type detailType() { return T::Type; } bool matchesTelepathyCollectionId(const QContactCollection &collection, int matchAccountId = 0) { const QString name = collection.metaData(QContactCollection::KeyName).toString(); const QString appName = collection.extendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_APPLICATIONNAME).toString(); const int accountId = collection.extendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_ACCOUNTID).toInt(); return name == telepathyCollectionName && appName == QCoreApplication::applicationName() && (matchAccountId == 0 || matchAccountId == accountId); } QList allTelepathyCollections() { QList telepathyCollections; const QList collections = manager()->collections(); for (const QContactCollection &collection : collections) { if (matchesTelepathyCollectionId(collection)) { qCDebug(lcContactsd) << "Found telepathy collection" << collection.id(); telepathyCollections.append(collection); } } return telepathyCollections; } QContactCollectionId telepathyCollectionId(int accountId) { const QList collections = manager()->collections(); QContactCollectionId matchedCollectionId; for (const QContactCollection &collection : collections) { if (matchesTelepathyCollectionId(collection, accountId)) { qCDebug(lcContactsd) << "Found telepathy collection" << collection.id() << "for accountId:" << accountId; matchedCollectionId = collection.id(); break; } } if (!matchedCollectionId.isNull()) { return matchedCollectionId; } QContactCollection collection; collection.setMetaData(QContactCollection::KeyName, telepathyCollectionName); collection.setMetaData(QContactCollection::KeyDescription, QStringLiteral("Telepathy contacts")); collection.setMetaData(QContactCollection::KeyColor, QStringLiteral("darkblue")); collection.setMetaData(QContactCollection::KeySecondaryColor, QStringLiteral("blue")); collection.setMetaData(QContactCollection::KeyImage, QStringLiteral("image://theme/graphic-service-xmpp")); collection.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_APPLICATIONNAME, QCoreApplication::applicationName()); collection.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_READONLY, true); collection.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_ACCOUNTID, accountId); if (manager()->saveCollection(&collection)) { qCDebug(lcContactsd) << "Created telepathy collection for account:" << accountId; return collection.id(); } qCWarning(lcContactsd) << "Unable to create telepathy collection for account!" << accountId << "error was:" << manager()->error(); return QContactCollectionId(); } QContactCollectionId telepathyCollectionId(const QString &accountPath) { const int i = accountPath.lastIndexOf(QLatin1Char('_')); if (i >= 0) { int accountId = accountPath.mid(i + 1).toInt(); if (accountId > 0) { return telepathyCollectionId(accountId); } } qCWarning(lcContactsd) << "telepathy accountPath does not contain valid account id:" << accountPath; return QContactCollectionId(); } QContactFetchHint contactFetchHint(const DetailList &detailTypes = DetailList()) { QContactFetchHint hint; // Relationships are slow and unnecessary here: hint.setOptimizationHints(QContactFetchHint::NoRelationships | QContactFetchHint::NoActionPreferences | QContactFetchHint::NoBinaryBlobs); if (!detailTypes.isEmpty()) { hint.setDetailTypesHint(detailTypes); } return hint; } QContactId selfContactAggregateId() { return manager()->selfContactId(); } QContactId selfContactLocalId(const QContactCollectionId &collectionId) { QContactManager *mgr(manager()); // Check that there is a self contact QContactId selfId(selfContactAggregateId()); // Find the telepathy contact aggregated by the real self contact QContactRelationshipFilter relationshipFilter; relationshipFilter.setRelationshipType(QContactRelationship::Aggregates()); QContact relatedContact; relatedContact.setId(selfId); relationshipFilter.setRelatedContactId(relatedContact.id()); relationshipFilter.setRelatedContactRole(QContactRelationship::First); QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(collectionId); QContactIntersectionFilter selfFilter; selfFilter << collectionFilter; selfFilter << relationshipFilter; QList selfContactIds = mgr->contactIds(selfFilter); if (selfContactIds.count() > 0) { if (selfContactIds.count() > 1) { qCWarning(lcContactsd) << "Invalid number of telepathy self contacts!" << selfContactIds.count(); } qCDebug(lcContactsd) << "Found self contact" << selfContactIds.first() << "for collection:" << collectionId; return selfContactIds.first(); } // Create a new self contact for telepathy qCDebug(lcContactsd) << "Creating self contact for collection:" << collectionId; QContact tpSelf; tpSelf.setCollectionId(collectionId); if (!mgr->saveContact(&tpSelf)) { qCWarning(lcContactsd) << "Unable to save empty contact as self contact - error:" << mgr->error(); } else { // Now connect our contact to the real self contact QContactRelationship relationship; relationship.setRelationshipType(QContactRelationship::Aggregates()); relatedContact.setId(selfId); relationship.setFirst(relatedContact.id()); relationship.setSecond(tpSelf.id()); if (!mgr->saveRelationship(&relationship)) { qCWarning(lcContactsd) << "Unable to save relationship for self contact - error:" << mgr->error(); qFatal("Cannot proceed with invalid self contact!"); } // Find the aggregate contact created by saving our self contact relationshipFilter.setRelationshipType(QContactRelationship::Aggregates()); relatedContact.setId(tpSelf.id()); relationshipFilter.setRelatedContactId(relatedContact.id()); relationshipFilter.setRelatedContactRole(QContactRelationship::Second); foreach (const QContact &aggregator, mgr->contacts(relationshipFilter)) { if (aggregator.id() == tpSelf.id()) continue; // Remove the relationship between these contacts (which removes the childless aggregate) QContactRelationship relationship; relationship.setRelationshipType(QContactRelationship::Aggregates()); relationship.setFirst(aggregator.id()); relationship.setSecond(tpSelf.id()); if (!mgr->removeRelationship(relationship)) { qCWarning(lcContactsd) << "Unable to remove relationship for self contact - error:" << mgr->error(); } } return tpSelf.id(); } return QContactId(); } QContact selfContact(const QContactCollectionId &collectionId) { // For the self contact, we only care about accounts/presence/avatars QContactId selfLocalId(selfContactLocalId(collectionId)); static QContactFetchHint hint(contactFetchHint(DetailList() << detailType() << detailType() << detailType() << detailType())); return manager()->contact(selfLocalId, hint); } QContact selfDetailsContact() { static QContactFetchHint hint(contactFetchHint(DetailList() << detailType() << detailType() << detailType())); return manager()->contact(selfContactAggregateId(), hint); } static void output(const QContactDetail &detail) { const QMap &values(detail.values()); QMap::const_iterator it = values.constBegin(), end = values.constEnd(); for ( ; it != end; ++it) { qCDebug(lcContactsd) << " -" << it.key() << ":" << it.value(); } } QContactDetail::DetailType detailType(const QContactDetail &detail) { return detail.type(); } static void output(const QContact &contact) { const QList &details(contact.details()); foreach (const QContactDetail &detail, details) { qCDebug(lcContactsd) << " Detail:" << detailType(detail); output(detail); } } bool storeContactDetail(QContact &contact, QContactDetail &detail, const QString &location) { #ifdef DEBUG_OVERLOAD qCDebug(lcContactsd) << " Storing" << detailType(detail) << "from:" << location; output(detail); #endif if (!contact.saveDetail(&detail)) { qCDebug(lcContactsd) << " Failed storing" << detailType(detail) << "from:" << location; #ifndef DEBUG_OVERLOAD output(detail); #endif return false; } return true; } DetailList contactChangesList(CDTpContact::Changes changes) { DetailList rv; if ((changes & CDTpContact::Information) == 0) { if (changes & CDTpContact::Alias) { rv.append(detailType()); } if (changes & CDTpContact::Presence) { rv.append(detailType()); rv.append(detailType()); } if (changes & CDTpContact::Capabilities) { rv.append(detailType()); rv.append(detailType()); } if (changes & CDTpContact::Avatar) { rv.append(detailType()); } } return rv; } bool storeContact(QContact &contact, const QString &location, CDTpContact::Changes changes = CDTpContact::All) { const DetailList updates = contactChangesList(changes); const bool minimizedUpdate(!updates.isEmpty()); #ifdef DEBUG_OVERLOAD qCDebug(lcContactsd) << "Storing contact" << asString(contact.id()) << "from:" << location; output(contact); #endif if (minimizedUpdate) { QList contacts; contacts << contact; if (!manager()->saveContacts(&contacts, updates)) { qCWarning(lcContactsd) << "Failed minimized storing contact" << asString(contact.id()) << "from:" << location << "error:" << manager()->error(); #ifndef DEBUG_OVERLOAD output(contact); #endif qCDebug(lcContactsd) << "Updates" << updates; return false; } } else { if (!manager()->saveContact(&contact)) { qCWarning(lcContactsd) << "Failed storing contact" << asString(contact.id()) << "from:" << location; #ifndef DEBUG_OVERLOAD output(contact); #endif return false; } } return true; } QChar::Script nameScript(const QString &name) { QChar::Script script(QChar::Script_Unknown); if (!name.isEmpty()) { QString::const_iterator it = name.begin(), end = name.end(); for ( ; it != end; ++it) { const QChar::Category charCategory((*it).category()); if (charCategory >= QChar::Letter_Uppercase && charCategory <= QChar::Letter_Other) { const QChar::Script charScript((*it).script()); if (script == QChar::Script_Unknown) { script = charScript; } else if (charScript != script) { return QChar::Script_Unknown; } } } } return script; } QChar::Script nameScript(const QString &firstName, const QString &lastName) { if (firstName.isEmpty()) { return nameScript(lastName); } else if (lastName.isEmpty()) { return nameScript(firstName); } QChar::Script firstScript(nameScript(firstName)); if (firstScript != QChar::Script_Unknown) { QChar::Script lastScript(nameScript(lastName)); if (lastScript == firstScript) { return lastScript; } } return QChar::Script_Unknown; } bool nameScriptImpliesFamilyFirst(const QString &firstName, const QString &lastName) { switch (nameScript(firstName, lastName)) { // These scripts are used by cultures that conform to the family-name-first naming convention: case QChar::Script_Han: case QChar::Script_Lao: case QChar::Script_Hangul: case QChar::Script_Khmer: case QChar::Script_Mongolian: case QChar::Script_Hiragana: case QChar::Script_Katakana: case QChar::Script_Bopomofo: case QChar::Script_Yi: return true; default: return false; } } bool needsSpaceBetweenNames(const QString &first, const QString &second) { if (first.isEmpty() || second.isEmpty()) { return false; } return first[first.length()-1].script() != QChar::Script_Han || second[0].script() != QChar::Script_Han; } QString generateDisplayLabel(const QContactName &nameDetail, CDTpStorage::DisplayLabelOrder order) { // Simplified version of the SeasideCache displayLabel generator QString rv; QString nameStr1(nameDetail.firstName()); QString nameStr2(nameDetail.lastName()); const bool familyNameFirst(order == CDTpStorage::LastNameFirst || nameScriptImpliesFamilyFirst(nameStr1, nameStr2)); if (familyNameFirst) { nameStr1 = nameDetail.lastName(); nameStr2 = nameDetail.firstName(); } if (!nameStr1.isEmpty()) rv.append(nameStr1); if (!nameStr2.isEmpty()) { if (needsSpaceBetweenNames(nameStr1, nameStr2)) { rv.append(QLatin1Char(' ')); } rv.append(nameStr2); } return rv; } void reportSelfDetails(CDTpDevicePresence *devicePresence, const QContact &contact, CDTpStorage::DisplayLabelOrder order) { const QContactName nameDetail(contact.detail()); QStringList nicknames; foreach (const QContactNickname &nickname, contact.details()) { nicknames.append(nickname.nickname()); } QString displayLabel(generateDisplayLabel(nameDetail, order)); if (displayLabel.isEmpty()) { displayLabel = contact.detail().label(); } emit devicePresence->selfUpdate(displayLabel, nameDetail.firstName(), nameDetail.lastName(), nicknames); } void emitAccountChanges(CDTpDevicePresence *devicePresence, QContact &self, bool updateAccountList = false) { // See if the global presence has been updated const QContactPresence::PresenceState previousState(self.detail().presenceState()); DetailList types(DetailList() << detailType()); if (updateAccountList) { types << detailType(); } const QContact updated(manager()->contact(self.id(), contactFetchHint(types))); const QContactPresence::PresenceState updatedState(updated.detail().presenceState()); if (updatedState != previousState) { emit devicePresence->globalUpdate(updatedState); } if (updateAccountList) { // Ensure that listeners are aware of any invalidated accounts QStringList accountPaths; foreach (const QContactOnlineAccount &qcoa, updated.details()) { accountPaths.append(qcoa.value(QContactOnlineAccount__FieldAccountPath)); } emit devicePresence->accountList(accountPaths); } } bool storeSelfContact(CDTpDevicePresence *devicePresence, QContact &self, const QString &location, CDTpContact::Changes changes = CDTpContact::All, bool updateAccountList = false) { if (!storeContact(self, location, changes)) { return false; } if ((changes & CDTpContact::Presence) || updateAccountList) { emitAccountChanges(devicePresence, self, updateAccountList); } return true; } void appendContactChange(CDTpStorage::ContactChangeSet *saveSet, const QContact &contact, CDTpContact::Changes changes) { if (changes != 0) { if (changes & CDTpContact::Information) { // All changes including Information will be full stores, so group them all together changes = CDTpContact::All; } (*saveSet)[changes].append(contact); } } void updateContacts(const QString &location, CDTpStorage::ContactChangeSet *saveSet, QList *removeList) { if (saveSet && !saveSet->isEmpty()) { // Each element of the save set is a list of contacts with the same set of changes CDTpStorage::ContactChangeSet::iterator sit = saveSet->begin(), send = saveSet->end(); for ( ; sit != send; ++sit) { CDTpContact::Changes changes = sit.key(); QList *saveList = &(sit.value()); if (saveList && !saveList->isEmpty()) { // Restrict the update to only modify the detail types that have changed for these contacts const DetailList detailList(contactChangesList(changes)); QElapsedTimer t; t.start(); // Try to store contacts in batches int storedCount = 0; while (storedCount < saveList->count()) { QList batch(saveList->mid(storedCount, BATCH_STORE_SIZE)); storedCount += BATCH_STORE_SIZE; do { bool success; QMap errorMap; if (detailList.isEmpty()) { success = manager()->saveContacts(&batch, &errorMap); } else { success = manager()->saveContacts(&batch, detailList, &errorMap); } if (success) { // We could copy the updated contacts back into saveList here, but it doesn't seem warranted break; } const int errorCount = errorMap.count(); if (!errorCount) { break; } // Remove the problematic contacts QList indices = errorMap.keys(); QList::const_iterator begin = indices.begin(), it = begin + errorCount; do { int errorIndex = (*--it); const QContact &badContact(batch.at(errorIndex)); qCWarning(lcContactsd) << "Failed storing contact" << asString(badContact.id()) << "from:" << location << "error:" << errorMap.value(errorIndex); output(badContact); batch.removeAt(errorIndex); } while (it != begin); } while (true); } qCDebug(lcContactsd) << "Updated" << saveList->count() << "batched contacts - elapsed:" << t.elapsed() << detailList; } } } if (removeList && !removeList->isEmpty()) { QElapsedTimer t; t.start(); QList::iterator it = removeList->begin(), end = removeList->end(); for ( ; it != end; ++it) { if (!manager()->removeContact(*it)) { qCWarning(lcContactsd) << "Unable to remove contact"; } } qCDebug(lcContactsd) << "Removed" << removeList->count() << "individual contacts - elapsed:" << t.elapsed(); } } QList findContactIdsForAccount(const QString &accountPath) { QContactIntersectionFilter filter; filter << QContactOriginMetadata::matchGroupId(accountPath); QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(telepathyCollectionId(accountPath)); filter << collectionFilter; return manager()->contactIds(filter); } QHash findExistingContacts(const QStringList &contactAddresses, const QContactCollectionId &collectionId) { QHash rv; QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(collectionId); // If there is a large number of contacts, do a two-step fetch const int maxDirectMatches = 10; if (contactAddresses.count() > maxDirectMatches) { QList ids; QSet addressSet(contactAddresses.toSet()); // First fetch all telepathy contacts, ID data only QContactFetchHint idHint(contactFetchHint(DetailList() << detailType())); foreach (const QContact &contact, manager()->contacts(collectionFilter, QList(), idHint)) { const QString &address = stringValue(contact.detail(), QContactOriginMetadata::FieldId); if (addressSet.contains(address)) { ids.append(contact.id()); } } // Now fetch the details of the required contacts by ID QContactFetchHint hint(contactFetchHint()); foreach (const QContact &contact, manager()->contacts(ids, hint)) { rv.insert(stringValue(contact.detail(), QContactOriginMetadata::FieldId), contact); } } else { // Just query the ones we need QContactIntersectionFilter filter; filter << collectionFilter; QContactUnionFilter addressFilter; foreach (const QString &address, contactAddresses) { addressFilter << QContactOriginMetadata::matchId(address); } filter << addressFilter; QContactFetchHint hint(contactFetchHint()); foreach (const QContact &contact, manager()->contacts(filter, QList(), hint)) { rv.insert(stringValue(contact.detail(), QContactOriginMetadata::FieldId), contact); } } return rv; } QHash findExistingContacts(const QSet &contactAddresses, const QContactCollectionId &collectionId) { return findExistingContacts(contactAddresses.toList(), collectionId); } QContact findExistingContact(const QString &contactAddress, const QContactCollectionId &collectionId) { QContactIntersectionFilter filter; filter << QContactOriginMetadata::matchId(contactAddress); QContactCollectionFilter collectionFilter; collectionFilter.setCollectionId(collectionId); filter << collectionFilter; QContactFetchHint hint(contactFetchHint()); foreach (const QContact &contact, manager()->contacts(filter, QList(), hint)) { // Return the first match we find (there should be only one) return contact; } qCDebug(lcContactsd) << "No matching contact:" << contactAddress; return QContact(); } template T findLinkedDetail(const QContact &owner, const QContactDetail &link) { const QString linkUri(link.detailUri()); foreach (const T &detail, owner.details()) { if (detail.linkedDetailUris().contains(linkUri)) { return detail; } } return T(); } QContactPresence findPresenceForAccount(const QContact &owner, const QContactOnlineAccount &qcoa) { return findLinkedDetail(owner, qcoa); } QContactAvatar findAvatarForAccount(const QContact &owner, const QContactOnlineAccount &qcoa) { return findLinkedDetail(owner, qcoa); } QString imAccount(Tp::AccountPtr account) { return account->objectPath(); } QString imAccount(CDTpAccount *account) { return imAccount(account->account()); } QString imAccount(CDTpAccountPtr accountWrapper) { return imAccount(accountWrapper->account()); } QString imAccount(CDTpContactPtr contactWrapper) { return imAccount(contactWrapper->accountWrapper()); } QString imAddress(const QString &accountPath, const QString &contactId = QString()) { static const QString tmpl = QString::fromLatin1("%1!%2"); return tmpl.arg(accountPath, contactId.isEmpty() ? QLatin1String("self") : contactId); } QString imAddress(Tp::AccountPtr account, const QString &contactId = QString()) { return imAddress(imAccount(account), contactId); } QString imAddress(CDTpAccountPtr accountWrapper, const QString &contactId = QString()) { return imAddress(accountWrapper->account(), contactId); } QString imAddress(CDTpContactPtr contactWrapper) { return imAddress(contactWrapper->accountWrapper(), contactWrapper->contact()->id()); } QString imPresence(const QString &accountPath, const QString &contactId = QString()) { static const QString tmpl = QString::fromLatin1("%1!%2!presence"); return tmpl.arg(accountPath, contactId.isEmpty() ? QLatin1String("self") : contactId); } QString imPresence(Tp::AccountPtr account, const QString &contactId = QString()) { return imPresence(imAccount(account), contactId); } QContactPresence::PresenceState qContactPresenceState(Tp::ConnectionPresenceType presenceType) { switch (presenceType) { case Tp::ConnectionPresenceTypeOffline: return QContactPresence::PresenceOffline; case Tp::ConnectionPresenceTypeAvailable: return QContactPresence::PresenceAvailable; case Tp::ConnectionPresenceTypeAway: return QContactPresence::PresenceAway; case Tp::ConnectionPresenceTypeExtendedAway: return QContactPresence::PresenceExtendedAway; case Tp::ConnectionPresenceTypeHidden: return QContactPresence::PresenceHidden; case Tp::ConnectionPresenceTypeBusy: return QContactPresence::PresenceBusy; case Tp::ConnectionPresenceTypeUnknown: case Tp::ConnectionPresenceTypeUnset: case Tp::ConnectionPresenceTypeError: break; default: qCWarning(lcContactsd) << "Unknown telepathy presence status" << presenceType; break; } return QContactPresence::PresenceUnknown; } bool isOnlinePresence(Tp::ConnectionPresenceType presenceType, Tp::AccountPtr account) { switch (presenceType) { // Why?? case Tp::ConnectionPresenceTypeOffline: return account->protocolName() == QLatin1String("skype"); case Tp::ConnectionPresenceTypeUnset: case Tp::ConnectionPresenceTypeUnknown: case Tp::ConnectionPresenceTypeError: return false; default: break; } return true; } QStringList currentCapabilites(const Tp::CapabilitiesBase &capabilities, Tp::ConnectionPresenceType presenceType, Tp::AccountPtr account) { QStringList current; if (capabilities.textChats()) { current << asString(CDTpContact::Info::TextChats); } if (isOnlinePresence(presenceType, account)) { if (capabilities.streamedMediaCalls()) { current << asString(CDTpContact::Info::StreamedMediaCalls); } if (capabilities.streamedMediaAudioCalls()) { current << asString(CDTpContact::Info::StreamedMediaAudioCalls); } if (capabilities.streamedMediaVideoCalls()) { current << asString(CDTpContact::Info::StreamedMediaAudioVideoCalls); } if (capabilities.upgradingStreamedMediaCalls()) { current << asString(CDTpContact::Info::UpgradingStreamMediaCalls); } if (capabilities.fileTransfers()) { current << asString(CDTpContact::Info::FileTransfers); } } return current; } QString saveAccountAvatar(CDTpAccountPtr accountWrapper) { const Tp::Avatar &avatar = accountWrapper->account()->avatar(); if (avatar.avatarData.isEmpty()) { return QString(); } const QString avatarDirPath(CDTpPlugin::cacheFileName(QString::fromLatin1("avatars/account"))); QDir storageDir(avatarDirPath); if (!storageDir.exists() && !storageDir.mkpath(QString::fromLatin1("."))) { qWarning() << "Unable to create contacts avatar storage directory:" << storageDir.path(); return QString(); } QString filename = QString::fromLatin1(QCryptographicHash::hash(avatar.avatarData, QCryptographicHash::Md5).toHex()); filename = avatarDirPath + QDir::separator() + filename + QString::fromLatin1(".jpg"); QFile avatarFile(filename); if (!avatarFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCWarning(lcContactsd) << "Unable to save account avatar: error opening avatar file" << filename << "for writing"; return QString(); } avatarFile.write(avatar.avatarData); avatarFile.close(); return filename; } void updateFacebookAvatar(QNetworkAccessManager &network, CDTpContactPtr contactWrapper, const QString &facebookId, const QString &avatarType) { const QUrl avatarUrl(QLatin1String("http://graph.facebook.com/v2.6/") % facebookId % QLatin1String("/picture?type=") % avatarType); // Initiate an avatar-update operation CDTpAvatarUpdate::updateContact(contactWrapper.data(), network.get(QNetworkRequest(avatarUrl)), QString::fromLatin1("%1-picture.jpg").arg(facebookId), avatarType); } void updateSocialAvatars(QNetworkAccessManager &network, CDTpContactPtr contactWrapper) { if (network.networkAccessible() == QNetworkAccessManager::NotAccessible) { return; } QRegExp facebookIdPattern(QLatin1String("-(\\d+)@chat\\.facebook\\.com")); if (not facebookIdPattern.exactMatch(contactWrapper->contact()->id())) { return; // only supporting Facebook avatars right now } const QString socialId = facebookIdPattern.cap(1); // Ignore the square avatar, we only need the large one updateFacebookAvatar(network, contactWrapper, socialId, CDTpAvatarUpdate::Large); } bool onlineAccountEnabled(const QContactOnlineAccount &qcoa) { return (qcoa.value(QContactOnlineAccount__FieldEnabled).toString() == asString(true)); } void reportAccountPresence(CDTpDevicePresence *devicePresence, const QContactOnlineAccount &qcoa, const QContactPresence &presence) { emit devicePresence->update(qcoa.value(QContactOnlineAccount__FieldAccountPath), qcoa.detailUri(), qcoa.serviceProvider(), qcoa.value(QContactOnlineAccount__FieldServiceProviderDisplayName), qcoa.value(QContactOnlineAccount__FieldAccountDisplayName), qcoa.value(QContactOnlineAccount__FieldAccountIconPath), presence.presenceState(), presence.customMessage(), qcoa.value(QContactOnlineAccount__FieldEnabled) == asString(true)); } CDTpContact::Changes updateAccountDetails(CDTpDevicePresence *devicePresence, QContact &self, QContactOnlineAccount &qcoa, QContactPresence &presence, CDTpAccountPtr accountWrapper, CDTpAccount::Changes changes) { CDTpContact::Changes selfChanges = 0; const QString accountPath(imAccount(accountWrapper)); qCDebug(lcContactsd) << SRC_LOC << "Update account" << accountPath; Tp::AccountPtr account = accountWrapper->account(); if (changes & CDTpAccount::Presence) { Tp::Presence tpPresence(account->currentPresence()); QContactPresence::PresenceState newState(qContactPresenceState(tpPresence.type())); const QString newMessage(tpPresence.statusMessage()); if ((presence.presenceState() != newState) || (presence.customMessage() != newMessage)) { presence.setPresenceState(newState); presence.setCustomMessage(newMessage); presence.setTimestamp(QDateTime::currentDateTime()); selfChanges |= CDTpContact::Presence; } } if (changes & CDTpAccount::Nickname) { const QString nickname(account->nickname()); if (presence.nickname() != nickname) { presence.setNickname(nickname); selfChanges |= CDTpContact::Alias; } } if (changes & CDTpAccount::DisplayName) { const QString displayName(account->displayName()); if (qcoa.value(QContactOnlineAccount__FieldAccountDisplayName) != displayName) { qcoa.setValue(QContactOnlineAccount__FieldAccountDisplayName, displayName); selfChanges |= CDTpContact::Capabilities; } } if (changes & CDTpAccount::StorageInfo) { const QString providerDisplayName(accountWrapper->storageInfo().value(QLatin1String("providerDisplayName")).toString()); if (qcoa.value(QContactOnlineAccount__FieldServiceProviderDisplayName) != providerDisplayName) { qcoa.setValue(QContactOnlineAccount__FieldServiceProviderDisplayName, providerDisplayName); selfChanges |= CDTpContact::Capabilities; } } if (changes & CDTpAccount::Avatar) { const QString avatarPath(saveAccountAvatar(accountWrapper)); QContactAvatar avatar(findAvatarForAccount(self, qcoa)); if (avatarPath.isEmpty()) { if (!avatar.isEmpty()) { if (!self.removeDetail(&avatar)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to remove avatar for account:" << accountPath; } selfChanges |= CDTpContact::Avatar; } } else { QUrl avatarUrl(QUrl::fromLocalFile(avatarPath)); if (avatarUrl != avatar.imageUrl()) { avatar.setImageUrl(avatarUrl); avatar.setLinkedDetailUris(qcoa.detailUri()); if (!storeContactDetail(self, avatar, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save avatar for account:" << accountPath; } selfChanges |= CDTpContact::Avatar; } } } // Ensure this account's enabled status is reflected if (account->isEnabled() != onlineAccountEnabled(qcoa)) { qcoa.setValue(QContactOnlineAccount__FieldEnabled, asString(account->isEnabled())); selfChanges |= CDTpContact::Capabilities; } // If this account is not enabled, ensure the presence indicates unknown state if (!account->isEnabled()) { if (presence.presenceState() != QContactPresence::PresenceUnknown) { presence.setPresenceState(QContactPresence::PresenceUnknown); presence.setTimestamp(QDateTime::currentDateTime()); selfChanges |= CDTpContact::Presence; } } if (selfChanges & CDTpContact::Presence) { if (!storeContactDetail(self, presence, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save presence for self account:" << accountPath; } // Presence changes also imply potential capabilities changes selfChanges |= CDTpContact::Capabilities; } if (selfChanges & CDTpContact::Capabilities) { // The account has changed if (!storeContactDetail(self, qcoa, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save details for self account:" << accountPath; } // Report the current presence state of this account reportAccountPresence(devicePresence, qcoa, presence); } return selfChanges; } template void deleteContactDetails(QContact &existing) { foreach (DetailType detail, existing.details()) { if (!existing.removeDetail(&detail)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to remove obsolete detail:" << detail.detailUri(); } } } typedef QHash Dictionary; Dictionary initPhoneTypes() { Dictionary types; types.insert(QLatin1String("bbsl"), QContactPhoneNumber::SubTypeBulletinBoardSystem); types.insert(QLatin1String("car"), QContactPhoneNumber::SubTypeCar); types.insert(QLatin1String("cell"), QContactPhoneNumber::SubTypeMobile); types.insert(QLatin1String("fax"), QContactPhoneNumber::SubTypeFax); types.insert(QLatin1String("modem"), QContactPhoneNumber::SubTypeModem); types.insert(QLatin1String("pager"), QContactPhoneNumber::SubTypePager); types.insert(QLatin1String("video"), QContactPhoneNumber::SubTypeVideo); types.insert(QLatin1String("voice"), QContactPhoneNumber::SubTypeVoice); // Not sure about these types: types.insert(QLatin1String("isdn"), QContactPhoneNumber::SubTypeLandline); types.insert(QLatin1String("pcs"), QContactPhoneNumber::SubTypeLandline); return types; } const Dictionary &phoneTypes() { static Dictionary types(initPhoneTypes()); return types; } Dictionary initAddressTypes() { Dictionary types; types.insert(QLatin1String("dom"), QContactAddress::SubTypeDomestic); types.insert(QLatin1String("intl"), QContactAddress::SubTypeInternational); types.insert(QLatin1String("parcel"), QContactAddress::SubTypeParcel); types.insert(QLatin1String("postal"), QContactAddress::SubTypePostal); return types; } const Dictionary &addressTypes() { static Dictionary types(initAddressTypes()); return types; } Dictionary initGenderTypes() { Dictionary types; types.insert(QLatin1String("f"), QContactGender::GenderFemale); types.insert(QLatin1String("female"), QContactGender::GenderFemale); types.insert(QLatin1String("m"), QContactGender::GenderMale); types.insert(QLatin1String("male"), QContactGender::GenderMale); return types; } const Dictionary &genderTypes() { static Dictionary types(initGenderTypes()); return types; } Dictionary initProtocolTypes() { Dictionary types; types.insert(QLatin1String("aim"), QContactOnlineAccount::ProtocolAim); types.insert(QLatin1String("icq"), QContactOnlineAccount::ProtocolIcq); types.insert(QLatin1String("irc"), QContactOnlineAccount::ProtocolIrc); types.insert(QLatin1String("jabber"), QContactOnlineAccount::ProtocolJabber); types.insert(QLatin1String("msn"), QContactOnlineAccount::ProtocolMsn); types.insert(QLatin1String("qq"), QContactOnlineAccount::ProtocolQq); types.insert(QLatin1String("skype"), QContactOnlineAccount::ProtocolSkype); types.insert(QLatin1String("yahoo"), QContactOnlineAccount::ProtocolYahoo); return types; } QContactOnlineAccount::Protocol protocolType(const QString &protocol) { static Dictionary types(initProtocolTypes()); Dictionary::const_iterator it = types.find(protocol.toLower()); if (it != types.constEnd()) { return static_cast(*it); } return QContactOnlineAccount::ProtocolUnknown; } template void replaceNameDetail(F1 getter, F2 setter, QContactName *nameDetail, const QString &value) { if (!value.isEmpty()) { (nameDetail->*setter)(value); } else { // If there is an existing value, remove it QString existing((nameDetail->*getter)()); if (!existing.isEmpty()) { (nameDetail->*setter)(value); } } } template bool detailListsDiffer(const QList &lhs, const QList &rhs, F detailsDiffer) { if (lhs.count() != rhs.count()) return true; typename QList::const_iterator lit = lhs.constBegin(), lend = lhs.constEnd(); for (typename QList::const_iterator rit = rhs.constBegin(); lit != lend; ++lit, ++rit) { if (detailsDiffer(*lit, *rit)) { return true; } } return false; } // Test detail for validity - for most details, we need some content, beside contexts, etc. bool invalidDetail(const QContactDetail &detail) { return detail.isEmpty(); } bool invalidDetail(const QContactAddress &address) { return address.postOfficeBox().isEmpty() && address.street().isEmpty() && address.locality().isEmpty() && address.region().isEmpty() && address.postcode().isEmpty() && address.country().isEmpty(); } bool invalidDetail(const QContactBirthday &birthday) { return !birthday.date().isValid(); } bool invalidDetail(const QContactEmailAddress &address) { return address.emailAddress().isEmpty(); } bool invalidDetail(const QContactName &name) { return name.prefix().isEmpty() && name.firstName().isEmpty() && name.middleName().isEmpty() && name.lastName().isEmpty() && name.suffix().isEmpty(); } bool invalidDetail(const QContactNote ¬e) { return note.note().isEmpty(); } bool invalidDetail(const QContactOrganization &org) { return org.title().isEmpty() && org.role().isEmpty() && org.name().isEmpty() && org.department().isEmpty(); } bool invalidDetail(const QContactPhoneNumber &phoneNumber) { return phoneNumber.number().isEmpty(); } bool invalidDetail(const QContactUrl &url) { return url.url().isEmpty(); } template bool replaceDetails(QContact &contact, QList &details, const QString &address, const QString &location) { deleteContactDetails(contact); foreach (T detail, details) { if (!invalidDetail(detail)) { if (!storeContactDetail(contact, detail, location)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save detail to contact:" << address; } } } return true; } template bool replaceDetails(QContact &contact, T &detail, const QString &address, const QString &location) { return replaceDetails(contact, QList() << detail, address, location); } CDTpContact::Changes updateContactDetails(QNetworkAccessManager &network, QContact &existing, CDTpContactPtr contactWrapper, CDTpContact::Changes changes) { const QString contactAddress(imAddress(contactWrapper)); qCDebug(lcContactsd) << "Update contact" << contactAddress; Tp::ContactPtr contact = contactWrapper->contact(); CDTpContact::Changes contactChanges; // Apply changes if (changes & CDTpContact::Alias) { QContactNickname nickname = existing.detail(); const QString newNickname(contact->alias().trimmed()); if (nickname.nickname() != newNickname) { nickname.setNickname(newNickname); if (!storeContactDetail(existing, nickname, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save alias to contact for:" << contactAddress; } contactChanges |= CDTpContact::Alias; } // The alias is also reflected in the presence changes |= CDTpContact::Presence; } if (changes & CDTpContact::Presence) { Tp::Presence tpPresence(contact->presence()); QContactPresence presence = existing.detail(); const QContactPresence::PresenceState newState(qContactPresenceState(tpPresence.type())); const QString newMessage(tpPresence.statusMessage()); const QString newNickname(contact->alias().trimmed()); if (presence.presenceState() != newState || presence.customMessage() != newMessage || presence.nickname() != newNickname) { presence.setPresenceState(newState); presence.setCustomMessage(newMessage); presence.setNickname(newNickname); presence.setTimestamp(QDateTime::currentDateTime()); if (!storeContactDetail(existing, presence, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save presence to contact for:" << contactAddress; } contactChanges |= CDTpContact::Presence; } // Since we use static account capabilities as fallback, each presence also implies // a capability change. This doesn't fit the pure school of Telepathy, but we really // should not drop the static caps fallback at this stage. changes |= CDTpContact::Capabilities; } if (changes & CDTpContact::Capabilities) { QContactOnlineAccount qcoa = existing.detail(); CDTpAccountPtr accountWrapper = contactWrapper->accountWrapper(); Tp::AccountPtr account = accountWrapper->account(); const QString providerDisplayName(accountWrapper->storageInfo().value(QLatin1String("providerDisplayName")).toString()); const QStringList newCapabilities(currentCapabilites(contact->capabilities(), contact->presence().type(), account)); if (qcoa.capabilities() != newCapabilities || onlineAccountEnabled(qcoa) != account->isEnabled() || qcoa.value(QContactOnlineAccount__FieldAccountDisplayName) != account->displayName() || qcoa.value(QContactOnlineAccount__FieldServiceProviderDisplayName) != providerDisplayName) { qcoa.setCapabilities(newCapabilities); qcoa.setValue(QContactOnlineAccount__FieldEnabled, asString(account->isEnabled())); qcoa.setValue(QContactOnlineAccount__FieldAccountDisplayName, account->displayName()); qcoa.setValue(QContactOnlineAccount__FieldServiceProviderDisplayName, providerDisplayName); if (!storeContactDetail(existing, qcoa, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save capabilities to contact for:" << contactAddress; } contactChanges |= CDTpContact::Capabilities; } } if (changes & CDTpContact::Information) { if (contactWrapper->isInformationKnown()) { // Extract the current information state from the info fields QList newAddresses; QContactBirthday newBirthday; QList newEmailAddresses; QContactGender newGender; QContactName newName; QList newNicknames; QList newNotes; QList newOrganizations; QList newPhoneNumbers; QList newUrls; Tp::ContactInfoFieldList listContactInfo = contact->infoFields().allFields(); if (listContactInfo.count() != 0) { const int defaultContext(QContactDetail::ContextOther); const int homeContext(QContactDetail::ContextHome); const int workContext(QContactDetail::ContextWork); QContactOrganization organizationDetail; QContactName nameDetail; QString formattedName; bool structuredName = false; // Add any information reported by telepathy foreach (const Tp::ContactInfoField &field, listContactInfo) { if (field.fieldValue.count() == 0) { continue; } // Extract field types QStringList subTypes; int detailContext = -1; const int invalidContext = -1; foreach (const QString ¶m, field.parameters) { if (!param.startsWith(QLatin1String("type="))) { continue; } const QString type = param.mid(5); if (type == QLatin1String("home")) { detailContext = homeContext; } else if (type == QLatin1String("work")) { detailContext = workContext; } else if (!subTypes.contains(type)){ subTypes << type; } } if (field.fieldName == QLatin1String("tel")) { QList selectedTypes; foreach (const QString &type, subTypes) { Dictionary::const_iterator it = phoneTypes().find(type.toLower()); if (it != phoneTypes().constEnd()) { selectedTypes.append(*it); } } if (selectedTypes.isEmpty()) { // Assume landline selectedTypes.append(QContactPhoneNumber::SubTypeLandline); } QContactPhoneNumber phoneNumberDetail; phoneNumberDetail.setContexts(detailContext == invalidContext ? defaultContext : detailContext); phoneNumberDetail.setNumber(asString(field, 0).trimmed()); phoneNumberDetail.setSubTypes(selectedTypes); newPhoneNumbers.append(phoneNumberDetail); } else if (field.fieldName == QLatin1String("adr")) { QList selectedTypes; foreach (const QString &type, subTypes) { Dictionary::const_iterator it = addressTypes().find(type.toLower()); if (it != addressTypes().constEnd()) { selectedTypes.append(*it); } } // QContactAddress does not support extended street address, so combine the fields QStringList streetParts; for (int i = 1; i <= 2; ++i) { QString part(asString(field, i).trimmed()); if (!part.isEmpty()) { streetParts.append(part); } } QContactAddress addressDetail; if (detailContext != invalidContext) { addressDetail.setContexts(detailContext); } if (selectedTypes.isEmpty()) { addressDetail.setSubTypes(selectedTypes); } addressDetail.setPostOfficeBox(asString(field, 0).trimmed()); addressDetail.setStreet(streetParts.join(QString::fromLatin1("\n"))); addressDetail.setLocality(asString(field, 3).trimmed()); addressDetail.setRegion(asString(field, 4).trimmed()); addressDetail.setPostcode(asString(field, 5).trimmed()); addressDetail.setCountry(asString(field, 6).trimmed()); newAddresses.append(addressDetail); } else if (field.fieldName == QLatin1String("email")) { QContactEmailAddress emailDetail; if (detailContext != invalidContext) { emailDetail.setContexts(detailContext); } emailDetail.setEmailAddress(asString(field, 0).trimmed()); newEmailAddresses.append(emailDetail); } else if (field.fieldName == QLatin1String("url")) { QContactUrl urlDetail; if (detailContext != invalidContext) { urlDetail.setContexts(detailContext); } urlDetail.setUrl(asString(field, 0).trimmed()); newUrls.append(urlDetail); } else if (field.fieldName == QLatin1String("title")) { organizationDetail.setTitle(asString(field, 0).trimmed()); if (detailContext != invalidContext) { organizationDetail.setContexts(detailContext); } } else if (field.fieldName == QLatin1String("role")) { organizationDetail.setRole(asString(field, 0).trimmed()); if (detailContext != invalidContext) { organizationDetail.setContexts(detailContext); } } else if (field.fieldName == QLatin1String("org")) { organizationDetail.setName(asString(field, 0).trimmed()); organizationDetail.setDepartment(asStringList(field, 1)); if (detailContext != invalidContext) { organizationDetail.setContexts(detailContext); } newOrganizations.append(organizationDetail); // Clear out the stored details organizationDetail = QContactOrganization(); } else if (field.fieldName == QLatin1String("n")) { if (detailContext != invalidContext) { nameDetail.setContexts(detailContext); } replaceNameDetail(&QContactName::lastName, &QContactName::setLastName, &nameDetail, asString(field, 0).trimmed()); replaceNameDetail(&QContactName::firstName, &QContactName::setFirstName, &nameDetail, asString(field, 1).trimmed()); replaceNameDetail(&QContactName::middleName, &QContactName::setMiddleName, &nameDetail, asString(field, 2).trimmed()); replaceNameDetail(&QContactName::prefix, &QContactName::setPrefix, &nameDetail, asString(field, 3).trimmed()); replaceNameDetail(&QContactName::suffix, &QContactName::setSuffix, &nameDetail, asString(field, 4).trimmed()); structuredName = true; } else if (field.fieldName == QLatin1String("fn")) { const QString fn(asString(field, 0).trimmed()); if (!fn.isEmpty()) { if (detailContext != invalidContext) { nameDetail.setContexts(detailContext); } formattedName = fn; } } else if (field.fieldName == QLatin1String("nickname")) { const QString nickname(asString(field, 0).trimmed()); if (!nickname.isEmpty()) { QContactNickname nicknameDetail; nicknameDetail.setNickname(nickname); if (detailContext != invalidContext) { nicknameDetail.setContexts(detailContext); } newNicknames.append(nicknameDetail); // Use the nickname as the customLabel if we have no 'fn' data if (formattedName.isEmpty()) { formattedName = nickname; } } } else if (field.fieldName == QLatin1String("note") || field.fieldName == QLatin1String("desc")) { QContactNote noteDetail; if (detailContext != invalidContext) { noteDetail.setContexts(detailContext); } noteDetail.setNote(asString(field, 0).trimmed()); newNotes.append(noteDetail); } else if (field.fieldName == QLatin1String("bday")) { /* FIXME: support more date format for compatibility */ const QString dateText(asString(field, 0)); QDate date = QDate::fromString(dateText, QLatin1String("yyyy-MM-dd")); if (!date.isValid()) { date = QDate::fromString(dateText, QLatin1String("yyyyMMdd")); } if (!date.isValid()) { date = QDate::fromString(dateText, Qt::ISODate); } if (date.isValid()) { QContactBirthday birthdayDetail; birthdayDetail.setDate(date); newBirthday = birthdayDetail; } else { qCDebug(lcContactsd) << "Unsupported bday format:" << field.fieldValue[0]; } } else if (field.fieldName == QLatin1String("x-gender")) { const QString type(field.fieldValue.at(0)); Dictionary::const_iterator it = genderTypes().find(type.toLower()); if (it != addressTypes().constEnd()) { QContactGender genderDetail; genderDetail.setGender(static_cast(*it)); newGender = genderDetail; } else { qCDebug(lcContactsd) << "Unsupported gender type:" << type; } } else { qCDebug(lcContactsd) << "Unsupported contact info field" << field.fieldName; } } if (structuredName || !formattedName.isEmpty()) { if (!structuredName) { SeasideCache::decomposeDisplayLabel(formattedName, &nameDetail); } if (!formattedName.isEmpty()) { nameDetail.setValue(QContactName::FieldCustomLabel, formattedName); } newName = nameDetail; } } // For all detail types, test if there has been any change bool changed = false; const QList oldAddresses = existing.details(); if (detailListsDiffer(oldAddresses, newAddresses, [](const QContactAddress &oldAddress, const QContactAddress &newAddress) { if ((oldAddress.contexts() != newAddress.contexts()) || (oldAddress.subTypes() != newAddress.subTypes()) || (oldAddress.postOfficeBox() != newAddress.postOfficeBox()) || (oldAddress.street() != newAddress.street()) || (oldAddress.locality() != newAddress.locality()) || (oldAddress.region() != newAddress.region()) || (oldAddress.postcode() != newAddress.postcode()) || (oldAddress.country() != newAddress.country())) { return true; } return false; }) ) { changed |= replaceDetails(existing, newAddresses, contactAddress, SRC_LOC); } QContactBirthday oldBirthday = existing.detail(); if (!oldBirthday.isEmpty() && newBirthday.isEmpty()) { deleteContactDetails(existing); } else if ((oldBirthday.isEmpty() && !newBirthday.isEmpty()) || (oldBirthday.date() != newBirthday.date())) { changed |= replaceDetails(existing, newBirthday, contactAddress, SRC_LOC); } const QList oldEmailAddresses = existing.details(); if (detailListsDiffer(oldEmailAddresses, newEmailAddresses, [](const QContactEmailAddress &oldEmailAddress, const QContactEmailAddress &newEmailAddress) { if ((oldEmailAddress.contexts() != newEmailAddress.contexts()) || (oldEmailAddress.emailAddress() != newEmailAddress.emailAddress())) { return true; } return false; }) ) { changed |= replaceDetails(existing, newEmailAddresses, contactAddress, SRC_LOC); } QContactGender oldGender = existing.detail(); if (!oldGender.isEmpty() && newGender.isEmpty()) { deleteContactDetails(existing); } else if ((oldGender.isEmpty() && !newGender.isEmpty()) || (oldGender.gender() != newGender.gender())) { changed |= replaceDetails(existing, newGender, contactAddress, SRC_LOC); } QContactName oldName = existing.detail(); if ((oldName.firstName() != newName.firstName()) || (oldName.middleName() != newName.middleName()) || (oldName.lastName() != newName.lastName()) || (oldName.value(QContactName::FieldCustomLabel) != newName.value(QContactName::FieldCustomLabel)) || (oldName.prefix() != newName.prefix()) || (oldName.suffix() != newName.suffix())) { changed |= replaceDetails(existing, newName, contactAddress, SRC_LOC); } // Nicknames are different to other list types, since they can come from the presence info as well const QList oldNicknames = existing.details(); foreach (QContactNickname newNickname, newNicknames) { bool found = false; foreach (const QContactNickname &oldNickname, oldNicknames) { if ((oldNickname.contexts() == newNickname.contexts()) && (oldNickname.nickname() == newNickname.nickname())) { // Nickname already present found = true; break; } } if (!found) { // Add this nickname if (!storeContactDetail(existing, newNickname, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save nickname to contact for:" << contactAddress; } changed = true; } } const QList oldNotes = existing.details(); if (detailListsDiffer(oldNotes, newNotes, [](const QContactNote &oldNote, const QContactNote &newNote) { if ((oldNote.contexts() != newNote.contexts()) || (oldNote.note() != newNote.note())) { return true; } return false; }) ) { changed |= replaceDetails(existing, newNotes, contactAddress, SRC_LOC); } const QList oldOrganizations = existing.details(); if (detailListsDiffer(oldOrganizations, newOrganizations, [](const QContactOrganization &oldOrganization, const QContactOrganization &newOrganization) { if ((oldOrganization.contexts() != newOrganization.contexts()) || (oldOrganization.name() != newOrganization.name()) || (oldOrganization.department() != newOrganization.department())) { return true; } return false; }) ) { changed |= replaceDetails(existing, newOrganizations, contactAddress, SRC_LOC); } const QList oldPhoneNumbers = existing.details(); if (detailListsDiffer(oldPhoneNumbers, newPhoneNumbers, [](const QContactPhoneNumber &oldPhoneNumber, const QContactPhoneNumber &newPhoneNumber) { if ((oldPhoneNumber.contexts() != newPhoneNumber.contexts()) || (oldPhoneNumber.subTypes() != newPhoneNumber.subTypes()) || (oldPhoneNumber.number() != newPhoneNumber.number())) { return true; } return false; }) ) { changed |= replaceDetails(existing, newPhoneNumbers, contactAddress, SRC_LOC); } const QList oldUrls = existing.details(); if (detailListsDiffer(oldUrls, newUrls, [](const QContactUrl &oldUrl, const QContactUrl &newUrl) { if ((oldUrl.contexts() != newUrl.contexts()) || (oldUrl.url() != newUrl.url())) { return true; } return false; }) ) { changed |= replaceDetails(existing, newUrls, contactAddress, SRC_LOC); } if (changed) { contactChanges |= CDTpContact::Information; } } } if (changes & CDTpContact::Avatar) { // Prefer the large avatar if available QString avatarPath(contactWrapper->largeAvatarPath()); if (avatarPath.isEmpty()) { avatarPath = contact->avatarData().fileName; } QContactAvatar avatar = existing.detail(); if (avatarPath.isEmpty()) { if (!avatar.isEmpty()) { // Remove the avatar detail if (!existing.removeDetail(&avatar)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to remove avatar from contact:" << contactAddress; } contactChanges |= CDTpContact::Avatar; } } else { QUrl avatarUrl(QUrl::fromLocalFile(avatarPath)); if (avatarUrl != avatar.imageUrl()) { avatar.setImageUrl(avatarUrl); QContactOnlineAccount qcoa = existing.detail(); avatar.setLinkedDetailUris(qcoa.detailUri()); if (!storeContactDetail(existing, avatar, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save avatar for contact:" << contactAddress; } contactChanges |= CDTpContact::Avatar; } } } if (changes & CDTpContact::DefaultAvatar) { updateSocialAvatars(network, contactWrapper); } /* What is this about? if (changes & CDTpContact::Authorization) { qCDebug(lcContactsd) << " authorization changed"; g.addPattern(imAddress, nco::imAddressAuthStatusFrom::resource(), presenceState(contact->subscriptionState())); g.addPattern(imAddress, nco::imAddressAuthStatusTo::resource(), presenceState(contact->publishState())); } */ return contactChanges; } template QList forEachItem(const QList &list, R (*f)(const T&)) { QList rv; rv.reserve(list.count()); foreach (const T &item, list) { const R& r = f(item); rv.append(r); } return rv; } QString extractAccountPath(const CDTpAccountPtr &accountWrapper) { return imAccount(accountWrapper); } void addIconPath(QContactOnlineAccount &qcoa, Tp::AccountPtr account) { QString iconName = account->iconName().trimmed(); // Ignore any default value returned by telepathy if (!iconName.startsWith(QLatin1String("im-"))) { qcoa.setValue(QContactOnlineAccount__FieldAccountIconPath, iconName); } } } // namespace CDTpStorage::CDTpStorage(QObject *parent) : QObject(parent) , mDevicePresence(new CDTpDevicePresence) , mDisplayLabelOrder(FirstNameFirst) , mDisplayLabelOrderConf(QStringLiteral("/org/nemomobile/contacts/display_label_order")) { connect(mDevicePresence, SIGNAL(requestUpdate()), this, SLOT(reportPresenceStates())); connect(&mDisplayLabelOrderConf, SIGNAL(valueChanged()), this, SLOT(displayLabelOrderChanged())); QVariant displayLabelOrder = mDisplayLabelOrderConf.value(); if (displayLabelOrder.isValid()) mDisplayLabelOrder = static_cast(displayLabelOrder.toInt()); mUpdateTimer.setInterval(UPDATE_TIMEOUT); mUpdateTimer.setSingleShot(true); connect(&mUpdateTimer, SIGNAL(timeout()), SLOT(onUpdateQueueTimeout())); mWaitTimer.invalidate(); } CDTpStorage::~CDTpStorage() { } /* Set generic account properties of a QContactOnlineAccount. Does not set: * detailUri * linkedDetailUris (i.e. presence) * enabled * accountUri */ static void updateContactAccount(QContactOnlineAccount &qcoa, CDTpAccountPtr accountWrapper) { Tp::AccountPtr account = accountWrapper->account(); qcoa.setValue(QContactOnlineAccount__FieldAccountPath, imAccount(account)); qcoa.setProtocol(protocolType(account->protocolName())); qcoa.setServiceProvider(account->serviceName()); QString providerDisplayName = accountWrapper->storageInfo().value(QLatin1String("providerDisplayName")).toString(); qcoa.setValue(QContactOnlineAccount__FieldServiceProviderDisplayName, providerDisplayName); qcoa.setValue(QContactOnlineAccount__FieldAccountDisplayName, account->displayName()); addIconPath(qcoa, account); } void CDTpStorage::addNewAccount() { CDTpAccount *account = qobject_cast(sender()); if (!account) return; // Disconnect the signal disconnect(account, SIGNAL(readyChanged()), this, SLOT(addNewAccount())); // Create a new contact collection for this account const QContactCollectionId collectionId = telepathyCollectionId(imAccount(account)); QContact self(selfContact(collectionId)); qCDebug(lcContactsd) << "New account" << imAccount(account) << "is ready, calling delayed addNewAccount"; addNewAccount(self, CDTpAccountPtr(account)); } void CDTpStorage::addNewAccount(QContact &self, CDTpAccountPtr accountWrapper) { Tp::AccountPtr account = accountWrapper->account(); const QString accountPath(imAccount(account)); const QString accountAddress(imAddress(account)); const QString accountPresence(imPresence(account)); if (!accountWrapper->isReady()) { qCDebug(lcContactsd) << "Waiting to create new self account" << accountPath << "until ready"; connect(accountWrapper.data(), SIGNAL(readyChanged()), SLOT(addNewAccount())); return; } qCDebug(lcContactsd) << "Creating new self account - account:" << accountPath << "address:" << accountAddress; // Create a new QCOA for this account QContactOnlineAccount newAccount; updateContactAccount(newAccount, accountWrapper); newAccount.setDetailUri(accountAddress); newAccount.setLinkedDetailUris(accountPresence); newAccount.setValue(QContactOnlineAccount__FieldEnabled, asString(account->isEnabled())); newAccount.setAccountUri(account->normalizedName()); // Add the new account to the self contact if (!storeContactDetail(self, newAccount, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to add account to self contact for:" << accountPath; return; } // Create a presence detail for this account QContactPresence presence; presence.setDetailUri(accountPresence); presence.setLinkedDetailUris(accountAddress); presence.setPresenceState(qContactPresenceState(Tp::ConnectionPresenceTypeUnknown)); if (!storeContactDetail(self, presence, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to add presence to self contact for:" << accountPath; return; } // Store any information from the account CDTpContact::Changes selfChanges = updateAccountDetails(mDevicePresence, self, newAccount, presence, accountWrapper, CDTpAccount::All); storeSelfContact(mDevicePresence, self, SRC_LOC, selfChanges); } void CDTpStorage::removeExistingAccount(QContact &self, QContactOnlineAccount &existing) { Q_UNUSED(self) const QString accountPath(stringValue(existing, QContactOnlineAccount__FieldAccountPath)); qCDebug(lcContactsd) << "Remove account for path" << accountPath << " and collection id" << telepathyCollectionId(accountPath); // Delete the collection and its contacts. QtContactsSqliteExtensions::ContactManagerEngine *cme = QtContactsSqliteExtensions::contactManagerEngine(*manager()); QContactManager::Error error = QContactManager::NoError; if (!cme->storeChanges(nullptr, nullptr, QList() << telepathyCollectionId(accountPath), QtContactsSqliteExtensions::ContactManagerEngine::PreserveLocalChanges, true, &error)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to remove linked contacts for account:" << accountPath << "error:" << error; } } bool CDTpStorage::initializeNewContact(QContact &newContact, CDTpAccountPtr accountWrapper, const QString &contactId, const QString &alias) { Tp::AccountPtr account = accountWrapper->account(); const QString accountPath(imAccount(account)); const QString contactAddress(imAddress(account, contactId)); const QString contactPresence(imPresence(account, contactId)); qCDebug(lcContactsd) << "Creating new contact - address:" << contactAddress; // This contact belongs to a telepathy address book newContact.setCollectionId(telepathyCollectionId(accountPath)); // Create a metadata field to link the contact with the telepathy data QContactOriginMetadata metadata; metadata.setId(contactAddress); metadata.setGroupId(imAccount(account)); metadata.setEnabled(true); if (!storeContactDetail(newContact, metadata, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to add metadata to contact:" << contactAddress; return false; } // Create a new QCOA for this contact QContactOnlineAccount newAccount; updateContactAccount(newAccount, accountWrapper); newAccount.setDetailUri(contactAddress); newAccount.setLinkedDetailUris(contactPresence); newAccount.setValue(QContactOnlineAccount__FieldEnabled, asString(true)); newAccount.setAccountUri(contactId); // Add the new account to the contact if (!storeContactDetail(newContact, newAccount, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save account to contact for:" << contactAddress; return false; } // Create a presence detail for this contact QContactPresence presence; presence.setDetailUri(contactPresence); presence.setLinkedDetailUris(contactAddress); presence.setPresenceState(qContactPresenceState(Tp::ConnectionPresenceTypeUnknown)); if (!alias.isEmpty()) { presence.setNickname(alias); } if (!storeContactDetail(newContact, presence, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save presence to contact for:" << contactAddress; return false; } // Initially we will have no name detail - try to extract it from the alias if (!alias.isEmpty()) { QContactName name; SeasideCache::decomposeDisplayLabel(alias, &name); name.setValue(QContactName::FieldCustomLabel, alias); if (!storeContactDetail(newContact, name, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save name to contact for:" << contactAddress; return false; } } return true; } bool CDTpStorage::initializeNewContact(QContact &newContact, CDTpContactPtr contactWrapper) { CDTpAccountPtr accountWrapper = contactWrapper->accountWrapper(); Tp::ContactPtr contact = contactWrapper->contact(); const QString id(contact->id()); const QString alias(contact->alias().trimmed()); return initializeNewContact(newContact, accountWrapper, id, alias); } void CDTpStorage::updateContactChanges(CDTpContactPtr contactWrapper, CDTpContact::Changes changes) { ContactChangeSet saveSet; QList removeList; QContact existing = findExistingContact(imAddress(contactWrapper), telepathyCollectionId(imAccount(contactWrapper))); updateContactChanges(contactWrapper, changes, existing, &saveSet, &removeList); updateContacts(SRC_LOC, &saveSet, &removeList); } void CDTpStorage::updateContactChanges(CDTpContactPtr contactWrapper, CDTpContact::Changes changes, QContact &existing, ContactChangeSet *saveSet, QList *removeList) { const QString accountPath(imAccount(contactWrapper)); const QString contactAddress(imAddress(contactWrapper)); if (changes & CDTpContact::Deleted) { // This contact has been deleted if (!existing.isEmpty()) { removeList->append(existing.id()); } } else { bool needAllChanges = false; if (existing.isEmpty()) { if (!initializeNewContact(existing, contactWrapper)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to create contact for account:" << accountPath << contactAddress; return; } needAllChanges = true; } changes = updateContactDetails(mNetwork, existing, contactWrapper, changes); if (needAllChanges) changes = CDTpContact::All; appendContactChange(saveSet, existing, changes); } } QList accountContacts(CDTpAccountPtr accountWrapper) { QList rv; QSet ids; foreach (CDTpContactPtr contactWrapper, accountWrapper->contacts()) { const QString id(contactWrapper->contact()->id()); if (ids.contains(id)) continue; ids.insert(id); rv.append(contactWrapper); } return rv; } void CDTpStorage::updateAccount() { CDTpAccount *account = qobject_cast(sender()); if (!account) return; // Disconnect the signal disconnect(account, SIGNAL(readyChanged()), this, SLOT(updateAccount())); const QString accountPath(imAccount(account)); qCDebug(lcContactsd) << "Delayed update of account" << accountPath << "is ready"; CDTpAccount::Changes changes = CDTpAccount::All; QMap::iterator it = m_accountPendingChanges.find(accountPath); if (it != m_accountPendingChanges.end()) { changes = it.value(); m_accountPendingChanges.erase(it); } updateAccount(CDTpAccountPtr(account), changes); } void CDTpStorage::updateAccountChanges(QContact &self, QContactOnlineAccount &qcoa, CDTpAccountPtr accountWrapper, CDTpAccount::Changes changes) { Tp::AccountPtr account = accountWrapper->account(); const QString accountPath(imAccount(account)); const QString accountAddress(imAddress(account)); if (!accountWrapper->isReady()) { qCDebug(lcContactsd) << "Delaying update of account" << accountPath << "address" << accountAddress << "until ready"; QMap::iterator it = m_accountPendingChanges.find(accountPath); if (it != m_accountPendingChanges.end()) { it.value() |= changes; } else { m_accountPendingChanges.insert(accountPath, changes); connect(accountWrapper.data(), SIGNAL(readyChanged()), SLOT(updateAccount())); } return; } qCDebug(lcContactsd) << "Synchronizing self account - account:" << accountPath << "address:" << accountAddress; QContactPresence presence(findPresenceForAccount(self, qcoa)); if (presence.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to find presence to match account:" << accountPath; } CDTpContact::Changes selfChanges = updateAccountDetails(mDevicePresence, self, qcoa, presence, accountWrapper, changes); if (!storeSelfContact(mDevicePresence, self, SRC_LOC, selfChanges)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save self contact - error:" << manager()->error(); } if (account->isEnabled() && accountWrapper->hasRoster()) { QHash allChanges; // Update all contacts reported in the roster changes of this account const QHash rosterChanges = accountWrapper->rosterChanges(); QHash::ConstIterator it = rosterChanges.constBegin(), end = rosterChanges.constEnd(); for ( ; it != end; ++it) { const QString address = imAddress(accountPath, it.key()); CDTpContact::Changes flags = it.value() | CDTpContact::Presence; // If account display name changes, update QCOA of all contacts if (changes & CDTpAccount::DisplayName) flags |= CDTpContact::Capabilities; // We always update contact presence since this method is called after a presence change allChanges.insert(address, flags); } QList tpContacts(accountContacts(accountWrapper)); QStringList contactAddresses; foreach (const CDTpContactPtr &contactWrapper, tpContacts) { const QString address = imAddress(accountPath, contactWrapper->contact()->id()); contactAddresses.append(address); } // Retrieve the existing contacts in a single batch QHash existingContacts = findExistingContacts( contactAddresses, telepathyCollectionId(imAccount(accountWrapper))); ContactChangeSet saveSet; QList removeList; foreach (const CDTpContactPtr &contactWrapper, tpContacts) { const QString address = imAddress(accountPath, contactWrapper->contact()->id()); QHash::Iterator cit = allChanges.find(address); if (cit == allChanges.end()) { qCWarning(lcContactsd) << SRC_LOC << "No changes found for contact:" << address; continue; } CDTpContact::Changes changes = *cit; QHash::Iterator existing = existingContacts.find(address); if (existing == existingContacts.end()) { qCWarning(lcContactsd) << SRC_LOC << "No contact found for address:" << address; existing = existingContacts.insert(address, QContact()); changes |= CDTpContact::All; } // If we got a contact without avatar in the roster, and the original // had an avatar, then ignore the avatar update (some contact managers // send the initial roster with the avatar missing) // Contact updates that have a null avatar will clear the avatar though if (changes & CDTpContact::DefaultAvatar) { if (((changes & CDTpContact::All) != CDTpContact::All) && contactWrapper->contact()->avatarData().fileName.isEmpty()) { changes &= ~CDTpContact::DefaultAvatar; } } updateContactChanges(contactWrapper, changes, *existing, &saveSet, &removeList); } updateContacts(SRC_LOC, &saveSet, &removeList); } else { ContactChangeSet saveSet; const QContactPresence::PresenceState newState(qContactPresenceState(Tp::ConnectionPresenceTypeUnknown)); const QStringList newCapabilities(currentCapabilites(account->capabilities(), Tp::ConnectionPresenceTypeUnknown, account)); // Set presence to unknown for all contacts of this account QContactFetchHint hint(contactFetchHint(DetailList() << detailType() << detailType() << detailType())); foreach (QContact existing, manager()->contacts(findContactIdsForAccount(accountPath), hint)) { const QContactId &contactId(existing.id()); CDTpContact::Changes changes; QContactPresence presence = existing.detail(); if (presence.presenceState() != newState) { presence.setPresenceState(newState); presence.setTimestamp(QDateTime::currentDateTime()); if (!storeContactDetail(existing, presence, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save unknown presence to contact for:" << contactId; } changes |= CDTpContact::Presence; } // Also reset the capabilities QContactOnlineAccount qcoa = existing.detail(); if (qcoa.capabilities() != newCapabilities || onlineAccountEnabled(qcoa)) { qcoa.setCapabilities(newCapabilities); qcoa.setValue(QContactOnlineAccount__FieldEnabled, asString(false)); if (!storeContactDetail(existing, qcoa, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to save capabilities to contact for:" << contactId; } changes |= CDTpContact::Capabilities; } if (!account->isEnabled()) { // Mark the contact as un-enabled also QContactOriginMetadata metadata = existing.detail(); if (metadata.enabled()) { metadata.setEnabled(false); if (!storeContactDetail(existing, metadata, SRC_LOC)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to un-enable contact for:" << contactId; } changes |= CDTpContact::Capabilities; } } appendContactChange(&saveSet, existing, changes); } updateContacts(SRC_LOC, &saveSet, 0); } } void CDTpStorage::syncAccounts(const QList &accounts) { qWarning() << "CDTpStorage: syncAccounts:" << accounts.count(); for (CDTpAccountPtr accountWrapper : accounts) { QContact self(selfContact(telepathyCollectionId(imAccount(accountWrapper)))); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact - error:" << manager()->error(); return; } syncAccountsForSelfContact(accounts, self); } } void CDTpStorage::syncAccountsForSelfContact(const QList &accounts, QContact &self) { // Find the list of paths for the accounts we now have QStringList accountPaths = forEachItem(accounts, extractAccountPath); qWarning() << "CDTpStorage: syncAccountsForSelfContact:" << accountPaths; QSet existingIndices; QSet removalPaths; foreach (QContactOnlineAccount existingAccount, self.details()) { const QString existingPath(stringValue(existingAccount, QContactOnlineAccount__FieldAccountPath)); if (existingPath.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "No path for existing account:" << existingPath; continue; } int index = accountPaths.indexOf(existingPath); if (index != -1) { existingIndices.insert(index); updateAccountChanges(self, existingAccount, accounts.at(index), CDTpAccount::All); } else { qCDebug(lcContactsd) << SRC_LOC << "Remove obsolete account:" << existingPath; // This account is no longer valid removalPaths.insert(existingPath); } } // Remove invalid accounts foreach (QContactOnlineAccount existingAccount, self.details()) { const QString existingPath(stringValue(existingAccount, QContactOnlineAccount__FieldAccountPath)); if (removalPaths.contains(existingPath)) { removeExistingAccount(self, existingAccount); } } // Add any previously unknown accounts for (int i = 0; i < accounts.length(); ++i) { if (!existingIndices.contains(i)) { addNewAccount(self, accounts.at(i)); } } storeSelfContact(mDevicePresence, self, SRC_LOC, CDTpContact::All, true); } void CDTpStorage::createAccount(CDTpAccountPtr accountWrapper) { QContact self(selfContact(telepathyCollectionId(imAccount(accountWrapper)))); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact:" << manager()->error(); return; } const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: createAccount:" << accountPath; // Ensure this account does not already exist foreach (const QContactOnlineAccount &existingAccount, self.details()) { const QString existingPath(stringValue(existingAccount, QContactOnlineAccount__FieldAccountPath)); if (existingPath == accountPath) { qCWarning(lcContactsd) << SRC_LOC << "Path already exists for create account:" << existingPath; return; } } // Add any previously unknown accounts addNewAccount(self, accountWrapper); QList tpContacts(accountContacts(accountWrapper)); QStringList contactAddresses; foreach (const CDTpContactPtr &contactWrapper, tpContacts) { const QString address = imAddress(accountPath, contactWrapper->contact()->id()); contactAddresses.append(address); } // Retrieve the existing contacts in a single batch QHash existingContacts = findExistingContacts( contactAddresses, telepathyCollectionId(imAccount(accountWrapper))); ContactChangeSet saveSet; QList removeList; // Add any contacts already present for this account foreach (const CDTpContactPtr &contactWrapper, tpContacts) { const QString address = imAddress(accountPath, contactWrapper->contact()->id()); QHash::Iterator existing = existingContacts.find(address); if (existing == existingContacts.end()) { qCWarning(lcContactsd) << SRC_LOC << "No contact found for address:" << address; existing = existingContacts.insert(address, QContact()); } updateContactChanges(contactWrapper, CDTpContact::All, *existing, &saveSet, &removeList); } updateContacts(SRC_LOC, &saveSet, &removeList); } void CDTpStorage::updateAccount(CDTpAccountPtr accountWrapper, CDTpAccount::Changes changes) { QContact self(selfContact(telepathyCollectionId(imAccount(accountWrapper)))); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact:" << manager()->error(); return; } const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: updateAccount:" << accountPath << asString(changes); foreach (QContactOnlineAccount existingAccount, self.details()) { const QString existingPath(stringValue(existingAccount, QContactOnlineAccount__FieldAccountPath)); if (existingPath == accountPath) { updateAccountChanges(self, existingAccount, accountWrapper, changes); return; } } qCWarning(lcContactsd) << SRC_LOC << "Account not found for update account:" << accountPath; } void CDTpStorage::removeAccount(CDTpAccountPtr accountWrapper) { cancelQueuedUpdates(accountContacts(accountWrapper)); QContact self(selfContact(telepathyCollectionId(imAccount(accountWrapper)))); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact:" << manager()->error(); return; } QContact globalSelf = manager()->contact(manager()->selfContactId()); const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: removeAccount:" << accountPath; foreach (QContactOnlineAccount existingAccount, self.details()) { const QString existingPath(stringValue(existingAccount, QContactOnlineAccount__FieldAccountPath)); if (existingPath == accountPath) { removeExistingAccount(self, existingAccount); emitAccountChanges(mDevicePresence, globalSelf, true); return; } } qCWarning(lcContactsd) << SRC_LOC << "Account not found for remove account:" << accountPath; } // This is called when account goes online/offline void CDTpStorage::syncAccountContacts(CDTpAccountPtr accountWrapper) { QContact self(selfContact(telepathyCollectionId(imAccount(accountWrapper)))); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact:" << manager()->error(); return; } const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: syncAccountContacts (roster change):" << accountPath; foreach (QContactOnlineAccount existingAccount, self.details()) { const QString existingPath(stringValue(existingAccount, QContactOnlineAccount__FieldAccountPath)); if (existingPath == accountPath) { updateAccountChanges(self, existingAccount, accountWrapper, CDTpAccount::Enabled); return; } } qCWarning(lcContactsd) << SRC_LOC << "Account not found for sync account:" << accountPath; } void CDTpStorage::syncAccountContacts(CDTpAccountPtr accountWrapper, const QList &contactsAdded, const QList &contactsRemoved) { const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: syncAccountContacts (roster update):" << accountPath << contactsAdded.count() << contactsRemoved.count(); // Ensure there are no duplicates in the list QList addedContacts(contactsAdded.toSet().toList()); QList removedContacts(contactsRemoved.toSet().toList()); QSet contactAddresses; foreach (const CDTpContactPtr &contactWrapper, addedContacts) { // This contact must be for the specified account if (imAccount(contactWrapper) != accountPath) { qCWarning(lcContactsd) << SRC_LOC << "Unable to add contact from wrong account:" << imAccount(contactWrapper) << accountPath; continue; } const QString address = imAddress(accountPath, contactWrapper->contact()->id()); contactAddresses.insert(address); } foreach (const CDTpContactPtr &contactWrapper, removedContacts) { if (imAccount(contactWrapper) != accountPath) { qCWarning(lcContactsd) << SRC_LOC << "Unable to remove contact from wrong account:" << imAccount(contactWrapper) << accountPath; continue; } const QString address = imAddress(accountPath, contactWrapper->contact()->id()); contactAddresses.insert(address); } // Retrieve the existing contacts in a single batch QHash existingContacts = findExistingContacts( contactAddresses, telepathyCollectionId(imAccount(accountWrapper))); ContactChangeSet saveSet; QList removeList; foreach (const CDTpContactPtr &contactWrapper, addedContacts) { const QString address = imAddress(accountPath, contactWrapper->contact()->id()); CDTpContact::Changes changes = CDTpContact::Information; QHash::Iterator existing = existingContacts.find(address); if (existing == existingContacts.end()) { qCWarning(lcContactsd) << SRC_LOC << "No contact found for address:" << address; existing = existingContacts.insert(address, QContact()); changes |= CDTpContact::All; } updateContactChanges(contactWrapper, changes, *existing, &saveSet, &removeList); } foreach (const CDTpContactPtr &contactWrapper, removedContacts) { const QString address = imAddress(accountPath, contactWrapper->contact()->id()); QHash::Iterator existing = existingContacts.find(address); if (existing == existingContacts.end()) { qCWarning(lcContactsd) << SRC_LOC << "No contact found for address:" << address; continue; } updateContactChanges(contactWrapper, CDTpContact::Deleted, *existing, &saveSet, &removeList); } updateContacts(SRC_LOC, &saveSet, &removeList); } void CDTpStorage::createAccountContacts(CDTpAccountPtr accountWrapper, const QStringList &imIds, uint localId) { Q_UNUSED(localId) // ??? const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: createAccountContacts:" << accountPath << imIds.count(); ContactChangeSet saveSet; foreach (const QString &id, imIds) { QContact newContact; if (!initializeNewContact(newContact, accountWrapper, id, QString())) { qCWarning(lcContactsd) << SRC_LOC << "Unable to create contact for account:" << accountPath << id; } else { appendContactChange(&saveSet, newContact, CDTpContact::All); } } updateContacts(SRC_LOC, &saveSet, 0); } /* Use this only in offline mode - use syncAccountContacts in online mode */ void CDTpStorage::removeAccountContacts(CDTpAccountPtr accountWrapper, const QStringList &contactIds) { const QString accountPath(imAccount(accountWrapper)); qWarning() << "CDTpStorage: removeAccountContacts:" << accountPath << contactIds.count(); QStringList imAddressList; foreach (const QString &id, contactIds) { imAddressList.append(imAddress(accountPath, id)); } QList removeIds; // Find any contacts matching the supplied ID list QContactFetchHint hint(contactFetchHint(DetailList() << detailType())); foreach (const QContact &existing, manager()->contacts(findContactIdsForAccount(accountPath), hint)) { QContactOriginMetadata metadata = existing.detail(); if (imAddressList.contains(metadata.id())) { removeIds.append(existing.id()); } } if (!manager()->removeContacts(removeIds)) { qCWarning(lcContactsd) << SRC_LOC << "Unable to remove contacts for account:" << accountPath << "error:" << manager()->error(); } } void CDTpStorage::updateContact(CDTpContactPtr contactWrapper, CDTpContact::Changes changes) { mUpdateQueue[contactWrapper] |= changes; // Only update IM contacts after not receiving an update notification for the defined period // Also use an upper limit to keep latency within acceptable bounds. if (mWaitTimer.isValid()) { if (mWaitTimer.elapsed() >= UPDATE_MAXIMUM_TIMEOUT) { // Don't prolong the wait any further return; } } else { mWaitTimer.start(); } mUpdateTimer.start(); } void CDTpStorage::onUpdateQueueTimeout() { mWaitTimer.invalidate(); qCDebug(lcContactsd) << "Update" << mUpdateQueue.count() << "contacts"; QHash updates; QSet contactAddresses; QHash::const_iterator it = mUpdateQueue.constBegin(), end = mUpdateQueue.constEnd(); for ( ; it != end; ++it) { CDTpContactPtr contactWrapper = it.key(); // If there are multiple entries for a contact, coalesce the changes updates[contactWrapper] |= it.value(); contactAddresses.insert(imAddress(contactWrapper)); } mUpdateQueue.clear(); // Retrieve the existing contacts QHash existingContacts; const QList telepathyCollections = allTelepathyCollections(); for (const QContactCollection &collection : telepathyCollections) { existingContacts.unite(findExistingContacts(contactAddresses, collection.id())); } ContactChangeSet saveSet; QList removeList; for (it = updates.constBegin(), end = updates.constEnd(); it != end; ++it) { CDTpContactPtr contactWrapper = it.key(); // Skip the contact in case its account was deleted before this function // was invoked if (contactWrapper->accountWrapper().isNull()) { continue; } if (!contactWrapper->isVisible()) { continue; } const QString address(imAddress(contactWrapper)); CDTpContact::Changes changes = it.value(); QHash::Iterator existing = existingContacts.find(address); if (existing == existingContacts.end()) { qCWarning(lcContactsd) << SRC_LOC << "No contact found for address:" << address; existing = existingContacts.insert(address, QContact()); changes |= CDTpContact::All; } updateContactChanges(contactWrapper, changes, *existing, &saveSet, &removeList); } updateContacts(SRC_LOC, &saveSet, &removeList); } void CDTpStorage::cancelQueuedUpdates(const QList &contacts) { foreach (const CDTpContactPtr &contactWrapper, contacts) { mUpdateQueue.remove(contactWrapper); } } void CDTpStorage::reportPresenceStates() { const QList telepathyCollections = allTelepathyCollections(); for (const QContactCollection &collection : telepathyCollections) { QContact self(selfContact(collection.id())); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact - error:" << manager()->error(); } else { reportPresenceState(self); } } } void CDTpStorage::reportPresenceState(QContact &self) { emit mDevicePresence->globalUpdate(self.detail().presenceState()); QStringList accountPaths; foreach (const QContactOnlineAccount &qcoa, self.details()) { QContactPresence presence(findPresenceForAccount(self, qcoa)); if (presence.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to find presence to match account:" << qcoa.detailUri(); } reportAccountPresence(mDevicePresence, qcoa, presence); accountPaths.append(qcoa.value(QContactOnlineAccount__FieldAccountPath)); } emit mDevicePresence->accountList(accountPaths); // Retrieve the aggregate self contact to report name details self = selfDetailsContact(); reportSelfDetails(mDevicePresence, self, mDisplayLabelOrder); } void CDTpStorage::displayLabelOrderChanged() { QVariant displayLabelOrder = mDisplayLabelOrderConf.value(); if (displayLabelOrder.isValid() && displayLabelOrder.toInt() != mDisplayLabelOrder) { mDisplayLabelOrder = static_cast(displayLabelOrder.toInt()); // Update our self details QContact self(selfDetailsContact()); if (self.isEmpty()) { qCWarning(lcContactsd) << SRC_LOC << "Unable to retrieve self contact - error:" << manager()->error(); return; } reportSelfDetails(mDevicePresence, self, mDisplayLabelOrder); } } // Instantiate the QContactOriginMetadata functions #include contactsd-1.4.14/plugins/telepathy/cdtpstorage.h000066400000000000000000000100541440357512200217360ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CDTPSTORAGE_H #define CDTPSTORAGE_H #include #include #include #include #include #include #include #include #include #include #include "cdtpaccount.h" #include "cdtpcontact.h" QTCONTACTS_USE_NAMESPACE class CDTpDevicePresence; class CDTpStorage : public QObject { Q_OBJECT public: typedef QMap > ContactChangeSet; enum DisplayLabelOrder { FirstNameFirst = 0, LastNameFirst }; CDTpStorage(QObject *parent = 0); ~CDTpStorage(); Q_SIGNALS: void error(int code, const QString &message); public Q_SLOTS: void syncAccounts(const QList &accounts); void syncAccountsForSelfContact(const QList &accounts, QContact &self); void createAccount(CDTpAccountPtr accountWrapper); void updateAccount(CDTpAccountPtr accountWrapper, CDTpAccount::Changes changes); void removeAccount(CDTpAccountPtr accountWrapper); void syncAccountContacts(CDTpAccountPtr accountWrapper); void syncAccountContacts(CDTpAccountPtr accountWrapper, const QList &contactsAdded, const QList &contactsRemoved); void updateContact(CDTpContactPtr contactWrapper, CDTpContact::Changes changes); void reportPresenceStates(); void reportPresenceState(QContact &self); public: void createAccountContacts(CDTpAccountPtr accountWrapper, const QStringList &imIds, uint localId); void removeAccountContacts(CDTpAccountPtr accountWrapper, const QStringList &contactIds); private Q_SLOTS: void onUpdateQueueTimeout(); void displayLabelOrderChanged(); void addNewAccount(); void updateAccount(); private: void cancelQueuedUpdates(const QList &contacts); void addNewAccount(QContact &self, CDTpAccountPtr accountWrapper); void removeExistingAccount(QContact &self, QContactOnlineAccount &existing); void updateAccountChanges(QContact &self, QContactOnlineAccount &qcoa, CDTpAccountPtr accountWrapper, CDTpAccount::Changes changes); bool initializeNewContact(QContact &newContact, CDTpAccountPtr accountWrapper, const QString &contactId, const QString &alias); bool initializeNewContact(QContact &newContact, CDTpContactPtr contactWrapper); void updateContactChanges(CDTpContactPtr contactWrapper, CDTpContact::Changes changes, QContact &existing, ContactChangeSet *saveList, QList *removeList); void updateContactChanges(CDTpContactPtr contactWrapper, CDTpContact::Changes changes); private: QNetworkAccessManager mNetwork; QHash mUpdateQueue; QTimer mUpdateTimer; QElapsedTimer mWaitTimer; QMap m_accountPendingChanges; CDTpDevicePresence *mDevicePresence; DisplayLabelOrder mDisplayLabelOrder; MGConfItem mDisplayLabelOrderConf; }; #endif // CDTPSTORAGE_H contactsd-1.4.14/plugins/telepathy/com.nokia.contacts.buddymanagement.xml000066400000000000000000000036331440357512200266330ustar00rootroot00000000000000 contactsd-1.4.14/plugins/telepathy/org.nemomobile.DevicePresenceIf.xml000066400000000000000000000023141440357512200260460ustar00rootroot00000000000000 contactsd-1.4.14/plugins/telepathy/telepathy.pro000066400000000000000000000050131440357512200217660ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../contacts-extensions.pri) include(../../lib.pri) TEMPLATE = lib QT -= gui QT += dbus network CONFIG += plugin link_pkgconfig CONFIG += c++11 PKGCONFIG += Qt5Contacts TelepathyQt5 mlite5 contactcache-qt5 system(qdbusxml2cpp -c BuddyManagementAdaptor -a buddymanagementadaptor com.nokia.contacts.buddymanagement.xml) system(qdbusxml2cpp -c DevicePresenceAdaptor -a devicepresenceadaptor org.nemomobile.DevicePresenceIf.xml) CONFIG(coverage):{ QMAKE_CXXFLAGS += -c -g --coverage -ftest-coverage -fprofile-arcs LIBS += -lgcov } DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII HEADERS = cdtpaccount.h \ cdtpaccountcache.h \ cdtpaccountcacheloader.h \ cdtpaccountcachewriter.h \ types.h \ cdtpcontact.h \ cdtpcontroller.h \ cdtpdevicepresence.h \ cdtpplugin.h \ cdtpstorage.h \ buddymanagementadaptor.h \ devicepresenceadaptor.h \ cdtpavatarupdate.h SOURCES = cdtpaccount.cpp \ cdtpaccountcacheloader.cpp \ cdtpaccountcachewriter.cpp \ cdtpcontact.cpp \ cdtpcontroller.cpp \ cdtpdevicepresence.cpp \ cdtpplugin.cpp \ cdtpstorage.cpp \ buddymanagementadaptor.cpp \ devicepresenceadaptor.cpp \ cdtpavatarupdate.cpp VERSIONED_PACKAGENAME=contactsd-1.0 TARGET = telepathyplugin target.path = $$LIBDIR/$${VERSIONED_PACKAGENAME}/plugins xml.files = com.nokia.contacts.buddymanagement.xml xml.path = $$INCLUDEDIR/$${VERSIONED_PACKAGENAME} INSTALLS += target xml OTHER_FILES += \ org.nemomobile.DevicePresenceIf.xml \ com.nokia.contacts.buddymanagement.xml contactsd-1.4.14/plugins/telepathy/types.h000066400000000000000000000023601440357512200205640ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef TYPES_H #define TYPES_H #include class CDTpAccount; class CDTpContact; typedef Tp::SharedPtr CDTpAccountPtr; typedef Tp::SharedPtr CDTpContactPtr; #endif contactsd-1.4.14/rpm/000077500000000000000000000000001440357512200143645ustar00rootroot00000000000000contactsd-1.4.14/rpm/contactsd.changes000066400000000000000000000017501440357512200177030ustar00rootroot00000000000000* Wed Feb 20 2013 Robin Burchell - 1.2.0 - Fork from upstream, merge existing patches back in - Fixes NEMO#517: contactsd goes nuts on upgrade - Removes dependency on qmsystem2 * Fri Nov 16 2012 John Brooks - 1.1.1 - Add 0001-Replace-meegotouch-with-mlite-in-unit-tests.patch - Remove meegotouch build dependency * Mon Oct 15 2012 Mohammed Hassan - 1.1.1 - requires tracker-sparql-0.14 for building against tracker 0.14.2 - Added Jens Georg's patch (0001-compile-against-tracker-0-14.patch) * Wed Sep 26 2012 John Brooks - 1.1.1 - Add 0001-Fix-header-path-for-new-versions-of-telepathy-qt4.patch - Add 0001-Use-mlocale-instead-of-meegotouch-for-birthday-plugi.patch * Sat Sep 22 2012 Carsten Munk - 1.1.1 - Add systemd user session service file * Thu Aug 09 2012 Bernd Wachter - 1.1.1 - Initial package for Nemo contactsd-1.4.14/rpm/contactsd.privileges000066400000000000000000000000301440357512200204320ustar00rootroot00000000000000/usr/bin/contactsd,acip contactsd-1.4.14/rpm/contactsd.spec000066400000000000000000000064611440357512200172310ustar00rootroot00000000000000Name: contactsd Version: 1.4.2 Release: 1 Summary: Telepathy <> QtContacts bridge for contacts URL: https://github.com/sailfishos/contactsd/ License: LGPLv2+ and (LGPLv2 or LGPLv2 with Nokia Qt LGPL Exception v1.1) Source0: %{name}-%{version}.tar.bz2 Source1: %{name}.privileges Requires: systemd Requires: systemd-user-session-targets BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5DBus) BuildRequires: pkgconfig(Qt5Network) BuildRequires: pkgconfig(Qt5Test) BuildRequires: pkgconfig(TelepathyQt5) BuildRequires: pkgconfig(Qt5Contacts) BuildRequires: pkgconfig(Qt5Versit) # mlite required only for tests BuildRequires: pkgconfig(mlite5) BuildRequires: pkgconfig(mlocale5) BuildRequires: pkgconfig(libmkcal-qt5) >= 0.6.10 BuildRequires: pkgconfig(KF5CalendarCore) BuildRequires: pkgconfig(telepathy-glib) BuildRequires: pkgconfig(qofono-qt5) BuildRequires: pkgconfig(qofonoext) BuildRequires: pkgconfig(qtcontacts-sqlite-qt5-extensions) >= 0.3.0 BuildRequires: pkgconfig(contactcache-qt5) BuildRequires: pkgconfig(dbus-glib-1) BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(accounts-qt5) BuildRequires: pkgconfig(systemd) BuildRequires: pkgconfig(buteosyncfw5) >= 0.6.33 BuildRequires: pkgconfig(qt5-boostable) BuildRequires: qt5-qttools BuildRequires: qt5-qttools-linguist Requires: libqofono-qt5 >= 0.67 Requires: mapplauncherd-qt5 %description %{name} is a service for collecting and observing changes in roster list from all the users telepathy accounts (buddies, their status and presence information), and store it to QtContacts. %files %defattr(-,root,root,-) %license LICENSE.LGPL %license LGPL_EXCEPTION.txt %{_userunitdir}/%{name}.service %{_userunitdir}/post-user-session.target.wants/%{name}.service %{_bindir}/%{name} %{_libdir}/%{name}-1.0 %{_datadir}/translations/*.qm %{_datadir}/mapplauncherd/privileges.d/* # we currently don't have a backup framework %exclude /usr/share/backup-framework/applications/%{name}.conf %{_libdir}/libcontactsd.so.* %package devel Summary: Development files for %{name} Requires: %{name} = %{version}-%{release} %description devel %{summary}. %files devel %defattr(-,root,root,-) %{_includedir}/%{name}-1.0 %{_libdir}/pkgconfig/*.pc %{_libdir}/libcontactsd.so %package tests Summary: Tests for %{name} Requires: %{name} = %{version}-%{release} Requires: blts-tools %description tests %{summary}. %files tests %defattr(-,root,root,-) /opt/tests/%{name} %package ts-devel Summary: Translation source for %{name} %description ts-devel Translation source for %{name} %files ts-devel %defattr(-,root,root,-) %{_datadir}/translations/source/*.ts %prep %setup -q -n %{name}-%{version} %build export QT_SELECT=5 export VERSION=%{version} ./configure --bindir %{_bindir} --libdir %{_libdir} --includedir %{_includedir} %qmake5 make %{?_smp_mflags} %install %qmake5_install mkdir -p %{buildroot}%{_userunitdir}/post-user-session.target.wants ln -s ../%{name}.service %{buildroot}%{_userunitdir}/post-user-session.target.wants/ mkdir -p %{buildroot}%{_datadir}/mapplauncherd/privileges.d install -m 644 -p %{SOURCE1} %{buildroot}%{_datadir}/mapplauncherd/privileges.d %post if [ "$1" -ge 1 ]; then systemctl-user daemon-reload || : systemctl-user try-restart %{name}.service || : fi %postun if [ "$1" -eq 0 ]; then systemctl-user stop %{name}.service || : systemctl-user daemon-reload || : fi contactsd-1.4.14/src/000077500000000000000000000000001440357512200143555ustar00rootroot00000000000000contactsd-1.4.14/src/BasePlugin000066400000000000000000000022001440357512200163230ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CONTACTSD_BASE_PLUGIN #define CONTACTSD_BASE_PLUGIN #include #endif contactsd-1.4.14/src/com.nokia.contacts.importprogress.xml000066400000000000000000000016771440357512200237030ustar00rootroot00000000000000 contactsd-1.4.14/src/contactsd.cpp000066400000000000000000000116251440357512200170500ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include "contactsd.h" #include "contactsdpluginloader.h" #include "synctrigger.h" #include "debug.h" #include #include #include using namespace Contactsd; /*! \class ContactsDaemon \brief Main daemon class which manages remote contact plug-ins. \section y Populating the contact list with remote contacts Remote contacts are the user's buddies from various online sources, like social networks, and IM accounts set up on the device. We have The daemon is responsible to retrieve remote contacts information from various sources and synchronizes them with tracker as well as providing real-time updates of this information. The remote contacts information can be accessed using the QtMobility contacts API. */ /*! \fn void ContactsDaemon::loadPlugins(const QStringList &plugins) Load plugins specified at \a plugins. \param loadPlugins The names of the plugins to load. */ /*! \fn QStringList ContactsDaemon::loadedPlugins() const Return a list containing the name of all loaded plugins. \return A list of plugin names. */ int ContactsDaemon::sigFd[2] = {0, 0}; ContactsDaemon::ContactsDaemon(QObject *parent) : QObject(parent), mDBusConnection(QDBusConnection::sessionBus()), mLoader(new ContactsdPluginLoader(&mDBusConnection)), mSyncTrigger(new SyncTrigger(&mDBusConnection)) { if (!mDBusConnection.isConnected()) { qCWarning(lcContactsd) << "Could not connect to DBus:" << mDBusConnection.lastError(); } else if (!mDBusConnection.registerService(QStringLiteral("com.nokia.contactsd"))) { qCWarning(lcContactsd) << "Could not register DBus service " "'com.nokia.contactsd':" << mDBusConnection.lastError(); } else if (!mLoader->registerNotificationService()) { qCWarning(lcContactsd) << "unable to register notification service"; } else if (!mSyncTrigger->registerTriggerService()) { qCWarning(lcContactsd) << "unable to register sync trigger service"; } // The UNIX signals call unixSignalHandler(), but that is not called // through the main loop. To process signals in the mainloop correctly, // we create a socket, and write a byte on one end when the UNIX signal // handler is invoked. This will trigger the QSocketNotifier::activated // signal (this time *through* the main loop) and call onUnixSignalReceived() // from where we can ask the QApplication to quit. if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigFd) != 0) { qCWarning(lcContactsd) << "Could not setup UNIX signal handler"; } mSignalNotifier = new QSocketNotifier(sigFd[1], QSocketNotifier::Read, this); connect(mSignalNotifier, SIGNAL(activated(int)), SLOT(onUnixSignalReceived())); } ContactsDaemon::~ContactsDaemon() { delete mLoader; delete mSyncTrigger; mDBusConnection.unregisterService(QLatin1String("com.nokia.contactsd")); } void ContactsDaemon::loadPlugins(const QStringList &plugins) { mLoader->loadPlugins(plugins); } QStringList ContactsDaemon::loadedPlugins() const { return mLoader->loadedPlugins(); } void ContactsDaemon::unixSignalHandler(int) { // Write a byte on the socket to activate the socket listener char a = 1; if (::write(sigFd[0], &a, sizeof(a)) != sizeof(a)) { qCWarning(lcContactsd) << "Unable to write to sigFd" << errno; } } void ContactsDaemon::onUnixSignalReceived() { // Mask signals mSignalNotifier->setEnabled(false); // Empty the socket buffer char dummy; if (::read(sigFd[1], &dummy, sizeof(dummy)) != sizeof(dummy)) { qCWarning(lcContactsd) << "Unable to complete read from sigFd" << errno; } qCDebug(lcContactsd) << "Received quit signal"; QCoreApplication::quit(); // Unmask signals mSignalNotifier->setEnabled(true); } contactsd-1.4.14/src/contactsd.h000066400000000000000000000035541440357512200165170ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CONTACTSD_H #define CONTACTSD_H #include #include #include #include class ContactsdPluginLoader; namespace Contactsd { class SyncTrigger; } class Q_DECL_EXPORT ContactsDaemon : public QObject { Q_OBJECT public: ContactsDaemon(QObject *parent = nullptr); virtual ~ContactsDaemon(); void loadPlugins(const QStringList &plugins = QStringList()); QStringList loadedPlugins() const; // UNIX signal handlers static void unixSignalHandler(int /* unused */); private Q_SLOTS: // Qt signal handler void onUnixSignalReceived(); private: QDBusConnection mDBusConnection; ContactsdPluginLoader *mLoader; static int sigFd[2]; QSocketNotifier *mSignalNotifier; Contactsd::SyncTrigger *mSyncTrigger; }; #endif // CONTACTSDAEMON_H contactsd-1.4.14/src/contactsdpluginloader.cpp000066400000000000000000000230221440357512200214500ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include "contactsdpluginloader.h" #include "contactsimportprogressadaptor.h" #include "debug.h" using namespace Contactsd; // import timeout is 5 minutes const int IMPORT_TIMEOUT = 5 * 60 * 1000; // alive check timeout is 30 seconds const int ALIVE_TIMEOUT = 30 * 1000; ContactsdPluginLoader::ContactsdPluginLoader(QDBusConnection *connection) : mImportTimer(0) , mCheckAliveTimer(0) , mDBusConnection(connection) , mHaveRegisteredDBus(false) { } ContactsdPluginLoader::~ContactsdPluginLoader() { if (mHaveRegisteredDBus) { mDBusConnection->unregisterObject(QLatin1String("/")); } qDeleteAll(mPluginStore.values()); mPluginStore.clear(); } void ContactsdPluginLoader::loadPlugins(const QStringList &plugins) { QStringList pluginsDirs; QString pluginsDirsEnv = QString::fromLocal8Bit(qgetenv("CONTACTSD_PLUGINS_DIRS")); if (pluginsDirsEnv.isEmpty()) { pluginsDirs << CONTACTSD_PLUGINS_DIR; } else { pluginsDirs << pluginsDirsEnv.split(':'); } Q_FOREACH (const QString &pluginsDir, pluginsDirs) { loadPlugins(pluginsDir, plugins); } } void ContactsdPluginLoader::loadPlugins(const QString &pluginsDir, const QStringList &plugins) { QDir dir(pluginsDir); dir.setFilter(QDir::Files | QDir::NoSymLinks); Q_FOREACH (const QString &fileName, dir.entryList()) { QString absFileName = dir.absoluteFilePath(fileName); qCDebug(lcContactsd) << "Trying to load plugin" << absFileName; // We intentionally leak this object, and never unload the plugin // When you load a plugin, you can't know what happens in the underlying // loaded libraries, so unloading a plugin while being sure it will not // have any unwanted side effect is just impossible. For ignored plugins, // we just avoid calling the init() function. QPluginLoader loader(absFileName); QObject *pluginObject = loader.instance(); if (!pluginObject) { qCWarning(lcContactsd) << "Error loading plugin" << absFileName << "- " << loader.errorString(); continue; } BasePlugin *basePlugin = qobject_cast(pluginObject); if (!basePlugin) { qCWarning(lcContactsd) << "Error loading plugin" << absFileName << "- not a Contactd::BasePlugin"; continue; } BasePlugin::MetaData metaData = basePlugin->metaData(); if (!metaData.contains(BasePlugin::metaDataKeyName)) { qCWarning(lcContactsd) << "Error loading plugin" << absFileName << "- invalid plugin metadata"; continue; } QString pluginName = metaData[BasePlugin::metaDataKeyName].toString(); if (!plugins.isEmpty() && !plugins.contains(pluginName)) { qCWarning(lcContactsd) << "Ignoring plugin" << absFileName; continue; } if (mPluginStore.contains(pluginName)) { qCWarning(lcContactsd) << "Ignoring plugin" << absFileName << "- plugin with name" << pluginName << "already registered"; continue; } qCDebug(lcContactsd) << "Plugin" << pluginName << "loaded"; mPluginStore.insert(pluginName, basePlugin); connect(basePlugin, SIGNAL(importStarted(const QString &, const QString &)), this, SLOT(onPluginImportStarted(const QString &, const QString &))); connect(basePlugin, SIGNAL(importEnded(const QString &, const QString &, int, int, int)), this, SLOT(onPluginImportEnded(const QString &, const QString &, int,int,int))); connect(basePlugin, SIGNAL(error(int, const QString &)), this, SIGNAL(error(int, const QString &))); connect(basePlugin, SIGNAL(importAlive()), this, SLOT(onImportAlive())); basePlugin->init(); } } QStringList ContactsdPluginLoader::loadedPlugins() const { return mPluginStore.keys(); } QStringList ContactsdPluginLoader::hasActiveImports() { return mImportState.activeImportingServices(); } void ContactsdPluginLoader::onPluginImportStarted(const QString &service, const QString &account) { BasePlugin *plugin = qobject_cast(sender()); if (not plugin) { qCWarning(lcContactsd) << Q_FUNC_INFO << "invalid Contactsd::BasePlugin object"; return; } QString name = pluginName(plugin); qCDebug(lcContactsd) << Q_FUNC_INFO << "by plugin" << name << "with service" << service << "account" << account; if (mImportState.hasActiveImports()) { // check if any account from the same service is importing now if (not mImportState.serviceHasActiveImports(service)) { // there was no active import from this service, so we update import state with new services Q_EMIT importStateChanged(QString(), service); } } else { // new import mImportState.reset(); startImportTimer(); Q_EMIT importStarted(service); } mImportState.addImportingAccount(service, account); } void ContactsdPluginLoader::onPluginImportEnded(const QString &service, const QString &account, int contactsAdded, int contactsRemoved, int contactsMerged) { BasePlugin *plugin = qobject_cast(sender()); if (not plugin) { qCWarning(lcContactsd) << Q_FUNC_INFO << "invalid Contactsd::BasePlugin object"; return; } QString name = pluginName(plugin); qCDebug(lcContactsd) << Q_FUNC_INFO << "by plugin" << name << "service" << service << "account" << account << "added" << contactsAdded << "removed" << contactsRemoved << "merged" << contactsMerged; bool removed = mImportState.removeImportingAccount(service, account, contactsAdded, contactsRemoved, contactsMerged); if (not removed) { qCDebug(lcContactsd) << Q_FUNC_INFO << "account does not exist"; return; } if (mImportState.hasActiveImports()) { if (not mImportState.serviceHasActiveImports(service)) { // This service has no acive importing accounts anymore Q_EMIT importStateChanged(service, QString()); } } else { stopImportTimer(); stopCheckAliveTimer(); Q_EMIT importEnded(mImportState.contactsAdded(), mImportState.contactsRemoved(), mImportState.contactsMerged()); } } void ContactsdPluginLoader::onImportAlive() { if (0 != mCheckAliveTimer && mCheckAliveTimer->isActive()) { startImportTimer(); } } void ContactsdPluginLoader::onImportTimeout() { qCDebug(lcContactsd) << Q_FUNC_INFO; stopImportTimer(); startCheckAliveTimer(); } void ContactsdPluginLoader::startCheckAliveTimer() { stopCheckAliveTimer(); mCheckAliveTimer = new QTimer(this); connect(mCheckAliveTimer, SIGNAL(timeout()), this, SLOT(onCheckAliveTimeout())); mCheckAliveTimer->start(ALIVE_TIMEOUT); } void ContactsdPluginLoader::stopCheckAliveTimer() { if (0 == mCheckAliveTimer) return; mCheckAliveTimer->stop(); delete mCheckAliveTimer; mCheckAliveTimer = 0; } void ContactsdPluginLoader::onCheckAliveTimeout() { stopCheckAliveTimer(); if (0 == mImportTimer) { Q_EMIT importEnded(mImportState.contactsAdded(), mImportState.contactsRemoved(), mImportState.contactsMerged()); mImportState.timeout(); } } void ContactsdPluginLoader::startImportTimer() { stopImportTimer(); // Add a timeout timer mImportTimer = new QTimer(this); connect(mImportTimer, SIGNAL(timeout()), this, SLOT(onImportTimeout())); mImportTimer->start(IMPORT_TIMEOUT); } void ContactsdPluginLoader::stopImportTimer() { delete mImportTimer; mImportTimer = 0; } QString ContactsdPluginLoader::pluginName(BasePlugin *plugin) { BasePlugin::MetaData metaData = plugin->metaData(); return metaData[BasePlugin::metaDataKeyName].toString(); } bool ContactsdPluginLoader::registerNotificationService() { if (mHaveRegisteredDBus) { return true; } if (!mDBusConnection->registerObject("/", this)) { qCWarning(lcContactsd) << "Could not register DBus object '/':" << mDBusConnection->lastError(); return false; } mHaveRegisteredDBus = true; new ContactsImportProgressAdaptor(this); return true; } contactsd-1.4.14/src/contactsdpluginloader.h000066400000000000000000000054741440357512200211300ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef CONTACTSPLUGINLOADER_H_ #define CONTACTSPLUGINLOADER_H_ #include #include #include "base-plugin.h" #include "importstate.h" class QTimer; class QPluginLoader; class QString; class QStringList; class QDBusConnection; class Q_DECL_EXPORT ContactsdPluginLoader : public QObject { Q_OBJECT public: ContactsdPluginLoader(QDBusConnection *connection); ~ContactsdPluginLoader(); void loadPlugins(const QStringList &plugins); void loadPlugins(const QString &pluginsDir, const QStringList &plugins); QStringList loadedPlugins() const; bool registerNotificationService(); public Q_SLOTS: QStringList hasActiveImports(); Q_SIGNALS: void importStarted(const QString &service); void importStateChanged(const QString &finishedService, const QString &newService); void importEnded(int contactsAdded, int contactsRemoved, int contactsMerged); void pluginsLoaded(); void error(int code, const QString &message); private Q_SLOTS: void onPluginImportStarted(const QString &service, const QString &account); void onPluginImportEnded(const QString &service, const QString &account, int contactsAdded, int contactsRemoved, int contactsMerged); void onImportTimeout(); void onImportAlive(); void onCheckAliveTimeout(); private: void startImportTimer(); void stopImportTimer(); void startCheckAliveTimer(); void stopCheckAliveTimer(); QString pluginName(Contactsd::BasePlugin *plugin); typedef QMap PluginStore; PluginStore mPluginStore; ImportState mImportState; QTimer *mImportTimer; QTimer *mCheckAliveTimer; QDBusConnection *mDBusConnection; bool mHaveRegisteredDBus; }; #endif contactsd-1.4.14/src/importstate.cpp000066400000000000000000000067101440357512200174400ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "debug.h" #include "importstate.h" static const QLatin1String SettingsOrganization("Nokia"); static const QLatin1String SettingsApplication("Contactsd"); /* * \enum AccountImportState * Each account's contacts importing state. * * \value Importing - account is actively importing contacts * \value Imported - account has finished importing contacts, * but UI has not yet handled these contacts */ enum AccountImportState { Importing = 1, Imported }; ImportState::ImportState() : mContactsAdded(0), mContactsMerged(0), mContactsRemoved(0), mStateStore(QSettings::IniFormat, QSettings::UserScope, SettingsOrganization, SettingsApplication) { } bool ImportState::hasActiveImports() { return (mService2Accounts.size() != 0); } void ImportState::timeout() { foreach (const QString &account, mService2Accounts.values()) { mStateStore.setValue(account, Imported); } mStateStore.sync(); reset(); } void ImportState::reset() { mService2Accounts.clear(); mContactsAdded = 0; mContactsMerged = 0; mContactsRemoved = 0; } QStringList ImportState::activeImportingServices() { return mService2Accounts.uniqueKeys(); } bool ImportState::serviceHasActiveImports(const QString &service) { return mService2Accounts.contains(service); } void ImportState::addImportingAccount(const QString &service, const QString &account) { qCDebug(lcContactsd) << Q_FUNC_INFO << service << account; if (not mService2Accounts.contains(service, account)) { mService2Accounts.insert(service, account); mStateStore.setValue(account, Importing); mStateStore.sync(); } } bool ImportState::removeImportingAccount(const QString &service, const QString &account, int added, int removed, int merged) { qCDebug(lcContactsd) << Q_FUNC_INFO << service << account; int numRemoved = mService2Accounts.remove(service, account); if (numRemoved) { mContactsAdded += added; mContactsRemoved += removed; mContactsMerged += merged; mStateStore.setValue(account, Imported); mStateStore.sync(); return true; } else { return false; } } int ImportState::contactsAdded() { return mContactsAdded; } int ImportState::contactsMerged() { return mContactsMerged; } int ImportState::contactsRemoved() { return mContactsRemoved; } contactsd-1.4.14/src/importstate.h000066400000000000000000000044351440357512200171070ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef IMPORTSTATE_H_ #define IMPORTSTATE_H_ #include #include #include #include class Q_DECL_EXPORT ImportState { public: ImportState(); bool hasActiveImports(); // make all account state as finished and reset the state void timeout(); // reset the state void reset(); QStringList activeImportingServices(); // check if a service still has active importing accounts bool serviceHasActiveImports(const QString &service); void addImportingAccount(const QString &service, const QString &account); // return true if the account is removed successfully, // fales if the account doesn't exist bool removeImportingAccount(const QString &service, const QString &account, int added, int removed, int merged); int contactsAdded(); int contactsMerged(); int contactsRemoved(); private: // each service may have multiple active importing accounts QMultiHash mService2Accounts; // accumlated amount of contacts being added, merged, removed int mContactsAdded; int mContactsMerged; int mContactsRemoved; // store each account's import state QSettings mStateStore; }; #endif // IMPORTSTATE_H_ contactsd-1.4.14/src/main.cpp000066400000000000000000000110001440357512200157750ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include #include #include "contactsd.h" #include "debug.h" using namespace Contactsd; static bool useDebug = false; static QLoggingCategory::CategoryFilter defaultCategoryFilter; static void usage() { QTextStream(stdout) << "Usage: contactsd [OPTION]...\n" << "\n" << "Options:\n" << "\n" << " --plugins PLUGINS Comma separated list of plugins to load\n" << " --enable-debug Enable debug logging\n" << " --version Output version information and exit\n" << " --help Display this help and exit\n" << "\n"; } static void setupUnixSignalHandlers() { struct sigaction sigterm, sigint; sigterm.sa_handler = ContactsDaemon::unixSignalHandler; sigemptyset(&sigterm.sa_mask); sigterm.sa_flags = SA_RESTART; if (sigaction(SIGTERM, &sigterm, 0) < 0) { qCWarning(lcContactsd) << "Could not setup signal handler for SIGTERM"; return; } sigint.sa_handler = ContactsDaemon::unixSignalHandler; sigemptyset(&sigint.sa_mask); sigint.sa_flags = SA_RESTART; if (sigaction(SIGINT, &sigint, 0) < 0) { qCWarning(lcContactsd) << "Could not setup signal handler for SIGINT"; return; } } static void categoryFilter(QLoggingCategory *category) { defaultCategoryFilter(category); if (qstrcmp(category->categoryName(), "contactsd") == 0 && useDebug) { category->setEnabled(QtDebugMsg, true); category->setEnabled(QtInfoMsg, true); } } Q_DECL_EXPORT int main(int argc, char **argv) { QCoreApplication app(argc, argv); QStringList plugins; useDebug = !qgetenv("CONTACTSD_DEBUG").isEmpty(); const QStringList args = app.arguments(); int i = 1; // ignore argv[0] while (i < args.count()) { QString arg = args.at(i); if (arg == "--plugins") { if (++i == args.count()) { usage(); return -1; } QString value = args.at(i); value.replace(" ", ","); plugins << value.split(",", QString::SkipEmptyParts); } else if (arg == "--version") { qDebug() << "contactsd version" << VERSION; return 0; } else if (arg == "--help") { usage(); return 0; } else if (arg == "--enable-debug") { useDebug = true; } else { qWarning() << "Invalid argument" << arg; usage(); return -1; } ++i; } defaultCategoryFilter = QLoggingCategory::installFilter(categoryFilter); setupUnixSignalHandlers(); qCDebug(lcContactsd) << "contactsd version" << VERSION << "started"; const QString translationPath(QString::fromLatin1(TRANSLATIONS_INSTALL_PATH)); QScopedPointer engineeringEnglish(new QTranslator); engineeringEnglish->load(QString::fromLatin1("contactsd_eng_en"), translationPath); app.installTranslator(engineeringEnglish.data()); QScopedPointer translator(new QTranslator); translator->load(QLocale(), QString::fromLatin1("contactsd"), QString::fromLatin1("-"), translationPath); app.installTranslator(translator.data()); ContactsDaemon daemon; daemon.loadPlugins(plugins); const int rc = app.exec(); return rc; } contactsd-1.4.14/src/src.pro000066400000000000000000000042411440357512200156670ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. TEMPLATE = app TARGET = contactsd VERSIONED_TARGET = $$TARGET-1.0 QT += dbus QT -= gui system(qdbusxml2cpp -c ContactsImportProgressAdaptor -a contactsimportprogressadaptor.h:contactsimportprogressadaptor.cpp com.nokia.contacts.importprogress.xml) include(../lib.pri) TRANSLATIONS_INSTALL_PATH = "/usr/share/translations" DEFINES += TRANSLATIONS_INSTALL_PATH=\"\\\"\"$${TRANSLATIONS_INSTALL_PATH}\"\\\"\" CONFIG += link_pkgconfig PKGCONFIG += buteosyncfw5 packagesExist(qt5-boostable) { DEFINES += HAS_BOOSTER PKGCONFIG += qt5-boostable } else { warning("qt5-boostable not available; startup times will be slower") } HEADERS += contactsd.h \ contactsdpluginloader.h \ importstate.h \ contactsimportprogressadaptor.h \ synctrigger.h \ base-plugin.h SOURCES += main.cpp \ contactsd.cpp \ contactsdpluginloader.cpp \ importstate.cpp \ contactsimportprogressadaptor.cpp \ synctrigger.cpp DEFINES += VERSION=\\\"$${VERSION}\\\" DEFINES += CONTACTSD_PLUGINS_DIR=\\\"$$LIBDIR/$${VERSIONED_TARGET}/plugins\\\" xml.files = com.nokia.contacts.importprogress.xml xml.path = $$INCLUDEDIR/$${VERSIONED_TARGET} target.path = $$BINDIR INSTALLS += target xml contactsd-1.4.14/src/synctrigger.cpp000066400000000000000000000120041440357512200174160ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2014 - 2019 Jolla Ltd ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "synctrigger.h" #include "debug.h" #include #include #include // buteo-syncfw #include #include #include #include namespace Contactsd { SyncTrigger::SyncTrigger(QDBusConnection *connection) : mDBusConnection(connection) , mHaveRegisteredDBus(false) { } SyncTrigger::~SyncTrigger() { if (mHaveRegisteredDBus) { mDBusConnection->unregisterObject(QLatin1String("/SyncTrigger")); } } bool SyncTrigger::registerTriggerService() { if (mHaveRegisteredDBus) { return true; } if (!mDBusConnection->registerObject(QLatin1String("/SyncTrigger"), this, QDBusConnection::ExportAllContents)) { qCWarning(lcContactsd) << "Could not register DBus object '/SyncTrigger':" << mDBusConnection->lastError(); return false; } mHaveRegisteredDBus = true; return true; } void SyncTrigger::triggerSync(const QStringList &accountProviders, int syncPolicy, int directionPolicy) { // TODO: in the future, handle AdaptivePolicy by coalescing triggered syncs into time segments. qCDebug(lcContactsd) << "triggering sync:" << accountProviders << syncPolicy << directionPolicy; // We trigger sync with a particular Buteo sync profile if: // - the profile is a per-account (not template) profile // - the profile is enabled // - the profile is for the service corresponding to the sync target // - the profile is a Contacts sync profile // - the profile has two-way directionality (or upsync), or the direction policy permits // - the profile has always-up-to-date set (sync on change), or the sync policy permits Buteo::ProfileManager profileManager; QList syncProfiles = profileManager.allSyncProfiles(); Q_FOREACH (Buteo::SyncProfile *profile, syncProfiles) { if (!profile) { continue; } QString profileId = profile->name(); bool notTemplate = !profile->key(Buteo::KEY_ACCOUNT_ID, QString()).isEmpty() && profile->key(Buteo::KEY_ACCOUNT_ID, QStringLiteral("0")) != QStringLiteral("0"); bool isEnabled = profile->isEnabled(); bool isUpsync = profile->syncDirection() == Buteo::SyncProfile::SYNC_DIRECTION_TWO_WAY || profile->syncDirection() == Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE; bool alwaysUpToDate = profile->key(Buteo::KEY_SYNC_ALWAYS_UP_TO_DATE, QStringLiteral("false")) == QStringLiteral("true"); // By convention, the template profile name should be of the form: // "accountProvider.dataType" -- eg, "google.Contacts" // And per-account profiles should be suffixed with "-accountId" bool isContacts = profileId.toLower().contains(QStringLiteral("contacts")); bool isTarget = accountProviders.isEmpty(); if (!isTarget) { Q_FOREACH (const QString &accountProvider, accountProviders) { if (profileId.toLower().startsWith(accountProvider.toLower())) { isTarget = true; break; } } } // in the future, we should inspect the sync profile for deltasync flag if (!profileId.toLower().startsWith(QStringLiteral("google")) && !profileId.toLower().startsWith(QStringLiteral("carddav"))) { isTarget = false; // we currently only support automatic sync with Google and CardDAV } delete profile; if (notTemplate && isEnabled && isTarget && isContacts && (isUpsync || directionPolicy == SyncTrigger::AnyDirection) && (alwaysUpToDate || syncPolicy == SyncTrigger::ForceSync)) { qCDebug(lcContactsd) << "SyncTrigger: profile meets criteria, triggering:" << profileId; QDBusMessage message = QDBusMessage::createMethodCall( QStringLiteral("com.meego.msyncd"), QStringLiteral("/synchronizer"), QStringLiteral("com.meego.msyncd"), QStringLiteral("startSync")); message.setArguments(QVariantList() << profileId); QDBusConnection::sessionBus().asyncCall(message); } } } } contactsd-1.4.14/src/synctrigger.h000066400000000000000000000031311440357512200170640ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2014 - 2019 Jolla Ltd ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef SYNCTRIGGER_H #define SYNCTRIGGER_H #include #include #include #include namespace Contactsd { class SyncTrigger : public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "com.nokia.contactsd") public: SyncTrigger(QDBusConnection *connection); ~SyncTrigger(); bool registerTriggerService(); enum SyncPolicy { ForceSync = 0, UpToDateSync, AdaptiveSync }; enum DirectionPolicy { AnyDirection = 0, UpsyncDirection }; public Q_SLOTS: Q_NOREPLY void triggerSync(const QStringList &accountProviders = QStringList(), int syncPolicy = ForceSync, int directionPolicy = AnyDirection); private: QDBusConnection *mDBusConnection; bool mHaveRegisteredDBus; }; } #endif contactsd-1.4.14/tests/000077500000000000000000000000001440357512200147305ustar00rootroot00000000000000contactsd-1.4.14/tests/common/000077500000000000000000000000001440357512200162205ustar00rootroot00000000000000contactsd-1.4.14/tests/common/test-common.h000066400000000000000000000031741440357512200206430ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef TEST_COMMON_H #define TEST_COMMON_H // replacement for QTEST_MAIN which always uses QCoreApplication #define CONTACTSD_TEST_MAIN(TestObject) \ int main(int argc, char *argv[]) \ { \ QCoreApplication app(argc, argv); \ TestObject tc; \ return QTest::qExec(&tc, argc, argv); \ } #endif contactsd-1.4.14/tests/common/test-common.pri000066400000000000000000000020571440357512200212050ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../../lib.pri) INCLUDEPATH += $$PWD PACKAGENAME = contactsd contactsd-1.4.14/tests/gcov-summary.sh000077500000000000000000000050061440357512200177210ustar00rootroot00000000000000#!/bin/sh # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. COVERAGE_TEST_DIRS=" ut_telepathyplugin " rm gcov.analysis.summary &> /dev/null function gcov_summary() { for dir in $COVERAGE_TEST_DIRS do find $dir -name "gcov.analysis" -exec cat {} \; | ./gcov.py >> gcov.analysis.summary done echo -e "\n" echo -e "Coverage summary for dirs: "$COVERAGE_TEST_DIRS ": \n" cat gcov.analysis.summary echo; } function lcov_generation() { if [ -z $1 ]; then LCOV_OUTPUT_DIR="reports/" else LCOV_OUTPUT_DIR=$1 fi for dir in $COVERAGE_TEST_DIRS do lcov --directory $dir --capture --output-file /tmp/people_tests-$dir.info -b $dir lcov -a /tmp/people_tests-$dir.info -o /tmp/people_tests-$dir.addinfo lcov -r /tmp/people_tests-$dir.addinfo "*targets*" -o /tmp/people_tests-$dir.addinfo lcov -r /tmp/people_tests-$dir.addinfo "tests/*" -o /tmp/people_tests-$dir.addinfo lcov -r /tmp/people_tests-$dir.addinfo "*usr/*" -o /tmp/people_tests-$dir.addinfo lcov -r /tmp/people_tests-$dir.addinfo "*moc*" -o /tmp/people_tests-$dir.addinfo lcov -r /tmp/people_tests-$dir.addinfo "contactsdplugininterface.h" -o /tmp/people_tests-$dir.addinfo lcov -r /tmp/people_tests-$dir.addinfo "contactsimportprogressadaptor.cpp" -o /tmp/people_tests-$dir.addinfo done; genhtml -o $LCOV_OUTPUT_DIR $(find /tmp -name '*addinfo'); rm /tmp/people_tests-*info echo -e "\n\nNow point your browser to "$LCOV_OUTPUT_DIR"\n\n" } if [ "$1" = "lcov" ]; then lcov_generation $2 else gcov_summary fi contactsd-1.4.14/tests/gcov.py000077500000000000000000000027171440357512200162520ustar00rootroot00000000000000#!/usr/bin/python3 # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. import sys mode = 0 lines = sys.stdin.readlines() for line in lines: if mode == 3: mode = 0 continue if mode == 2: mode = 3 continue if mode == 1: mode = 2 if last.find('.moc') > 0: continue if last[-6:-2] != ".cpp": continue print(last[:-1], ":", line[:-1]) continue if mode == 0: mode = 1 last = line continue contactsd-1.4.14/tests/libtelepathy/000077500000000000000000000000001440357512200174165ustar00rootroot00000000000000contactsd-1.4.14/tests/libtelepathy/contact-list-manager.c000066400000000000000000000550421440357512200236040ustar00rootroot00000000000000/* * Example channel manager for contact lists * * Copyright © 2007-2010 Collabora Ltd. * Copyright © 2007-2010 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "contact-list-manager.h" #include #include struct _TestContactListManagerPrivate { TpBaseConnection *conn; gulong status_changed_id; /* TpHandle => ContactDetails */ GHashTable *contact_details; TpHandleRepoIface *contact_repo; TpHandleRepoIface *group_repo; TpHandleSet *groups; }; static void contact_groups_iface_init (TpContactGroupListInterface *iface); static void mutable_contact_list_iface_init ( TpMutableContactListInterface *iface); static void mutable_contact_groups_iface_init ( TpMutableContactGroupListInterface *iface); G_DEFINE_TYPE_WITH_CODE (TestContactListManager, test_contact_list_manager, TP_TYPE_BASE_CONTACT_LIST, G_IMPLEMENT_INTERFACE (TP_TYPE_MUTABLE_CONTACT_LIST, mutable_contact_list_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_CONTACT_GROUP_LIST, contact_groups_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_MUTABLE_CONTACT_GROUP_LIST, mutable_contact_groups_iface_init)) typedef struct { TpSubscriptionState subscribe; TpSubscriptionState publish; gchar *publish_request; TpHandleSet *groups; TpHandle handle; TpHandleRepoIface *contact_repo; } ContactDetails; static void contact_detail_destroy (gpointer p) { ContactDetails *d = p; g_free (d->publish_request); tp_handle_set_destroy (d->groups); tp_handle_unref (d->contact_repo, d->handle); g_slice_free (ContactDetails, d); } static ContactDetails * lookup_contact (TestContactListManager *self, TpHandle handle) { return g_hash_table_lookup (self->priv->contact_details, GUINT_TO_POINTER (handle)); } static ContactDetails * ensure_contact (TestContactListManager *self, TpHandle handle) { ContactDetails *d = lookup_contact (self, handle); if (d == NULL) { d = g_slice_new0 (ContactDetails); d->subscribe = TP_SUBSCRIPTION_STATE_NO; d->publish = TP_SUBSCRIPTION_STATE_NO; d->publish_request = NULL; d->groups = tp_handle_set_new (self->priv->group_repo); d->handle = handle; d->contact_repo = self->priv->contact_repo; tp_handle_ref (d->contact_repo, d->handle); g_hash_table_insert (self->priv->contact_details, GUINT_TO_POINTER (handle), d); } return d; } static void test_contact_list_manager_init (TestContactListManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TEST_TYPE_CONTACT_LIST_MANAGER, TestContactListManagerPrivate); self->priv->contact_details = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, contact_detail_destroy); } static void close_all (TestContactListManager *self) { if (self->priv->status_changed_id != 0) { g_signal_handler_disconnect (self->priv->conn, self->priv->status_changed_id); self->priv->status_changed_id = 0; } tp_clear_pointer (&self->priv->contact_details, g_hash_table_unref); tp_clear_pointer (&self->priv->groups, tp_handle_set_destroy); } static void dispose (GObject *object) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (object); close_all (self); ((GObjectClass *) test_contact_list_manager_parent_class)->dispose ( object); } static TpHandleSet * contact_list_dup_contacts (TpBaseContactList *base) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); TpHandleSet *set; GHashTableIter iter; gpointer k, v; set = tp_handle_set_new (self->priv->contact_repo); g_hash_table_iter_init (&iter, self->priv->contact_details); while (g_hash_table_iter_next (&iter, &k, &v)) { ContactDetails *d = v; /* add all the interesting items */ if (d->subscribe != TP_SUBSCRIPTION_STATE_NO || d->publish != TP_SUBSCRIPTION_STATE_NO) tp_handle_set_add (set, GPOINTER_TO_UINT (k)); } return set; } static void contact_list_dup_states (TpBaseContactList *base, TpHandle contact, TpSubscriptionState *subscribe, TpSubscriptionState *publish, gchar **publish_request) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); ContactDetails *d = lookup_contact (self, contact); if (d == NULL) { if (subscribe != NULL) *subscribe = TP_SUBSCRIPTION_STATE_NO; if (publish != NULL) *publish = TP_SUBSCRIPTION_STATE_NO; if (publish_request != NULL) *publish_request = NULL; } else { if (subscribe != NULL) *subscribe = d->subscribe; if (publish != NULL) *publish = d->publish; if (publish_request != NULL) *publish_request = g_strdup (d->publish_request); } } static void contact_list_request_subscription_async (TpBaseContactList *base, TpHandleSet *contacts, const gchar *message, GAsyncReadyCallback callback, gpointer user_data) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); GArray *handles; handles = tp_handle_set_to_array (contacts); test_contact_list_manager_request_subscription (self, handles->len, (TpHandle *) handles->data, message); g_array_unref (handles); } static void contact_list_authorize_publication_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); GArray *handles; handles = tp_handle_set_to_array (contacts); test_contact_list_manager_authorize_publication (self, handles->len, (TpHandle *) handles->data); g_array_unref (handles); } static void contact_list_remove_contacts_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); GArray *handles; handles = tp_handle_set_to_array (contacts); test_contact_list_manager_remove (self, handles->len, (TpHandle *) handles->data); g_array_unref (handles); } static void contact_list_unsubscribe_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); GArray *handles; handles = tp_handle_set_to_array (contacts); test_contact_list_manager_unsubscribe (self, handles->len, (TpHandle *) handles->data); g_array_unref (handles); } static void contact_list_unpublish_async (TpBaseContactList *base, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); GArray *handles; handles = tp_handle_set_to_array (contacts); test_contact_list_manager_unpublish (self, handles->len, (TpHandle *) handles->data); g_array_unref (handles); } static GStrv contact_list_dup_groups (TpBaseContactList *base) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); GPtrArray *ret; if (self->priv->groups != NULL) { TpIntSetFastIter iter; TpHandle group; ret = g_ptr_array_sized_new (tp_handle_set_size (self->priv->groups) + 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (self->priv->groups)); while (tp_intset_fast_iter_next (&iter, &group)) { g_ptr_array_add (ret, g_strdup (tp_handle_inspect ( self->priv->group_repo, group))); } } else { ret = g_ptr_array_sized_new (1); } g_ptr_array_add (ret, NULL); return (GStrv) g_ptr_array_free (ret, FALSE); } static GStrv contact_list_dup_contact_groups (TpBaseContactList *base, TpHandle contact) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); ContactDetails *d = lookup_contact (self, contact); GPtrArray *ret; if (d != NULL && d->groups != NULL) { TpIntSetFastIter iter; TpHandle group; ret = g_ptr_array_sized_new (tp_handle_set_size (d->groups) + 1); tp_intset_fast_iter_init (&iter, tp_handle_set_peek (d->groups)); while (tp_intset_fast_iter_next (&iter, &group)) { g_ptr_array_add (ret, g_strdup (tp_handle_inspect ( self->priv->group_repo, group))); } } else { ret = g_ptr_array_sized_new (1); } g_ptr_array_add (ret, NULL); return (GStrv) g_ptr_array_free (ret, FALSE); } static TpHandleSet * contact_list_dup_group_members (TpBaseContactList *base, const gchar *group) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); TpHandleSet *set; TpHandle group_handle; GHashTableIter iter; gpointer k, v; set = tp_handle_set_new (self->priv->contact_repo); group_handle = tp_handle_lookup (self->priv->group_repo, group, NULL, NULL); if (G_UNLIKELY (group_handle == 0)) { /* clearly it doesn't have members */ return set; } g_hash_table_iter_init (&iter, self->priv->contact_details); while (g_hash_table_iter_next (&iter, &k, &v)) { ContactDetails *d = v; if (d->groups != NULL && tp_handle_set_is_member (d->groups, group_handle)) tp_handle_set_add (set, GPOINTER_TO_UINT (k)); } return set; } static void contact_list_set_contact_groups_async (TpBaseContactList *base, TpHandle contact, const gchar * const *names, gsize n, GAsyncReadyCallback callback, gpointer user_data) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (base); ContactDetails *d; TpIntset *set, *added_set, *removed_set; GPtrArray *added_names, *removed_names; TpIntSetFastIter iter; TpHandle group_handle; guint i; d = ensure_contact (self, contact); set = tp_intset_new (); for (i = 0; i < n; i++) { group_handle = tp_handle_ensure (self->priv->group_repo, names[i], NULL, NULL); tp_intset_add (set, group_handle); } added_set = tp_intset_difference (set, tp_handle_set_peek (d->groups)); added_names = g_ptr_array_sized_new (tp_intset_size (added_set)); tp_intset_fast_iter_init (&iter, added_set); while (tp_intset_fast_iter_next (&iter, &group_handle)) { g_ptr_array_add (added_names, (gchar *) tp_handle_inspect ( self->priv->group_repo, group_handle)); } tp_intset_destroy (added_set); removed_set = tp_intset_difference (tp_handle_set_peek (d->groups), set); removed_names = g_ptr_array_sized_new (tp_intset_size (removed_set)); tp_intset_fast_iter_init (&iter, removed_set); while (tp_intset_fast_iter_next (&iter, &group_handle)) { g_ptr_array_add (removed_names, (gchar *) tp_handle_inspect ( self->priv->group_repo, group_handle)); } tp_intset_destroy (removed_set); tp_handle_set_destroy (d->groups); d->groups = tp_handle_set_new_from_intset (self->priv->group_repo, set); tp_intset_destroy (set); tp_base_contact_list_one_contact_groups_changed (base, contact, (const gchar * const *) added_names->pdata, added_names->len, (const gchar * const *) removed_names->pdata, removed_names->len); tp_simple_async_report_success_in_idle ((GObject *) self, callback, user_data, contact_list_set_contact_groups_async); g_ptr_array_unref (added_names); g_ptr_array_unref (removed_names); } static void contact_list_set_group_members_async (TpBaseContactList *base, const gchar *normalized_group, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new_error ((GObject *) base, callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Not implemented"); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } static void contact_list_add_to_group_async (TpBaseContactList *base, const gchar *group, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new_error ((GObject *) base, callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Not implemented"); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } static void contact_list_remove_from_group_async (TpBaseContactList *base, const gchar *group, TpHandleSet *contacts, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new_error ((GObject *) base, callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Not implemented"); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } static void contact_list_remove_group_async (TpBaseContactList *base, const gchar *group, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *simple; simple = g_simple_async_result_new_error ((GObject *) base, callback, user_data, TP_ERROR, TP_ERROR_NOT_IMPLEMENTED, "Not implemented"); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } static void status_changed_cb (TpBaseConnection *conn, guint status, guint reason, TestContactListManager *self) { switch (status) { case TP_CONNECTION_STATUS_CONNECTED: { tp_base_contact_list_set_list_received (TP_BASE_CONTACT_LIST (self)); } break; case TP_CONNECTION_STATUS_DISCONNECTED: { close_all (self); } break; } } static void constructed (GObject *object) { TestContactListManager *self = TEST_CONTACT_LIST_MANAGER (object); void (*chain_up) (GObject *) = ((GObjectClass *) test_contact_list_manager_parent_class)->constructed; if (chain_up != NULL) { chain_up (object); } self->priv->conn = tp_base_contact_list_get_connection ( TP_BASE_CONTACT_LIST (self), NULL); self->priv->status_changed_id = g_signal_connect (self->priv->conn, "status-changed", G_CALLBACK (status_changed_cb), self); self->priv->contact_repo = tp_base_connection_get_handles (self->priv->conn, TP_HANDLE_TYPE_CONTACT); self->priv->group_repo = tp_base_connection_get_handles (self->priv->conn, TP_HANDLE_TYPE_GROUP); self->priv->groups = tp_handle_set_new (self->priv->group_repo); } static void mutable_contact_list_iface_init (TpMutableContactListInterface *iface) { iface->request_subscription_async = contact_list_request_subscription_async; iface->authorize_publication_async = contact_list_authorize_publication_async; iface->remove_contacts_async = contact_list_remove_contacts_async; iface->unsubscribe_async = contact_list_unsubscribe_async; iface->unpublish_async = contact_list_unpublish_async; } static void contact_groups_iface_init (TpContactGroupListInterface *iface) { iface->dup_groups = contact_list_dup_groups; iface->dup_contact_groups = contact_list_dup_contact_groups; iface->dup_group_members = contact_list_dup_group_members; } static void mutable_contact_groups_iface_init ( TpMutableContactGroupListInterface *iface) { iface->set_contact_groups_async = contact_list_set_contact_groups_async; iface->set_group_members_async = contact_list_set_group_members_async; iface->add_to_group_async = contact_list_add_to_group_async; iface->remove_from_group_async = contact_list_remove_from_group_async; iface->remove_group_async = contact_list_remove_group_async; } static void test_contact_list_manager_class_init (TestContactListManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; TpBaseContactListClass *base_class =(TpBaseContactListClass *) klass; g_type_class_add_private (klass, sizeof (TestContactListManagerPrivate)); object_class->constructed = constructed; object_class->dispose = dispose; base_class->dup_states = contact_list_dup_states; base_class->dup_contacts = contact_list_dup_contacts; } void test_contact_list_manager_add_to_group (TestContactListManager *self, const gchar *group_name, TpHandle member) { TpBaseContactList *base = TP_BASE_CONTACT_LIST (self); ContactDetails *d = ensure_contact (self, member); TpHandle group_handle; group_handle = tp_handle_ensure (self->priv->group_repo, group_name, NULL, NULL); tp_handle_set_add (d->groups, group_handle); tp_base_contact_list_one_contact_groups_changed (base, member, &group_name, 1, NULL, 0); } void test_contact_list_manager_remove_from_group (TestContactListManager *self, const gchar *group_name, TpHandle member) { TpBaseContactList *base = TP_BASE_CONTACT_LIST (self); ContactDetails *d = lookup_contact (self, member); TpHandle group_handle; if (d == NULL) return; group_handle = tp_handle_ensure (self->priv->group_repo, group_name, NULL, NULL); tp_handle_set_remove (d->groups, group_handle); tp_base_contact_list_one_contact_groups_changed (base, member, NULL, 0, &group_name, 1); } typedef struct { TestContactListManager *self; TpHandleSet *handles; } SelfAndContact; static SelfAndContact * self_and_contact_new (TestContactListManager *self, TpHandleSet *handles) { SelfAndContact *ret = g_slice_new0 (SelfAndContact); ret->self = g_object_ref (self); ret->handles = tp_handle_set_copy (handles); return ret; } static void self_and_contact_destroy (gpointer p) { SelfAndContact *s = p; tp_handle_set_destroy (s->handles); g_object_unref (s->self); g_slice_free (SelfAndContact, s); } static gboolean receive_authorized (gpointer p) { SelfAndContact *s = p; GArray *handles_array; guint i; handles_array = tp_handle_set_to_array (s->handles); for (i = 0; i < handles_array->len; i++) { ContactDetails *d = lookup_contact (s->self, g_array_index (handles_array, TpHandle, i)); if (d == NULL) continue; d->subscribe = TP_SUBSCRIPTION_STATE_YES; /* if we're not publishing to them, also pretend they have asked us to do so */ if (d->publish != TP_SUBSCRIPTION_STATE_YES) { d->publish = TP_SUBSCRIPTION_STATE_ASK; tp_clear_pointer (&d->publish_request, g_free); d->publish_request = g_strdup ("automatic publish request"); } } g_array_unref (handles_array); tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (s->self), s->handles, NULL); return FALSE; } static gboolean receive_unauthorized (gpointer p) { SelfAndContact *s = p; GArray *handles_array; guint i; handles_array = tp_handle_set_to_array (s->handles); for (i = 0; i < handles_array->len; i++) { ContactDetails *d = lookup_contact (s->self, g_array_index (handles_array, TpHandle, i)); if (d == NULL) continue; d->subscribe = TP_SUBSCRIPTION_STATE_REMOVED_REMOTELY; } g_array_unref (handles_array); tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (s->self), s->handles, NULL); return FALSE; } void test_contact_list_manager_request_subscription (TestContactListManager *self, guint n_members, TpHandle *members, const gchar *message) { TpHandleSet *handles; guint i; gchar *message_lc; handles = tp_handle_set_new (self->priv->contact_repo); for (i = 0; i < n_members; i++) { ContactDetails *d = ensure_contact (self, members[i]); if (d->subscribe == TP_SUBSCRIPTION_STATE_YES) continue; d->subscribe = TP_SUBSCRIPTION_STATE_ASK; tp_handle_set_add (handles, members[i]); } tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (self), handles, NULL); message_lc = g_ascii_strdown (message, -1); if (strstr (message_lc, "please") != NULL) { g_idle_add_full (G_PRIORITY_DEFAULT, receive_authorized, self_and_contact_new (self, handles), self_and_contact_destroy); } else if (strstr (message_lc, "no") != NULL) { g_idle_add_full (G_PRIORITY_DEFAULT, receive_unauthorized, self_and_contact_new (self, handles), self_and_contact_destroy); } g_free (message_lc); tp_handle_set_destroy (handles); } void test_contact_list_manager_unsubscribe (TestContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; guint i; handles = tp_handle_set_new (self->priv->contact_repo); for (i = 0; i < n_members; i++) { ContactDetails *d = lookup_contact (self, members[i]); if (d == NULL || d->subscribe == TP_SUBSCRIPTION_STATE_NO) continue; d->subscribe = TP_SUBSCRIPTION_STATE_NO; tp_handle_set_add (handles, members[i]); } tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (self), handles, NULL); tp_handle_set_destroy (handles); } void test_contact_list_manager_authorize_publication (TestContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; guint i; handles = tp_handle_set_new (self->priv->contact_repo); for (i = 0; i < n_members; i++) { ContactDetails *d = lookup_contact (self, members[i]); if (d == NULL || d->publish != TP_SUBSCRIPTION_STATE_ASK) continue; d->publish = TP_SUBSCRIPTION_STATE_YES; tp_clear_pointer (&d->publish_request, g_free); tp_handle_set_add (handles, members[i]); } tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (self), handles, NULL); tp_handle_set_destroy (handles); } void test_contact_list_manager_unpublish (TestContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; guint i; handles = tp_handle_set_new (self->priv->contact_repo); for (i = 0; i < n_members; i++) { ContactDetails *d = lookup_contact (self, members[i]); if (d == NULL || d->publish == TP_SUBSCRIPTION_STATE_NO) continue; d->publish = TP_SUBSCRIPTION_STATE_NO; tp_clear_pointer (&d->publish_request, g_free); tp_handle_set_add (handles, members[i]); } tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (self), handles, NULL); tp_handle_set_destroy (handles); } void test_contact_list_manager_remove (TestContactListManager *self, guint n_members, TpHandle *members) { TpHandleSet *handles; guint i; handles = tp_handle_set_new (self->priv->contact_repo); for (i = 0; i < n_members; i++) { ContactDetails *d = lookup_contact (self, members[i]); if (d == NULL) continue; g_hash_table_remove (self->priv->contact_details, GUINT_TO_POINTER (members[i])); tp_handle_set_add (handles, members[i]); } tp_base_contact_list_contacts_changed (TP_BASE_CONTACT_LIST (self), NULL, handles); tp_handle_set_destroy (handles); } contactsd-1.4.14/tests/libtelepathy/contact-list-manager.h000066400000000000000000000051711440357512200236070ustar00rootroot00000000000000/* * Example channel manager for contact lists * * Copyright © 2007-2010 Collabora Ltd. * Copyright © 2007-2010 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TEST_CONTACT_LIST_MANAGER_H__ #define __TEST_CONTACT_LIST_MANAGER_H__ #include G_BEGIN_DECLS #define TEST_TYPE_CONTACT_LIST_MANAGER \ (test_contact_list_manager_get_type ()) #define TEST_CONTACT_LIST_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TEST_TYPE_CONTACT_LIST_MANAGER, \ TestContactListManager)) #define TEST_CONTACT_LIST_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_CONTACT_LIST_MANAGER, \ TestContactListManagerClass)) #define TEST_IS_CONTACT_LIST_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_CONTACT_LIST_MANAGER)) #define TEST_IS_CONTACT_LIST_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_CONTACT_LIST_MANAGER)) #define TEST_CONTACT_LIST_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_CONTACT_LIST_MANAGER, \ TestContactListManagerClass)) typedef struct _TestContactListManager TestContactListManager; typedef struct _TestContactListManagerClass TestContactListManagerClass; typedef struct _TestContactListManagerPrivate TestContactListManagerPrivate; struct _TestContactListManagerClass { TpBaseContactListClass parent_class; }; struct _TestContactListManager { TpBaseContactList parent; TestContactListManagerPrivate *priv; }; GType test_contact_list_manager_get_type (void); void test_contact_list_manager_add_to_group (TestContactListManager *self, const gchar *group_name, TpHandle member); void test_contact_list_manager_remove_from_group (TestContactListManager *self, const gchar *group_name, TpHandle member); void test_contact_list_manager_request_subscription (TestContactListManager *self, guint n_members, TpHandle *members, const gchar *message); void test_contact_list_manager_unsubscribe (TestContactListManager *self, guint n_members, TpHandle *members); void test_contact_list_manager_authorize_publication (TestContactListManager *self, guint n_members, TpHandle *members); void test_contact_list_manager_unpublish (TestContactListManager *self, guint n_members, TpHandle *members); void test_contact_list_manager_remove (TestContactListManager *self, guint n_members, TpHandle *members); G_END_DECLS #endif contactsd-1.4.14/tests/libtelepathy/contacts-conn.c000066400000000000000000001204631440357512200223410ustar00rootroot00000000000000/* * contacts-conn.c - connection with contact info * * Copyright (C) 2007-2008 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "contacts-conn.h" #include #include #include #include #include #include #include #include "debug.h" static void init_aliasing (gpointer, gpointer); static void init_avatars (gpointer, gpointer); static void init_location (gpointer, gpointer); static void init_contact_caps (gpointer, gpointer); static void init_contact_info (gpointer, gpointer); static void conn_avatars_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data); G_DEFINE_TYPE_WITH_CODE (TpTestsContactsConnection, tp_tests_contacts_connection, TP_TESTS_TYPE_SIMPLE_CONNECTION, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, init_aliasing); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS, init_avatars); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE, tp_presence_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE, tp_presence_mixin_simple_presence_iface_init) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_LOCATION, init_location) G_IMPLEMENT_INTERFACE ( TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, init_contact_caps) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO, init_contact_info) G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, tp_contacts_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_LIST, tp_base_contact_list_mixin_list_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_GROUPS, tp_base_contact_list_mixin_groups_iface_init); ); /* type definition stuff */ static const char *mime_types[] = { "image/png", NULL }; static TpDBusPropertiesMixinPropImpl conn_avatars_properties[] = { { "MinimumAvatarWidth", GUINT_TO_POINTER (1), NULL }, { "MinimumAvatarHeight", GUINT_TO_POINTER (2), NULL }, { "RecommendedAvatarWidth", GUINT_TO_POINTER (3), NULL }, { "RecommendedAvatarHeight", GUINT_TO_POINTER (4), NULL }, { "MaximumAvatarWidth", GUINT_TO_POINTER (5), NULL }, { "MaximumAvatarHeight", GUINT_TO_POINTER (6), NULL }, { "MaximumAvatarBytes", GUINT_TO_POINTER (7), NULL }, /* special-cased - it's the only one with a non-guint value */ { "SupportedAvatarMIMETypes", NULL, NULL }, { NULL } }; enum { N_SIGNALS }; struct _TpTestsContactsConnectionPrivate { /* TpHandle => gchar * */ GHashTable *aliases; /* TpHandle => AvatarData */ GHashTable *avatars; /* TpHandle => ContactsConnectionPresenceStatusIndex */ GHashTable *presence_statuses; /* TpHandle => gchar * */ GHashTable *presence_messages; /* TpHandle => GHashTable * */ GHashTable *locations; /* TpHandle => GPtrArray * */ GHashTable *capabilities; /* TpHandle => GPtrArray * */ GHashTable *contact_info; GPtrArray *default_contact_info; TestContactListManager *list_manager; }; typedef struct { GArray *data; gchar *mime_type; gchar *token; } AvatarData; static AvatarData * avatar_data_new (GArray *data, const gchar *mime_type, const gchar *token) { AvatarData *a; a = g_slice_new (AvatarData); a->data = data ? g_array_ref (data) : NULL; a->mime_type = g_strdup (mime_type); a->token = g_strdup (token); return a; } static void avatar_data_free (gpointer data) { AvatarData *a = data; if (a != NULL) { if (a->data != NULL) g_array_unref (a->data); g_free (a->mime_type); g_free (a->token); g_slice_free (AvatarData, a); } } static void free_rcc_list (GPtrArray *rccs) { g_boxed_free (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, rccs); } static void tp_tests_contacts_connection_init (TpTestsContactsConnection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_CONTACTS_CONNECTION, TpTestsContactsConnectionPrivate); self->priv->aliases = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); self->priv->avatars = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, avatar_data_free); self->priv->presence_statuses = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); self->priv->presence_messages = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); self->priv->locations = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_unref); self->priv->capabilities = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_rcc_list); self->priv->contact_info = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_ptr_array_unref); } static void finalize (GObject *object) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); tp_contacts_mixin_finalize (object); g_hash_table_destroy (self->priv->aliases); g_hash_table_destroy (self->priv->avatars); g_hash_table_destroy (self->priv->presence_statuses); g_hash_table_destroy (self->priv->presence_messages); g_hash_table_destroy (self->priv->locations); g_hash_table_destroy (self->priv->capabilities); g_hash_table_destroy (self->priv->contact_info); if (self->priv->default_contact_info != NULL) g_ptr_array_unref (self->priv->default_contact_info); G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->finalize (object); } static void aliasing_fill_contact_attributes (GObject *object, const GArray *contacts, GHashTable *attributes) { guint i; TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); TpBaseConnection *base = TP_BASE_CONNECTION (object); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); const gchar *alias = g_hash_table_lookup (self->priv->aliases, GUINT_TO_POINTER (handle)); if (alias == NULL) { alias = tp_handle_inspect (contact_repo, handle); } tp_contacts_mixin_set_contact_attribute (attributes, handle, TP_IFACE_CONNECTION_INTERFACE_ALIASING "/alias", tp_g_value_slice_new_string (alias)); } } static void avatars_fill_contact_attributes (GObject *object, const GArray *contacts, GHashTable *attributes) { guint i; TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); AvatarData *a = g_hash_table_lookup (self->priv->avatars, GUINT_TO_POINTER (handle)); if (a != NULL && a->token != NULL) { tp_contacts_mixin_set_contact_attribute (attributes, handle, TP_IFACE_CONNECTION_INTERFACE_AVATARS "/token", tp_g_value_slice_new_string (a->token)); } } } static void location_fill_contact_attributes (GObject *object, const GArray *contacts, GHashTable *attributes) { guint i; TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); GHashTable *location = g_hash_table_lookup (self->priv->locations, GUINT_TO_POINTER (handle)); if (location != NULL) { tp_contacts_mixin_set_contact_attribute (attributes, handle, TP_IFACE_CONNECTION_INTERFACE_LOCATION "/location", tp_g_value_slice_new_boxed (TP_HASH_TYPE_LOCATION, location)); } } } static void contact_caps_fill_contact_attributes (GObject *object, const GArray *contacts, GHashTable *attributes) { guint i; TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); GPtrArray *caps = g_hash_table_lookup (self->priv->capabilities, GUINT_TO_POINTER (handle)); if (caps != NULL) { tp_contacts_mixin_set_contact_attribute (attributes, handle, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES "/capabilities", tp_g_value_slice_new_boxed ( TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, caps)); } } } static void contact_info_fill_contact_attributes (GObject *object, const GArray *contacts, GHashTable *attributes) { guint i; TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); GPtrArray *info = g_hash_table_lookup (self->priv->contact_info, GUINT_TO_POINTER (handle)); if (info != NULL) { tp_contacts_mixin_set_contact_attribute (attributes, handle, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO "/info", tp_g_value_slice_new_boxed (TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, info)); } } } static TpDBusPropertiesMixinPropImpl conn_contact_info_properties[] = { { "ContactInfoFlags", GUINT_TO_POINTER (TP_CONTACT_INFO_FLAG_PUSH | TP_CONTACT_INFO_FLAG_CAN_SET), NULL }, { "SupportedFields", NULL, NULL }, { NULL } }; static void conn_contact_info_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GQuark q_supported_fields = g_quark_from_static_string ("SupportedFields"); static GPtrArray *supported_fields = NULL; if (name == q_supported_fields) { if (supported_fields == NULL) { supported_fields = g_ptr_array_new (); g_ptr_array_add (supported_fields, tp_value_array_build (4, G_TYPE_STRING, "n", G_TYPE_STRV, NULL, G_TYPE_UINT, 0, G_TYPE_UINT, 0, G_TYPE_INVALID)); } g_value_set_boxed (value, supported_fields); } else { g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); } } static void constructed (GObject *object) { TpBaseConnection *base = TP_BASE_CONNECTION (object); void (*parent_impl) (GObject *) = G_OBJECT_CLASS (tp_tests_contacts_connection_parent_class)->constructed; if (parent_impl != NULL) parent_impl (object); tp_contacts_mixin_init (object, G_STRUCT_OFFSET (TpTestsContactsConnection, contacts_mixin)); tp_base_connection_register_with_contacts_mixin (base); tp_base_contact_list_mixin_register_with_contacts_mixin (base); tp_contacts_mixin_add_contact_attributes_iface (object, TP_IFACE_CONNECTION_INTERFACE_ALIASING, aliasing_fill_contact_attributes); tp_contacts_mixin_add_contact_attributes_iface (object, TP_IFACE_CONNECTION_INTERFACE_AVATARS, avatars_fill_contact_attributes); tp_contacts_mixin_add_contact_attributes_iface (object, TP_IFACE_CONNECTION_INTERFACE_LOCATION, location_fill_contact_attributes); tp_contacts_mixin_add_contact_attributes_iface (object, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, contact_caps_fill_contact_attributes); tp_contacts_mixin_add_contact_attributes_iface (object, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, contact_info_fill_contact_attributes); tp_presence_mixin_init (object, G_STRUCT_OFFSET (TpTestsContactsConnection, presence_mixin)); tp_presence_mixin_simple_presence_register_with_contacts_mixin (object); } static const TpPresenceStatusOptionalArgumentSpec can_have_message[] = { { "message", "s", NULL, NULL }, { NULL } }; /* Must match TpTestsContactsConnectionPresenceStatusIndex in the .h */ static const TpPresenceStatusSpec my_statuses[] = { { "unset", TP_CONNECTION_PRESENCE_TYPE_UNSET, FALSE, NULL }, { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, can_have_message }, { "busy", TP_CONNECTION_PRESENCE_TYPE_BUSY, TRUE, can_have_message }, { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, can_have_message }, { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, NULL }, { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, NULL }, { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, NULL }, { NULL } }; static gboolean my_status_available (GObject *object, guint index) { TpBaseConnection *base = TP_BASE_CONNECTION (object); if (base->status != TP_CONNECTION_STATUS_CONNECTED) return FALSE; return TRUE; } static GHashTable * my_get_contact_statuses (GObject *object, const GArray *contacts, GError **error) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (object); GHashTable *result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_presence_status_free); guint i; for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); gpointer key = GUINT_TO_POINTER (handle); TpTestsContactsConnectionPresenceStatusIndex index; const gchar *presence_message; GHashTable *parameters; index = GPOINTER_TO_UINT (g_hash_table_lookup ( self->priv->presence_statuses, key)); presence_message = g_hash_table_lookup ( self->priv->presence_messages, key); parameters = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); if (presence_message != NULL) g_hash_table_insert (parameters, "message", tp_g_value_slice_new_string (presence_message)); g_hash_table_insert (result, key, tp_presence_status_new (index, parameters)); g_hash_table_destroy (parameters); } return result; } static gboolean my_set_own_status (GObject *object, const TpPresenceStatus *status, GError **error) { TpBaseConnection *base_conn = TP_BASE_CONNECTION (object); TpTestsContactsConnectionPresenceStatusIndex index = status->index; const gchar *message = ""; if (status->optional_arguments != NULL) { message = g_hash_table_lookup (status->optional_arguments, "message"); if (message == NULL) message = ""; } tp_tests_contacts_connection_change_presences (TP_TESTS_CONTACTS_CONNECTION (object), 1, &(base_conn->self_handle), &index, &message); return TRUE; } static GPtrArray * create_channel_managers (TpBaseConnection *conn) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (conn); GPtrArray *ret = g_ptr_array_sized_new (1); self->priv->list_manager = g_object_new (TEST_TYPE_CONTACT_LIST_MANAGER, "connection", conn, NULL); g_ptr_array_add (ret, self->priv->list_manager); return ret; } static void tp_tests_contacts_connection_class_init (TpTestsContactsConnectionClass *klass) { TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; static const gchar *interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_ALIASING, TP_IFACE_CONNECTION_INTERFACE_AVATARS, TP_IFACE_CONNECTION_INTERFACE_CONTACTS, TP_IFACE_CONNECTION_INTERFACE_CONTACT_LIST, TP_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS, TP_IFACE_CONNECTION_INTERFACE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_LOCATION, TP_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES, TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CONNECTION_INTERFACE_AVATARS, conn_avatars_properties_getter, NULL, conn_avatars_properties, }, { TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, conn_contact_info_properties_getter, NULL, conn_contact_info_properties, }, { NULL } }; object_class->constructed = constructed; object_class->finalize = finalize; g_type_class_add_private (klass, sizeof (TpTestsContactsConnectionPrivate)); base_class->interfaces_always_present = interfaces_always_present; base_class->create_channel_managers = create_channel_managers; tp_contacts_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsContactsConnectionClass, contacts_mixin)); tp_presence_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsContactsConnectionClass, presence_mixin), my_status_available, my_get_contact_statuses, my_set_own_status, my_statuses); tp_presence_mixin_simple_presence_init_dbus_properties (object_class); klass->properties_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsContactsConnectionClass, properties_class)); tp_base_contact_list_mixin_class_init (base_class); } TestContactListManager * tp_tests_contacts_connection_get_contact_list_manager ( TpTestsContactsConnection *self) { return self->priv->list_manager; } void tp_tests_contacts_connection_change_aliases (TpTestsContactsConnection *self, guint n, const TpHandle *handles, const gchar * const *aliases) { GPtrArray *structs = g_ptr_array_sized_new (n); guint i; for (i = 0; i < n; i++) { GValueArray *pair = g_value_array_new (2); DEBUG ("contact#%u -> %s", handles[i], aliases[i]); g_hash_table_insert (self->priv->aliases, GUINT_TO_POINTER (handles[i]), g_strdup (aliases[i])); g_value_array_append (pair, NULL); g_value_init (pair->values + 0, G_TYPE_UINT); g_value_set_uint (pair->values + 0, handles[i]); g_value_array_append (pair, NULL); g_value_init (pair->values + 1, G_TYPE_STRING); g_value_set_string (pair->values + 1, aliases[i]); g_ptr_array_add (structs, pair); } tp_svc_connection_interface_aliasing_emit_aliases_changed (self, structs); g_ptr_array_foreach (structs, (GFunc) g_value_array_free, NULL); g_ptr_array_free (structs, TRUE); } void tp_tests_contacts_connection_change_presences ( TpTestsContactsConnection *self, guint n, const TpHandle *handles, const TpTestsContactsConnectionPresenceStatusIndex *indexes, const gchar * const *messages) { GHashTable *presences = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) tp_presence_status_free); guint i; for (i = 0; i < n; i++) { GHashTable *parameters; gpointer key = GUINT_TO_POINTER (handles[i]); DEBUG ("contact#%u -> %s \"%s\"", handles[i], my_statuses[indexes[i]].name, messages[i]); g_hash_table_insert (self->priv->presence_statuses, key, GUINT_TO_POINTER (indexes[i])); g_hash_table_insert (self->priv->presence_messages, key, g_strdup (messages[i])); parameters = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) tp_g_value_slice_free); if (messages[i] != NULL && messages[i][0] != '\0') g_hash_table_insert (parameters, "message", tp_g_value_slice_new_string (messages[i])); g_hash_table_insert (presences, key, tp_presence_status_new (indexes[i], parameters)); g_hash_table_destroy (parameters); } tp_presence_mixin_emit_presence_update ((GObject *) self, presences); g_hash_table_destroy (presences); } void tp_tests_contacts_connection_change_avatar_tokens (TpTestsContactsConnection *self, guint n, const TpHandle *handles, const gchar * const *tokens) { guint i; for (i = 0; i < n; i++) { DEBUG ("contact#%u -> %s", handles[i], tokens[i]); g_hash_table_insert (self->priv->avatars, GUINT_TO_POINTER (handles[i]), avatar_data_new (NULL, NULL, tokens[i])); tp_svc_connection_interface_avatars_emit_avatar_updated (self, handles[i], tokens[i]); } } void tp_tests_contacts_connection_change_avatar_data ( TpTestsContactsConnection *self, TpHandle handle, GArray *data, const gchar *mime_type, const gchar *token) { g_hash_table_insert (self->priv->avatars, GUINT_TO_POINTER (handle), avatar_data_new (data, mime_type, token)); tp_svc_connection_interface_avatars_emit_avatar_updated (self, handle, token); } void tp_tests_contacts_connection_change_locations (TpTestsContactsConnection *self, guint n, const TpHandle *handles, GHashTable **locations) { guint i; for (i = 0; i < n; i++) { DEBUG ("contact#%u ->", handles[i]); tp_asv_dump (locations[i]); g_hash_table_insert (self->priv->locations, GUINT_TO_POINTER (handles[i]), g_hash_table_ref (locations[i])); tp_svc_connection_interface_location_emit_location_updated (self, handles[i], locations[i]); } } void tp_tests_contacts_connection_change_capabilities ( TpTestsContactsConnection *self, GHashTable *capabilities) { GHashTableIter iter; gpointer handle, caps; g_hash_table_iter_init (&iter, capabilities); while (g_hash_table_iter_next (&iter, &handle, &caps)) { g_hash_table_insert (self->priv->capabilities, handle, g_boxed_copy (TP_ARRAY_TYPE_REQUESTABLE_CHANNEL_CLASS_LIST, caps)); } tp_svc_connection_interface_contact_capabilities_emit_contact_capabilities_changed ( self, capabilities); } void tp_tests_contacts_connection_change_contact_info ( TpTestsContactsConnection *self, TpHandle handle, GPtrArray *info) { g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle), g_ptr_array_ref (info)); tp_svc_connection_interface_contact_info_emit_contact_info_changed (self, handle, info); } void tp_tests_contacts_connection_set_default_contact_info ( TpTestsContactsConnection *self, GPtrArray *info) { if (self->priv->default_contact_info != NULL) g_ptr_array_unref (self->priv->default_contact_info); self->priv->default_contact_info = g_ptr_array_ref (info); } static void my_get_alias_flags (TpSvcConnectionInterfaceAliasing *aliasing, DBusGMethodInvocation *context) { TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); tp_svc_connection_interface_aliasing_return_from_get_alias_flags (context, 0); } static void my_get_aliases (TpSvcConnectionInterfaceAliasing *aliasing, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing); TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GHashTable *result; GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); const gchar *alias = g_hash_table_lookup (self->priv->aliases, GUINT_TO_POINTER (handle)); if (alias == NULL) g_hash_table_insert (result, GUINT_TO_POINTER (handle), (gchar *) tp_handle_inspect (contact_repo, handle)); else g_hash_table_insert (result, GUINT_TO_POINTER (handle), (gchar *) alias); } tp_svc_connection_interface_aliasing_return_from_get_aliases (context, result); g_hash_table_destroy (result); } static void my_request_aliases (TpSvcConnectionInterfaceAliasing *aliasing, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (aliasing); TpBaseConnection *base = TP_BASE_CONNECTION (aliasing); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GPtrArray *result; gchar **strings; GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_ptr_array_sized_new (contacts->len + 1); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); const gchar *alias = g_hash_table_lookup (self->priv->aliases, GUINT_TO_POINTER (handle)); if (alias == NULL) g_ptr_array_add (result, (gchar *) tp_handle_inspect (contact_repo, handle)); else g_ptr_array_add (result, (gchar *) alias); } g_ptr_array_add (result, NULL); strings = (gchar **) g_ptr_array_free (result, FALSE); tp_svc_connection_interface_aliasing_return_from_request_aliases (context, (const gchar **) strings); g_free (strings); } static void init_aliasing (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceAliasingClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\ klass, my_##x) IMPLEMENT(get_alias_flags); IMPLEMENT(request_aliases); IMPLEMENT(get_aliases); /* IMPLEMENT(set_aliases); */ #undef IMPLEMENT } static void my_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); TpBaseConnection *base = TP_BASE_CONNECTION (avatars); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GHashTable *result; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); AvatarData *a = g_hash_table_lookup (self->priv->avatars, GUINT_TO_POINTER (handle)); if (a == NULL || a->token == NULL) { /* we're expected to do a round-trip to the server to find out * their token, so we have to give some sort of result. Assume * no avatar, here */ a = avatar_data_new (NULL, NULL, ""); g_hash_table_insert (self->priv->avatars, GUINT_TO_POINTER (handle), a); tp_svc_connection_interface_avatars_emit_avatar_updated (self, handle, a->token); } g_hash_table_insert (result, GUINT_TO_POINTER (handle), a->token); } tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens ( context, result); g_hash_table_destroy (result); } static void my_get_known_avatar_tokens (TpSvcConnectionInterfaceAvatars *avatars, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); TpBaseConnection *base = TP_BASE_CONNECTION (avatars); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GHashTable *result; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); AvatarData *a = g_hash_table_lookup (self->priv->avatars, GUINT_TO_POINTER (handle)); const gchar *token = a ? a->token : NULL; g_hash_table_insert (result, GUINT_TO_POINTER (handle), (gchar *) (token != NULL ? token : "")); } tp_svc_connection_interface_avatars_return_from_get_known_avatar_tokens ( context, result); g_hash_table_destroy (result); } static void my_request_avatars (TpSvcConnectionInterfaceAvatars *avatars, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); TpBaseConnection *base = TP_BASE_CONNECTION (avatars); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); AvatarData *a = g_hash_table_lookup (self->priv->avatars, GUINT_TO_POINTER (handle)); if (a != NULL) tp_svc_connection_interface_avatars_emit_avatar_retrieved (self, handle, a->token, a->data, a->mime_type); } tp_svc_connection_interface_avatars_return_from_request_avatars (context); } static void conn_avatars_properties_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { GQuark q_mime_types = g_quark_from_static_string ( "SupportedAvatarMIMETypes"); if (name == q_mime_types) { g_value_set_static_boxed (value, mime_types); } else { g_value_set_uint (value, GPOINTER_TO_UINT (getter_data)); } } static void init_avatars (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceAvatarsClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x (\ klass, my_##x) /* IMPLEMENT(get_avatar_requirements); */ IMPLEMENT(get_avatar_tokens); IMPLEMENT(get_known_avatar_tokens); /* IMPLEMENT(request_avatar); */ IMPLEMENT(request_avatars); /* IMPLEMENT(set_avatar); */ /* IMPLEMENT(clear_avatar); */ #undef IMPLEMENT } static void my_get_locations (TpSvcConnectionInterfaceLocation *avatars, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (avatars); TpBaseConnection *base = TP_BASE_CONNECTION (avatars); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GHashTable *result; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GHashTable *location = g_hash_table_lookup (self->priv->locations, GUINT_TO_POINTER (handle)); if (location != NULL) { g_hash_table_insert (result, GUINT_TO_POINTER (handle), location); } } tp_svc_connection_interface_location_return_from_get_locations ( context, result); g_hash_table_destroy (result); } static void init_location (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceLocationClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_location_implement_##x (\ klass, my_##x) IMPLEMENT(get_locations); #undef IMPLEMENT } static void my_get_contact_capabilities (TpSvcConnectionInterfaceContactCapabilities *obj, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GHashTable *result; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } result = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, TpHandle, i); GPtrArray *arr = g_hash_table_lookup (self->priv->capabilities, GUINT_TO_POINTER (handle)); if (arr != NULL) { g_hash_table_insert (result, GUINT_TO_POINTER (handle), arr); } } tp_svc_connection_interface_contact_capabilities_return_from_get_contact_capabilities ( context, result); g_hash_table_destroy (result); } static void init_contact_caps (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceContactCapabilitiesClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_contact_capabilities_implement_##x (\ klass, my_##x) IMPLEMENT(get_contact_capabilities); #undef IMPLEMENT } static GPtrArray * lookup_contact_info (TpTestsContactsConnection *self, TpHandle handle) { GPtrArray *ret = g_hash_table_lookup (self->priv->contact_info, GUINT_TO_POINTER (handle)); if (ret == NULL && self->priv->default_contact_info != NULL) { ret = self->priv->default_contact_info; g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (handle), g_ptr_array_ref (ret)); } if (ret == NULL) return g_ptr_array_new (); return g_ptr_array_ref (ret); } static void my_refresh_contact_info (TpSvcConnectionInterfaceContactInfo *obj, const GArray *contacts, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; guint i; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handles_are_valid (contact_repo, contacts, FALSE, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } for (i = 0; i < contacts->len; i++) { TpHandle handle = g_array_index (contacts, guint, i); GPtrArray *arr = lookup_contact_info (self, handle); tp_svc_connection_interface_contact_info_emit_contact_info_changed (self, handle, arr); g_ptr_array_unref (arr); } tp_svc_connection_interface_contact_info_return_from_refresh_contact_info ( context); } static void my_request_contact_info (TpSvcConnectionInterfaceContactInfo *obj, guint handle, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT); GError *error = NULL; GPtrArray *ret; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); if (!tp_handle_is_valid (contact_repo, handle, &error)) { dbus_g_method_return_error (context, error); g_error_free (error); return; } ret = lookup_contact_info (self, handle); tp_svc_connection_interface_contact_info_return_from_request_contact_info ( context, ret); g_ptr_array_unref (ret); } static void my_set_contact_info (TpSvcConnectionInterfaceContactInfo *obj, const GPtrArray *info, DBusGMethodInvocation *context) { TpTestsContactsConnection *self = TP_TESTS_CONTACTS_CONNECTION (obj); TpBaseConnection *base = TP_BASE_CONNECTION (obj); GPtrArray *copy; guint i; TpHandle self_handle; TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); /* Deep copy info */ copy = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free); for (i = 0; i < info->len; i++) g_ptr_array_add (copy, g_value_array_copy (g_ptr_array_index (info, i))); self_handle = tp_base_connection_get_self_handle (base); g_hash_table_insert (self->priv->contact_info, GUINT_TO_POINTER (self_handle), copy); tp_svc_connection_interface_contact_info_return_from_set_contact_info ( context); } static void init_contact_info (gpointer g_iface, gpointer iface_data) { TpSvcConnectionInterfaceContactInfoClass *klass = g_iface; #define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x (\ klass, my_##x) IMPLEMENT (refresh_contact_info); IMPLEMENT (request_contact_info); IMPLEMENT (set_contact_info); #undef IMPLEMENT } /* =============== Legacy version (no Contacts interface) ================= */ G_DEFINE_TYPE (TpTestsLegacyContactsConnection, tp_tests_legacy_contacts_connection, TP_TESTS_TYPE_CONTACTS_CONNECTION); enum { LEGACY_PROP_HAS_IMMORTAL_HANDLES = 1 }; static void legacy_contacts_connection_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case LEGACY_PROP_HAS_IMMORTAL_HANDLES: /* Pretend we don't. */ g_value_set_boolean (value, FALSE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void tp_tests_legacy_contacts_connection_init (TpTestsLegacyContactsConnection *self) { } static void tp_tests_legacy_contacts_connection_class_init ( TpTestsLegacyContactsConnectionClass *klass) { /* Leave Contacts out of the interfaces we say are present, so clients * won't use it */ static const gchar *interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_ALIASING, TP_IFACE_CONNECTION_INTERFACE_AVATARS, TP_IFACE_CONNECTION_INTERFACE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_LOCATION, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL }; TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; object_class->get_property = legacy_contacts_connection_get_property; base_class->interfaces_always_present = interfaces_always_present; g_object_class_override_property (object_class, LEGACY_PROP_HAS_IMMORTAL_HANDLES, "has-immortal-handles"); } /* =============== No Requests and no ContactCapabilities ================= */ G_DEFINE_TYPE (TpTestsNoRequestsConnection, tp_tests_no_requests_connection, TP_TESTS_TYPE_CONTACTS_CONNECTION); static void tp_tests_no_requests_connection_init (TpTestsNoRequestsConnection *self) { } static void tp_tests_no_requests_connection_class_init ( TpTestsNoRequestsConnectionClass *klass) { static const gchar *interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_ALIASING, TP_IFACE_CONNECTION_INTERFACE_AVATARS, TP_IFACE_CONNECTION_INTERFACE_CONTACTS, TP_IFACE_CONNECTION_INTERFACE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, TP_IFACE_CONNECTION_INTERFACE_LOCATION, NULL }; TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; base_class->interfaces_always_present = interfaces_always_present; } contactsd-1.4.14/tests/libtelepathy/contacts-conn.h000066400000000000000000000162321440357512200223440ustar00rootroot00000000000000/* * contacts-conn.h - header for a connection with contact info * * Copyright (C) 2007-2008 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TP_TESTS_CONTACTS_CONN_H__ #define __TP_TESTS_CONTACTS_CONN_H__ #include #include #include #include #include "simple-conn.h" #include "contact-list-manager.h" G_BEGIN_DECLS typedef struct _TpTestsContactsConnection TpTestsContactsConnection; typedef struct _TpTestsContactsConnectionClass TpTestsContactsConnectionClass; typedef struct _TpTestsContactsConnectionPrivate TpTestsContactsConnectionPrivate; struct _TpTestsContactsConnectionClass { TpTestsSimpleConnectionClass parent_class; TpPresenceMixinClass presence_mixin; TpContactsMixinClass contacts_mixin; TpDBusPropertiesMixinClass properties_class; }; struct _TpTestsContactsConnection { TpTestsSimpleConnection parent; TpPresenceMixin presence_mixin; TpContactsMixin contacts_mixin; TpTestsContactsConnectionPrivate *priv; }; GType tp_tests_contacts_connection_get_type (void); /* Must match my_statuses in the .c */ typedef enum { TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET, TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE, TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY, TP_TESTS_CONTACTS_CONNECTION_STATUS_AWAY, TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE, TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN, TP_TESTS_CONTACTS_CONNECTION_STATUS_ERROR } TpTestsContactsConnectionPresenceStatusIndex; /* TYPE MACROS */ #define TP_TESTS_TYPE_CONTACTS_CONNECTION \ (tp_tests_contacts_connection_get_type ()) #define TP_TESTS_CONTACTS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ TpTestsContactsConnection)) #define TP_TESTS_CONTACTS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ TpTestsContactsConnectionClass)) #define TP_TESTS_IS_CONTACTS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION)) #define TP_TESTS_IS_CONTACTS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_CONTACTS_CONNECTION)) #define TP_TESTS_CONTACTS_CONNECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_CONTACTS_CONNECTION, \ TpTestsContactsConnectionClass)) TestContactListManager *tp_tests_contacts_connection_get_contact_list_manager ( TpTestsContactsConnection *self); void tp_tests_contacts_connection_change_aliases ( TpTestsContactsConnection *self, guint n, const TpHandle *handles, const gchar * const *aliases); void tp_tests_contacts_connection_change_presences ( TpTestsContactsConnection *self, guint n, const TpHandle *handles, const TpTestsContactsConnectionPresenceStatusIndex *indexes, const gchar * const *messages); void tp_tests_contacts_connection_change_avatar_tokens ( TpTestsContactsConnection *self, guint n, const TpHandle *handles, const gchar * const *tokens); void tp_tests_contacts_connection_change_avatar_data ( TpTestsContactsConnection *self, TpHandle handle, GArray *data, const gchar *mime_type, const gchar *token); void tp_tests_contacts_connection_change_locations ( TpTestsContactsConnection *self, guint n, const TpHandle *handles, GHashTable **locations); void tp_tests_contacts_connection_change_capabilities ( TpTestsContactsConnection *self, GHashTable *capabilities); void tp_tests_contacts_connection_change_contact_info ( TpTestsContactsConnection *self, TpHandle handle, GPtrArray *info); void tp_tests_contacts_connection_set_default_contact_info ( TpTestsContactsConnection *self, GPtrArray *info); /* Legacy version (no Contacts interface, and no immortal handles) */ typedef struct _TpTestsLegacyContactsConnection TpTestsLegacyContactsConnection; typedef struct _TpTestsLegacyContactsConnectionClass TpTestsLegacyContactsConnectionClass; typedef struct _TpTestsLegacyContactsConnectionPrivate TpTestsLegacyContactsConnectionPrivate; struct _TpTestsLegacyContactsConnectionClass { TpTestsContactsConnectionClass parent_class; }; struct _TpTestsLegacyContactsConnection { TpTestsContactsConnection parent; TpTestsLegacyContactsConnectionPrivate *priv; }; GType tp_tests_legacy_contacts_connection_get_type (void); /* TYPE MACROS */ #define TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION \ (tp_tests_legacy_contacts_connection_get_type ()) #define LEGACY_TP_TESTS_CONTACTS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \ TpTestsLegacyContactsConnection)) #define LEGACY_TP_TESTS_CONTACTS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \ TpTestsLegacyContactsConnectionClass)) #define TP_TESTS_LEGACY_CONTACTS_IS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION)) #define TP_TESTS_LEGACY_CONTACTS_IS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION)) #define LEGACY_TP_TESTS_CONTACTS_CONNECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_LEGACY_CONTACTS_CONNECTION, \ TpTestsLegacyContactsConnectionClass)) /* No Requests version */ typedef struct _TpTestsNoRequestsConnection TpTestsNoRequestsConnection; typedef struct _TpTestsNoRequestsConnectionClass TpTestsNoRequestsConnectionClass; typedef struct _TpTestsNoRequestsConnectionPrivate TpTestsNoRequestsConnectionPrivate; struct _TpTestsNoRequestsConnectionClass { TpTestsContactsConnectionClass parent_class; }; struct _TpTestsNoRequestsConnection { TpTestsContactsConnection parent; TpTestsNoRequestsConnectionPrivate *priv; }; GType tp_tests_no_requests_connection_get_type (void); /* TYPE MACROS */ #define TP_TESTS_TYPE_NO_REQUESTS_CONNECTION \ (tp_tests_no_requests_connection_get_type ()) #define TP_TESTS_NO_REQUESTS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \ TpTestsNoRequestsConnection)) #define TP_TESTS_NO_REQUESTS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \ TpTestsNoRequestsConnectionClass)) #define TP_TESTS_NO_REQUESTS_IS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION)) #define TP_TESTS_NO_REQUESTS_IS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION)) #define TP_TESTS_NO_REQUESTS_CONNECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_NO_REQUESTS_CONNECTION, \ TpTestsNoRequestsConnectionClass)) G_END_DECLS #endif /* ifndef __TP_TESTS_CONTACTS_CONN_H__ */ contactsd-1.4.14/tests/libtelepathy/debug.c000066400000000000000000000011551440357512200206520ustar00rootroot00000000000000/* * Copyright © 2011 Collabora Ltd. * Copyright © 2011 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "debug.h" static gboolean enabled = FALSE; void _test_log (const gchar *format, ...) { if (enabled) { va_list args; va_start (args, format); g_logv ("test", G_LOG_LEVEL_DEBUG, format, args); va_end (args); } } void test_debug_enable (gboolean enable) { enabled = enable; } contactsd-1.4.14/tests/libtelepathy/debug.h000066400000000000000000000010401440357512200206500ustar00rootroot00000000000000/* * Copyright © 2011 Collabora Ltd. * Copyright © 2011 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TEST_DEBUG_H__ #define __TEST_DEBUG_H__ #include #undef DEBUG #define DEBUG(format, ...) \ _test_log ("%s: " format, G_STRFUNC, ##__VA_ARGS__) G_BEGIN_DECLS void test_debug_enable (gboolean enable); G_END_DECLS #endif contactsd-1.4.14/tests/libtelepathy/libtelepathy.pro000066400000000000000000000027631440357512200226360ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. TARGET = telepathy TEMPLATE = lib CONFIG -= warn_on qt CONFIG += link_pkgconfig CONFIG += staticlib PKGCONFIG += telepathy-glib dbus-glib-1 gio-2.0 HEADERS += \ contacts-conn.h \ contact-list-manager.h \ debug.h \ simple-account.h \ simple-account-manager.h \ simple-conn.h \ textchan-null.h \ util.h SOURCES += \ contacts-conn.c \ contact-list-manager.c \ debug.c \ simple-account.c \ simple-account-manager.c \ simple-conn.c \ textchan-null.c \ util.c contactsd-1.4.14/tests/libtelepathy/simple-account-manager.c000066400000000000000000000150011440357512200241120ustar00rootroot00000000000000/* * simple-account-manager.c - a simple account manager service. * * Copyright (C) 2007-2009 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "simple-account-manager.h" #include #include #include #include static void account_manager_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleAccountManager, tp_tests_simple_account_manager, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_ACCOUNT_MANAGER, account_manager_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init) ) /* TP_IFACE_ACCOUNT_MANAGER is implied */ static const char *ACCOUNT_MANAGER_INTERFACES[] = { NULL }; enum { PROP_0, PROP_INTERFACES, PROP_VALID_ACCOUNTS, PROP_INVALID_ACCOUNTS, }; struct _TpTestsSimpleAccountManagerPrivate { GPtrArray *valid_accounts; GPtrArray *invalid_accounts; }; static void tp_tests_simple_account_manager_create_account (TpSvcAccountManager *self, const gchar *in_Connection_Manager, const gchar *in_Protocol, const gchar *in_Display_Name, GHashTable *in_Parameters, GHashTable *in_Properties, DBusGMethodInvocation *context) { const gchar *out_Account = "/some/fake/account/i/think"; tp_svc_account_manager_return_from_create_account (context, out_Account); } static void account_manager_iface_init (gpointer klass, gpointer unused G_GNUC_UNUSED) { #define IMPLEMENT(x) tp_svc_account_manager_implement_##x (\ klass, tp_tests_simple_account_manager_##x) IMPLEMENT (create_account); #undef IMPLEMENT } static void tp_tests_simple_account_manager_init (TpTestsSimpleAccountManager *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, TpTestsSimpleAccountManagerPrivate); self->priv->valid_accounts = g_ptr_array_new_with_free_func (g_free); self->priv->invalid_accounts = g_ptr_array_new_with_free_func (g_free); } static void tp_tests_simple_account_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *spec) { TpTestsSimpleAccountManager *self = SIMPLE_ACCOUNT_MANAGER (object); guint i = 0; switch (property_id) { case PROP_INTERFACES: g_value_set_boxed (value, ACCOUNT_MANAGER_INTERFACES); break; case PROP_VALID_ACCOUNTS: g_value_set_boxed (value, self->priv->valid_accounts); break; case PROP_INVALID_ACCOUNTS: g_value_set_boxed (value, self->priv->invalid_accounts); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); break; } } static void tp_tests_simple_account_manager_finalize (GObject *object) { TpTestsSimpleAccountManager *self = SIMPLE_ACCOUNT_MANAGER (object); g_ptr_array_unref (self->priv->valid_accounts); g_ptr_array_unref (self->priv->invalid_accounts); G_OBJECT_CLASS (tp_tests_simple_account_manager_parent_class)->finalize(object); } /** * This class currently only provides the minimum for * tp_account_manager_prepare to succeed. This turns out to be only a working * Properties.GetAll(). If we wanted later to check the case where * tp_account_prepare succeeds, we would need to implement an account object * too. */ static void tp_tests_simple_account_manager_class_init ( TpTestsSimpleAccountManagerClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; GParamSpec *param_spec; static TpDBusPropertiesMixinPropImpl am_props[] = { { "Interfaces", "interfaces", NULL }, { "ValidAccounts", "valid-accounts", NULL }, { "InvalidAccounts", "invalid-accounts", NULL }, /* { "SupportedAccountProperties", "supported-account-properties", NULL }, */ { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_ACCOUNT_MANAGER, tp_dbus_properties_mixin_getter_gobject_properties, NULL, am_props }, { NULL }, }; g_type_class_add_private (klass, sizeof (TpTestsSimpleAccountManagerPrivate)); object_class->finalize = tp_tests_simple_account_manager_finalize; object_class->get_property = tp_tests_simple_account_manager_get_property; param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces", "In this case we only implement AccountManager, so none.", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INTERFACES, param_spec); param_spec = g_param_spec_boxed ("valid-accounts", "Valid accounts", "The accounts which are valid on this account. This may be a lie.", TP_ARRAY_TYPE_OBJECT_PATH_LIST, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_VALID_ACCOUNTS, param_spec); param_spec = g_param_spec_boxed ("invalid-accounts", "Invalid accounts", "The accounts which are invalid on this account. This may be a lie.", TP_ARRAY_TYPE_OBJECT_PATH_LIST, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_INVALID_ACCOUNTS, param_spec); klass->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsSimpleAccountManagerClass, dbus_props_class)); } void tp_tests_simple_account_manager_add_account ( TpTestsSimpleAccountManager *self, const gchar *object_path, gboolean valid) { if (valid) g_ptr_array_add (self->priv->valid_accounts, g_strdup (object_path)); else g_ptr_array_add (self->priv->invalid_accounts, g_strdup (object_path)); tp_svc_account_manager_emit_account_validity_changed (self, object_path, valid); } static void remove_from_array (GPtrArray *array, const gchar *str) { guint i; for (i = 0; i < array->len; i++) if (!tp_strdiff (str, g_ptr_array_index (array, i))) { g_ptr_array_remove_index_fast (array, i); return; } } void tp_tests_simple_account_manager_remove_account ( TpTestsSimpleAccountManager *self, const gchar *object_path) { remove_from_array (self->priv->valid_accounts, object_path); remove_from_array (self->priv->valid_accounts, object_path); tp_svc_account_manager_emit_account_removed (self, object_path); } contactsd-1.4.14/tests/libtelepathy/simple-account-manager.h000066400000000000000000000044611440357512200241270ustar00rootroot00000000000000/* * simple-account-manager.h - header for a simple account manager service. * * Copyright (C) 2007-2009 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TP_TESTS_SIMPLE_ACCOUNT_MANAGER_H__ #define __TP_TESTS_SIMPLE_ACCOUNT_MANAGER_H__ #include #include G_BEGIN_DECLS typedef struct _TpTestsSimpleAccountManager TpTestsSimpleAccountManager; typedef struct _TpTestsSimpleAccountManagerClass TpTestsSimpleAccountManagerClass; typedef struct _TpTestsSimpleAccountManagerPrivate TpTestsSimpleAccountManagerPrivate; struct _TpTestsSimpleAccountManagerClass { GObjectClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _TpTestsSimpleAccountManager { GObject parent; TpTestsSimpleAccountManagerPrivate *priv; }; GType tp_tests_simple_account_manager_get_type (void); /* TYPE MACROS */ #define TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER \ (tp_tests_simple_account_manager_get_type ()) #define SIMPLE_ACCOUNT_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, \ TpTestsSimpleAccountManager)) #define SIMPLE_ACCOUNT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, \ TpTestsSimpleAccountManagerClass)) #define SIMPLE_IS_ACCOUNT_MANAGER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER)) #define SIMPLE_IS_ACCOUNT_MANAGER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER)) #define SIMPLE_ACCOUNT_MANAGER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, \ TpTestsSimpleAccountManagerClass)) void tp_tests_simple_account_manager_add_account ( TpTestsSimpleAccountManager *self, const gchar *object_path, gboolean valid); void tp_tests_simple_account_manager_remove_account ( TpTestsSimpleAccountManager *self, const gchar *object_path); G_END_DECLS #endif /* #ifndef __TP_TESTS_SIMPLE_ACCOUNT_MANAGER_H__ */ contactsd-1.4.14/tests/libtelepathy/simple-account.c000066400000000000000000000455761440357512200225260ustar00rootroot00000000000000/* * simple-account.c - a simple account service. * * Copyright (C) 2010 Collabora Ltd. * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "simple-account.h" #include #include #include #include #include #include #include #include #include #include static void account_iface_init (gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE (TpTestsSimpleAccount, tp_tests_simple_account, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_ACCOUNT, account_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init) ) /* TP_IFACE_ACCOUNT is implied */ static const char *ACCOUNT_INTERFACES[] = { NULL }; enum { PROP_0, PROP_INTERFACES, PROP_DISPLAY_NAME, PROP_ICON, PROP_VALID, PROP_ENABLED, PROP_NICKNAME, PROP_PARAMETERS, PROP_AUTOMATIC_PRESENCE, PROP_CONNECT_AUTO, PROP_CONNECTION, PROP_CONNECTION_STATUS, PROP_CONNECTION_STATUS_REASON, PROP_CURRENT_PRESENCE, PROP_REQUESTED_PRESENCE, PROP_NORMALIZED_NAME, PROP_HAS_BEEN_ONLINE, }; struct _TpTestsSimpleAccountPrivate { TpConnection *connection; TpConnectionStatus connection_status; TpConnectionStatusReason connection_status_reason; gboolean has_been_online; gboolean enabled; GHashTable *parameters; TpContact *self_contact; gchar *nickname; gchar *normalized_name; GValueArray *current_presence; }; static void connection_invalidated_cb (TpProxy *proxy, guint domain, gint code, gchar *message, TpTestsSimpleAccount *self); static void account_iface_init (gpointer klass, gpointer unused G_GNUC_UNUSED) { #define IMPLEMENT(x) tp_svc_account_implement_##x (\ klass, tp_tests_simple_account_##x) /* TODO */ #undef IMPLEMENT } static void tp_tests_simple_account_init (TpTestsSimpleAccount *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_SIMPLE_ACCOUNT, TpTestsSimpleAccountPrivate); self->priv->connection_status = TP_CONNECTION_STATUS_DISCONNECTED; self->priv->connection_status_reason = TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED; self->priv->has_been_online = FALSE; self->priv->enabled = TRUE; self->priv->parameters = g_hash_table_new (NULL, NULL); self->priv->nickname = g_strdup (""); self->priv->normalized_name = g_strdup (""); self->priv->current_presence = tp_value_array_build (3, G_TYPE_UINT, TP_CONNECTION_PRESENCE_TYPE_OFFLINE, G_TYPE_STRING, "offline", G_TYPE_STRING, "", G_TYPE_INVALID); } static void tp_tests_simple_account_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *spec) { TpTestsSimpleAccount *self = TP_TESTS_SIMPLE_ACCOUNT (object); GValueArray *offline_presence; offline_presence = tp_value_array_build (3, G_TYPE_UINT, TP_CONNECTION_PRESENCE_TYPE_OFFLINE, G_TYPE_STRING, "offline", G_TYPE_STRING, "", G_TYPE_INVALID); switch (property_id) { case PROP_INTERFACES: g_value_set_boxed (value, ACCOUNT_INTERFACES); break; case PROP_DISPLAY_NAME: g_value_set_string (value, "Fake Account"); break; case PROP_ICON: g_value_set_string (value, ""); break; case PROP_VALID: g_value_set_boolean (value, TRUE); break; case PROP_ENABLED: g_value_set_boolean (value, self->priv->enabled); break; case PROP_NICKNAME: g_value_set_string (value, self->priv->nickname); break; case PROP_PARAMETERS: g_value_set_boxed (value, self->priv->parameters); break; case PROP_AUTOMATIC_PRESENCE: g_value_set_boxed (value, offline_presence); break; case PROP_CONNECT_AUTO: g_value_set_boolean (value, FALSE); break; case PROP_CONNECTION: if (self->priv->connection != NULL) g_value_set_boxed (value, tp_proxy_get_object_path (self->priv->connection)); else g_value_set_boxed (value, "/"); break; case PROP_CONNECTION_STATUS: g_value_set_uint (value, self->priv->connection_status); break; case PROP_CONNECTION_STATUS_REASON: g_value_set_uint (value, self->priv->connection_status_reason); break; case PROP_CURRENT_PRESENCE: g_value_set_boxed (value, self->priv->current_presence); break; case PROP_REQUESTED_PRESENCE: g_value_set_boxed (value, offline_presence); break; case PROP_NORMALIZED_NAME: g_value_set_string (value, self->priv->normalized_name); break; case PROP_HAS_BEEN_ONLINE: g_value_set_boolean (value, self->priv->has_been_online); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); break; } g_boxed_free (TP_STRUCT_TYPE_SIMPLE_PRESENCE, offline_presence); } static void tp_tests_simple_account_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *spec) { TpTestsSimpleAccount *self = TP_TESTS_SIMPLE_ACCOUNT (object); switch (property_id) { case PROP_PARAMETERS: if (g_value_get_boxed (value) != NULL) { if (self->priv->parameters) g_hash_table_unref (self->priv->parameters); self->priv->parameters = g_value_dup_boxed (value); } else { g_hash_table_remove_all (self->priv->parameters); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); break; } } static void tp_tests_simple_account_finalize (GObject *object) { TpTestsSimpleAccount *self = TP_TESTS_SIMPLE_ACCOUNT (object); if (self->priv->connection) { g_signal_handlers_disconnect_by_func (self->priv->connection, connection_invalidated_cb, self); g_object_unref (self->priv->connection); } if (self->priv->parameters) g_hash_table_unref (self->priv->parameters); if (self->priv->self_contact != NULL) g_object_unref (self->priv->self_contact); g_free (self->priv->nickname); g_free (self->priv->normalized_name); g_value_array_free (self->priv->current_presence); G_OBJECT_CLASS (tp_tests_simple_account_parent_class)->finalize(object); } /** * This class currently only provides the minimum for * tp_account_prepare to succeed. This turns out to be only a working * Properties.GetAll(). */ static void tp_tests_simple_account_class_init (TpTestsSimpleAccountClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; GParamSpec *param_spec; static TpDBusPropertiesMixinPropImpl a_props[] = { { "Interfaces", "interfaces", NULL }, { "DisplayName", "display-name", NULL }, { "Icon", "icon", NULL }, { "Valid", "valid", NULL }, { "Enabled", "enabled", NULL }, { "Nickname", "nickname", NULL }, { "Parameters", "parameters", NULL }, { "AutomaticPresence", "automatic-presence", NULL }, { "ConnectAutomatically", "connect-automatically", NULL }, { "Connection", "connection", NULL }, { "ConnectionStatus", "connection-status", NULL }, { "ConnectionStatusReason", "connection-status-reason", NULL }, { "CurrentPresence", "current-presence", NULL }, { "RequestedPresence", "requested-presence", NULL }, { "NormalizedName", "normalized-name", NULL }, { "HasBeenOnline", "has-been-online", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_ACCOUNT, tp_dbus_properties_mixin_getter_gobject_properties, NULL, a_props }, { NULL }, }; g_type_class_add_private (klass, sizeof (TpTestsSimpleAccountPrivate)); object_class->set_property = tp_tests_simple_account_set_property; object_class->get_property = tp_tests_simple_account_get_property; object_class->finalize = tp_tests_simple_account_finalize; param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces", "In this case we only implement Account, so none.", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INTERFACES, param_spec); param_spec = g_param_spec_string ("display-name", "display name", "DisplayName property", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_DISPLAY_NAME, param_spec); param_spec = g_param_spec_string ("icon", "icon", "Icon property", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ICON, param_spec); param_spec = g_param_spec_boolean ("valid", "valid", "Valid property", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_VALID, param_spec); param_spec = g_param_spec_boolean ("enabled", "enabled", "Enabled property", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_ENABLED, param_spec); param_spec = g_param_spec_string ("nickname", "nickname", "Nickname property", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NICKNAME, param_spec); param_spec = g_param_spec_boxed ("parameters", "parameters", "Parameters property", TP_HASH_TYPE_STRING_VARIANT_MAP, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_PARAMETERS, param_spec); param_spec = g_param_spec_boxed ("automatic-presence", "automatic presence", "AutomaticPresence property", TP_STRUCT_TYPE_SIMPLE_PRESENCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_AUTOMATIC_PRESENCE, param_spec); param_spec = g_param_spec_boolean ("connect-automatically", "connect automatically", "ConnectAutomatically property", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECT_AUTO, param_spec); param_spec = g_param_spec_boxed ("connection", "connection", "Connection property", DBUS_TYPE_G_OBJECT_PATH, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); param_spec = g_param_spec_uint ("connection-status", "connection status", "ConnectionStatus property", 0, NUM_TP_CONNECTION_STATUSES, TP_CONNECTION_STATUS_DISCONNECTED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION_STATUS, param_spec); param_spec = g_param_spec_uint ("connection-status-reason", "connection status reason", "ConnectionStatusReason property", 0, NUM_TP_CONNECTION_STATUS_REASONS, TP_CONNECTION_STATUS_REASON_NONE_SPECIFIED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CONNECTION_STATUS_REASON, param_spec); param_spec = g_param_spec_boxed ("current-presence", "current presence", "CurrentPresence property", TP_STRUCT_TYPE_SIMPLE_PRESENCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_CURRENT_PRESENCE, param_spec); param_spec = g_param_spec_boxed ("requested-presence", "requested presence", "RequestedPresence property", TP_STRUCT_TYPE_SIMPLE_PRESENCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REQUESTED_PRESENCE, param_spec); param_spec = g_param_spec_string ("normalized-name", "normalized name", "NormalizedName property", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_NORMALIZED_NAME, param_spec); param_spec = g_param_spec_boolean ("has-been-online", "has been online", "HasBeenOnline property", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_HAS_BEEN_ONLINE, param_spec); klass->dbus_props_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsSimpleAccountClass, dbus_props_class)); } static void remove_connection (TpTestsSimpleAccount *self) { GHashTable *change; self->priv->connection_status = tp_connection_get_status (self->priv->connection, &self->priv->connection_status_reason); g_signal_handlers_disconnect_by_func (self->priv->connection, connection_invalidated_cb, self); g_object_unref (self->priv->connection); self->priv->connection = NULL; if (self->priv->self_contact != NULL) { g_object_unref (self->priv->self_contact); self->priv->self_contact = NULL; } g_value_array_free (self->priv->current_presence); self->priv->current_presence = tp_value_array_build (3, G_TYPE_UINT, TP_CONNECTION_PRESENCE_TYPE_OFFLINE, G_TYPE_STRING, "offline", G_TYPE_STRING, "", G_TYPE_INVALID); change = tp_asv_new (NULL, NULL); tp_asv_set_object_path (change, "Connection", "/"); tp_asv_set_uint32 (change, "ConnectionStatus", self->priv->connection_status); tp_asv_set_uint32 (change, "ConnectionStatusReason", self->priv->connection_status_reason); tp_asv_set_boxed (change, "CurrentPresence", TP_STRUCT_TYPE_SIMPLE_PRESENCE, self->priv->current_presence); tp_svc_account_emit_account_property_changed (self, change); g_hash_table_unref (change); } static void connection_invalidated_cb (TpProxy *proxy, guint domain, gint code, gchar *message, TpTestsSimpleAccount *self) { remove_connection (self); } static void on_alias_notify_cb (TpContact *self_contact, GParamSpec *pspec, TpTestsSimpleAccount *self) { GHashTable *change; g_free (self->priv->nickname); self->priv->nickname = g_strdup (tp_contact_get_alias (self->priv->self_contact)); change = tp_asv_new (NULL, NULL); tp_asv_set_string (change, "Nickname", self->priv->nickname); tp_svc_account_emit_account_property_changed (self, change); g_hash_table_unref (change); } static void on_presence_changed_cb (TpContact *self_contact, guint type, gchar *status, gchar *message, TpTestsSimpleAccount *self) { GHashTable *change; g_value_array_free (self->priv->current_presence); self->priv->current_presence = tp_value_array_build (3, G_TYPE_UINT, tp_contact_get_presence_type (self->priv->self_contact), G_TYPE_STRING, tp_contact_get_presence_status (self->priv->self_contact), G_TYPE_STRING, tp_contact_get_presence_message (self->priv->self_contact), G_TYPE_INVALID); change = tp_asv_new (NULL, NULL); tp_asv_set_boxed (change, "CurrentPresence", TP_STRUCT_TYPE_SIMPLE_PRESENCE, self->priv->current_presence); tp_svc_account_emit_account_property_changed (self, change); g_hash_table_unref (change); } static void got_self_contact_cb (TpConnection *connection, guint n_contacts, TpContact * const *contacts, guint n_failed, const TpHandle *failed, const GError *error, gpointer user_data, GObject *weak_object) { TpTestsSimpleAccount *self = user_data; GHashTable *change; if (n_failed != 0) return; self->priv->self_contact = g_object_ref (contacts[0]); self->priv->connection_status = tp_connection_get_status (self->priv->connection, &self->priv->connection_status_reason); self->priv->has_been_online = TRUE; g_free (self->priv->normalized_name); self->priv->normalized_name = g_strdup (tp_contact_get_identifier (self->priv->self_contact)); g_free (self->priv->nickname); self->priv->nickname = g_strdup (tp_contact_get_alias (self->priv->self_contact)); g_signal_connect (self->priv->self_contact, "notify::alias", G_CALLBACK (on_alias_notify_cb), self); g_value_array_free (self->priv->current_presence); self->priv->current_presence = tp_value_array_build (3, G_TYPE_UINT, tp_contact_get_presence_type (self->priv->self_contact), G_TYPE_STRING, tp_contact_get_presence_status (self->priv->self_contact), G_TYPE_STRING, tp_contact_get_presence_message (self->priv->self_contact), G_TYPE_INVALID); g_signal_connect (self->priv->self_contact, "presence-changed", G_CALLBACK (on_presence_changed_cb), self); change = tp_asv_new (NULL, NULL); tp_asv_set_object_path (change, "Connection", tp_proxy_get_object_path (self->priv->connection)); tp_asv_set_uint32 (change, "ConnectionStatus", self->priv->connection_status); tp_asv_set_uint32 (change, "ConnectionStatusReason", self->priv->connection_status_reason); tp_asv_set_boolean (change, "HasBeenOnline", self->priv->has_been_online); tp_asv_set_boxed (change, "CurrentPresence", TP_STRUCT_TYPE_SIMPLE_PRESENCE, self->priv->current_presence); tp_asv_set_string (change, "Nickname", self->priv->nickname); tp_asv_set_string (change, "NormalizedName", self->priv->normalized_name); tp_svc_account_emit_account_property_changed (self, change); g_hash_table_unref (change); } static void connection_ready_cb (TpConnection *connection, const GError *error, gpointer user_data) { TpTestsSimpleAccount *self = user_data; TpHandle self_handle; TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS, TP_CONTACT_FEATURE_PRESENCE }; if (error != NULL) return; self_handle = tp_connection_get_self_handle (self->priv->connection); tp_connection_get_contacts_by_handle (self->priv->connection, 1, &self_handle, G_N_ELEMENTS (features), features, got_self_contact_cb, self, NULL, G_OBJECT (self)); } void tp_tests_simple_account_set_connection (TpTestsSimpleAccount *self, const gchar *object_path) { if (self->priv->connection != NULL) remove_connection (self); if (object_path != NULL && tp_strdiff (object_path, "/")) { TpDBusDaemon *dbus = tp_dbus_daemon_dup (NULL); self->priv->connection = tp_connection_new (dbus, NULL, object_path, NULL); g_signal_connect (self->priv->connection, "invalidated", G_CALLBACK (connection_invalidated_cb), self); tp_connection_call_when_ready (self->priv->connection, connection_ready_cb, self); g_object_unref (dbus); } } void tp_tests_simple_account_removed (TpTestsSimpleAccount *self) { tp_svc_account_emit_removed (self); } void tp_tests_simple_account_set_enabled (TpTestsSimpleAccount *self, gboolean enabled) { GHashTable *change; self->priv->enabled = enabled; change = tp_asv_new (NULL, NULL); tp_asv_set_boolean (change, "Enabled", enabled); tp_svc_account_emit_account_property_changed(self, change); g_hash_table_unref (change); } contactsd-1.4.14/tests/libtelepathy/simple-account.h000066400000000000000000000041441440357512200225150ustar00rootroot00000000000000/* * simple-account.h - header for a simple account service. * * Copyright (C) 2010 Collabora Ltd. * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TP_TESTS_SIMPLE_ACCOUNT_H__ #define __TP_TESTS_SIMPLE_ACCOUNT_H__ #include #include #include G_BEGIN_DECLS typedef struct _TpTestsSimpleAccount TpTestsSimpleAccount; typedef struct _TpTestsSimpleAccountClass TpTestsSimpleAccountClass; typedef struct _TpTestsSimpleAccountPrivate TpTestsSimpleAccountPrivate; struct _TpTestsSimpleAccountClass { GObjectClass parent_class; TpDBusPropertiesMixinClass dbus_props_class; }; struct _TpTestsSimpleAccount { GObject parent; TpTestsSimpleAccountPrivate *priv; }; GType tp_tests_simple_account_get_type (void); /* TYPE MACROS */ #define TP_TESTS_TYPE_SIMPLE_ACCOUNT \ (tp_tests_simple_account_get_type ()) #define TP_TESTS_SIMPLE_ACCOUNT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT, \ TpTestsSimpleAccount)) #define TP_TESTS_SIMPLE_ACCOUNT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_ACCOUNT, \ TpTestsSimpleAccountClass)) #define TP_TESTS_SIMPLE_IS_ACCOUNT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT)) #define TP_TESTS_SIMPLE_IS_ACCOUNT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_ACCOUNT)) #define TP_TESTS_SIMPLE_ACCOUNT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_ACCOUNT, \ TpTestsSimpleAccountClass)) void tp_tests_simple_account_set_connection (TpTestsSimpleAccount *self, const gchar *object_path); void tp_tests_simple_account_removed (TpTestsSimpleAccount *self); void tp_tests_simple_account_set_enabled (TpTestsSimpleAccount *self, gboolean enabled); G_END_DECLS #endif /* #ifndef __TP_TESTS_SIMPLE_ACCOUNT_H__ */ contactsd-1.4.14/tests/libtelepathy/simple-conn.c000066400000000000000000000227561440357512200220220ustar00rootroot00000000000000/* * simple-conn.c - a simple connection * * Copyright (C) 2007-2008 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "simple-conn.h" #include #include #include #include #include #include #include #include #include "textchan-null.h" #include "util.h" G_DEFINE_TYPE (TpTestsSimpleConnection, tp_tests_simple_connection, TP_TYPE_BASE_CONNECTION); /* type definition stuff */ enum { PROP_ACCOUNT = 1, N_PROPS }; struct _TpTestsSimpleConnectionPrivate { gchar *account; guint connect_source; guint disconnect_source; /* TpHandle => reffed TpTestsTextChannelNull */ GHashTable *channels; }; static void tp_tests_simple_connection_init (TpTestsSimpleConnection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_SIMPLE_CONNECTION, TpTestsSimpleConnectionPrivate); self->priv->channels = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref); } static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *spec) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); switch (property_id) { case PROP_ACCOUNT: g_value_set_string (value, self->priv->account); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); } } static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *spec) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); switch (property_id) { case PROP_ACCOUNT: g_free (self->priv->account); self->priv->account = g_utf8_strdown (g_value_get_string (value), -1); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); } } static void dispose (GObject *object) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); g_hash_table_unref (self->priv->channels); G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->dispose (object); } static void finalize (GObject *object) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (object); if (self->priv->connect_source != 0) { g_source_remove (self->priv->connect_source); } if (self->priv->disconnect_source != 0) { g_source_remove (self->priv->disconnect_source); } g_free (self->priv->account); G_OBJECT_CLASS (tp_tests_simple_connection_parent_class)->finalize (object); } static gchar * get_unique_connection_name (TpBaseConnection *conn) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); return g_strdup (self->priv->account); } static gchar * tp_tests_simple_normalize_contact (TpHandleRepoIface *repo, const gchar *id, gpointer context, GError **error) { if (id[0] == '\0') { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE, "ID must not be empty"); return NULL; } if (strchr (id, ' ') != NULL) { g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_HANDLE, "ID must not contain spaces"); return NULL; } return g_utf8_strdown (id, -1); } static void create_handle_repos (TpBaseConnection *conn, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { repos[TP_HANDLE_TYPE_CONTACT] = tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT, tp_tests_simple_normalize_contact, NULL); } static GPtrArray * create_channel_factories (TpBaseConnection *conn) { return g_ptr_array_sized_new (0); } void tp_tests_simple_connection_inject_disconnect (TpTestsSimpleConnection *self) { tp_base_connection_change_status ((TpBaseConnection *) self, TP_CONNECTION_STATUS_DISCONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } static gboolean pretend_connected (gpointer data) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (data); TpBaseConnection *conn = (TpBaseConnection *) self; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); conn->self_handle = tp_handle_ensure (contact_repo, self->priv->account, NULL, NULL); if (conn->status == TP_CONNECTION_STATUS_CONNECTING) { tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); } self->priv->connect_source = 0; return FALSE; } static gboolean start_connecting (TpBaseConnection *conn, GError **error) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); tp_base_connection_change_status (conn, TP_CONNECTION_STATUS_CONNECTING, TP_CONNECTION_STATUS_REASON_REQUESTED); /* In a real connection manager we'd ask the underlying implementation to * start connecting, then go to state CONNECTED when finished. Here there * isn't actually a connection, so we'll fake a connection process that * takes time. */ self->priv->connect_source = g_timeout_add (0, pretend_connected, self); return TRUE; } static gboolean pretend_disconnected (gpointer data) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (data); tp_base_connection_finish_shutdown (TP_BASE_CONNECTION (data)); self->priv->disconnect_source = 0; return FALSE; } static void shut_down (TpBaseConnection *conn) { TpTestsSimpleConnection *self = TP_TESTS_SIMPLE_CONNECTION (conn); /* In a real connection manager we'd ask the underlying implementation to * start shutting down, then call this function when finished. Here there * isn't actually a connection, so we'll fake a disconnection process that * takes time. */ self->priv->disconnect_source = g_timeout_add (0, pretend_disconnected, conn); } static void tp_tests_simple_connection_class_init (TpTestsSimpleConnectionClass *klass) { TpBaseConnectionClass *base_class = (TpBaseConnectionClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; GParamSpec *param_spec; static const gchar *interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL }; object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; object_class->finalize = finalize; g_type_class_add_private (klass, sizeof (TpTestsSimpleConnectionPrivate)); base_class->create_handle_repos = create_handle_repos; base_class->get_unique_connection_name = get_unique_connection_name; base_class->create_channel_factories = create_channel_factories; base_class->start_connecting = start_connecting; base_class->shut_down = shut_down; base_class->interfaces_always_present = interfaces_always_present; param_spec = g_param_spec_string ("account", "Account name", "The username of this user", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec); } void tp_tests_simple_connection_set_identifier (TpTestsSimpleConnection *self, const gchar *identifier) { TpBaseConnection *conn = (TpBaseConnection *) self; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); TpHandle handle = tp_handle_ensure (contact_repo, identifier, NULL, NULL); /* if this fails then the identifier was bad - caller error */ g_return_if_fail (handle != 0); tp_base_connection_set_self_handle (conn, handle); tp_handle_unref (contact_repo, handle); } TpTestsSimpleConnection * tp_tests_simple_connection_new (const gchar *account, const gchar *protocol) { return TP_TESTS_SIMPLE_CONNECTION (g_object_new ( TP_TESTS_TYPE_SIMPLE_CONNECTION, "account", account, "protocol", protocol, NULL)); } gchar * tp_tests_simple_connection_ensure_text_chan (TpTestsSimpleConnection *self, const gchar *target_id, GHashTable **props) { TpTestsTextChannelNull *chan; gchar *chan_path; TpHandleRepoIface *contact_repo; TpHandle handle; static guint count = 0; TpBaseConnection *base_conn = (TpBaseConnection *) self; /* Get contact handle */ contact_repo = tp_base_connection_get_handles (base_conn, TP_HANDLE_TYPE_CONTACT); g_assert (contact_repo != NULL); handle = tp_handle_ensure (contact_repo, target_id, NULL, NULL); chan = g_hash_table_lookup (self->priv->channels, GUINT_TO_POINTER (handle)); if (chan != NULL) { /* Channel already exist, reuse it */ g_object_get (chan, "object-path", &chan_path, NULL); } else { chan_path = g_strdup_printf ("%s/Channel%u", base_conn->object_path, count++); chan = TP_TESTS_TEXT_CHANNEL_NULL ( tp_tests_object_new_static_class ( TP_TESTS_TYPE_TEXT_CHANNEL_NULL, "connection", self, "object-path", chan_path, "handle", handle, NULL)); g_hash_table_insert (self->priv->channels, GUINT_TO_POINTER (handle), chan); } tp_handle_unref (contact_repo, handle); if (props != NULL) *props = tp_tests_text_channel_get_props (chan); return chan_path; } contactsd-1.4.14/tests/libtelepathy/simple-conn.h000066400000000000000000000045721440357512200220230ustar00rootroot00000000000000/* * simple-conn.h - header for a simple connection * * Copyright (C) 2007-2008 Collabora Ltd. * Copyright (C) 2007-2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TP_TESTS_SIMPLE_CONN_H__ #define __TP_TESTS_SIMPLE_CONN_H__ #include #include G_BEGIN_DECLS typedef struct _TpTestsSimpleConnection TpTestsSimpleConnection; typedef struct _TpTestsSimpleConnectionClass TpTestsSimpleConnectionClass; typedef struct _TpTestsSimpleConnectionPrivate TpTestsSimpleConnectionPrivate; struct _TpTestsSimpleConnectionClass { TpBaseConnectionClass parent_class; }; struct _TpTestsSimpleConnection { TpBaseConnection parent; TpTestsSimpleConnectionPrivate *priv; }; GType tp_tests_simple_connection_get_type (void); /* TYPE MACROS */ #define TP_TESTS_TYPE_SIMPLE_CONNECTION \ (tp_tests_simple_connection_get_type ()) #define TP_TESTS_SIMPLE_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj), TP_TESTS_TYPE_SIMPLE_CONNECTION, \ TpTestsSimpleConnection)) #define TP_TESTS_SIMPLE_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TP_TESTS_TYPE_SIMPLE_CONNECTION, \ TpTestsSimpleConnectionClass)) #define TP_TESTS_SIMPLE_IS_CONNECTION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TP_TESTS_TYPE_SIMPLE_CONNECTION)) #define TP_TESTS_SIMPLE_IS_CONNECTION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE((klass), TP_TESTS_TYPE_SIMPLE_CONNECTION)) #define TP_TESTS_SIMPLE_CONNECTION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_SIMPLE_CONNECTION, \ TpTestsSimpleConnectionClass)) TpTestsSimpleConnection * tp_tests_simple_connection_new (const gchar *account, const gchar *protocol); /* Cause "network events", for debugging/testing */ void tp_tests_simple_connection_inject_disconnect ( TpTestsSimpleConnection *self); void tp_tests_simple_connection_set_identifier (TpTestsSimpleConnection *self, const gchar *identifier); gchar * tp_tests_simple_connection_ensure_text_chan ( TpTestsSimpleConnection *self, const gchar *target_id, GHashTable **props); G_END_DECLS #endif /* #ifndef __TP_TESTS_SIMPLE_CONN_H__ */ contactsd-1.4.14/tests/libtelepathy/textchan-null.c000066400000000000000000000411521440357512200223530ustar00rootroot00000000000000/* * /dev/null as a text channel * * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "textchan-null.h" #include #include #include #include #include #include #include static void text_iface_init (gpointer iface, gpointer data); static void channel_iface_init (gpointer iface, gpointer data); G_DEFINE_TYPE_WITH_CODE (TpTestsTextChannelNull, tp_tests_text_channel_null, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)) G_DEFINE_TYPE_WITH_CODE (TpTestsPropsTextChannel, tp_tests_props_text_channel, TP_TESTS_TYPE_TEXT_CHANNEL_NULL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, tp_dbus_properties_mixin_iface_init)) G_DEFINE_TYPE_WITH_CODE (TpTestsPropsGroupTextChannel, tp_tests_props_group_text_channel, TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, tp_group_mixin_iface_init)) static const char *tp_tests_text_channel_null_interfaces[] = { NULL }; /* type definition stuff */ enum { PROP_OBJECT_PATH = 1, PROP_CHANNEL_TYPE, PROP_HANDLE_TYPE, PROP_HANDLE, PROP_TARGET_ID, PROP_CONNECTION, PROP_INTERFACES, PROP_REQUESTED, PROP_INITIATOR_HANDLE, PROP_INITIATOR_ID, N_PROPS }; struct _TpTestsTextChannelNullPrivate { TpBaseConnection *conn; gchar *object_path; TpHandle handle; unsigned closed:1; unsigned disposed:1; }; static void tp_tests_text_channel_null_init (TpTestsTextChannelNull *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, TP_TESTS_TYPE_TEXT_CHANNEL_NULL, TpTestsTextChannelNullPrivate); } static void tp_tests_props_text_channel_init (TpTestsPropsTextChannel *self) { self->dbus_property_interfaces_retrieved = g_hash_table_new (NULL, NULL); } static GObject * constructor (GType type, guint n_props, GObjectConstructParam *props) { GObject *object = G_OBJECT_CLASS (tp_tests_text_channel_null_parent_class)->constructor (type, n_props, props); TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (self->priv->conn, TP_HANDLE_TYPE_CONTACT); tp_handle_ref (contact_repo, self->priv->handle); tp_dbus_daemon_register_object ( tp_base_connection_get_dbus_daemon (self->priv->conn), self->priv->object_path, self); tp_text_mixin_init (object, G_STRUCT_OFFSET (TpTestsTextChannelNull, text), contact_repo); tp_text_mixin_set_message_types (object, TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION, TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, G_MAXUINT); return object; } static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); switch (property_id) { case PROP_OBJECT_PATH: g_value_set_string (value, self->priv->object_path); break; case PROP_CHANNEL_TYPE: g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT); break; case PROP_HANDLE_TYPE: g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); break; case PROP_HANDLE: g_value_set_uint (value, self->priv->handle); break; case PROP_TARGET_ID: { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( self->priv->conn, TP_HANDLE_TYPE_CONTACT); g_value_set_string (value, tp_handle_inspect (contact_repo, self->priv->handle)); } break; case PROP_REQUESTED: g_value_set_boolean (value, TRUE); break; case PROP_INITIATOR_HANDLE: g_value_set_uint (value, self->priv->conn->self_handle); break; case PROP_INITIATOR_ID: { TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( self->priv->conn, TP_HANDLE_TYPE_CONTACT); g_value_set_string (value, tp_handle_inspect (contact_repo, self->priv->conn->self_handle)); } break; case PROP_INTERFACES: g_value_set_boxed (value, tp_tests_text_channel_null_interfaces); break; case PROP_CONNECTION: g_value_set_object (value, self->priv->conn); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); switch (property_id) { case PROP_OBJECT_PATH: g_free (self->priv->object_path); self->priv->object_path = g_value_dup_string (value); break; case PROP_HANDLE: /* we don't ref it here because we don't necessarily have access to the * contact repo yet - instead we ref it in the constructor. */ self->priv->handle = g_value_get_uint (value); break; case PROP_HANDLE_TYPE: case PROP_CHANNEL_TYPE: /* these properties are writable in the interface, but not actually * meaningfully changable on this channel, so we do nothing */ break; case PROP_CONNECTION: self->priv->conn = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } void tp_tests_text_channel_null_close (TpTestsTextChannelNull *self) { if (!self->priv->closed) { self->priv->closed = TRUE; tp_svc_channel_emit_closed (self); tp_dbus_daemon_unregister_object ( tp_base_connection_get_dbus_daemon (self->priv->conn), self); } } static void dispose (GObject *object) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); if (self->priv->disposed) return; self->priv->disposed = TRUE; tp_tests_text_channel_null_close (self); ((GObjectClass *) tp_tests_text_channel_null_parent_class)->dispose (object); } static void finalize (GObject *object) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (object); TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (self->priv->conn, TP_HANDLE_TYPE_CONTACT); tp_handle_unref (contact_handles, self->priv->handle); g_free (self->priv->object_path); tp_text_mixin_finalize (object); ((GObjectClass *) tp_tests_text_channel_null_parent_class)->finalize (object); } static void tp_tests_text_channel_null_class_init (TpTestsTextChannelNullClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; GParamSpec *param_spec; g_type_class_add_private (klass, sizeof (TpTestsTextChannelNullPrivate)); object_class->constructor = constructor; object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; object_class->finalize = finalize; g_object_class_override_property (object_class, PROP_OBJECT_PATH, "object-path"); g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, "channel-type"); g_object_class_override_property (object_class, PROP_HANDLE_TYPE, "handle-type"); g_object_class_override_property (object_class, PROP_HANDLE, "handle"); param_spec = g_param_spec_object ("connection", "TpBaseConnection object", "Connection object that owns this channel", TP_TYPE_BASE_CONNECTION, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); param_spec = g_param_spec_boxed ("interfaces", "Extra D-Bus interfaces", "Additional Channel.Interface.* interfaces", G_TYPE_STRV, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INTERFACES, param_spec); param_spec = g_param_spec_string ("target-id", "Peer's ID", "The string obtained by inspecting the target handle", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_TARGET_ID, param_spec); param_spec = g_param_spec_uint ("initiator-handle", "Initiator's handle", "The contact who initiated the channel", 0, G_MAXUINT32, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIATOR_HANDLE, param_spec); param_spec = g_param_spec_string ("initiator-id", "Initiator's ID", "The string obtained by inspecting the initiator-handle", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_INITIATOR_ID, param_spec); param_spec = g_param_spec_boolean ("requested", "Requested?", "True if this channel was requested by the local user", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, PROP_REQUESTED, param_spec); tp_text_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsTextChannelNullClass, text_class)); } static void tp_tests_props_text_channel_getter_gobject_properties (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object); g_hash_table_insert (self->dbus_property_interfaces_retrieved, GUINT_TO_POINTER (interface), GUINT_TO_POINTER (interface)); tp_dbus_properties_mixin_getter_gobject_properties (object, interface, name, value, getter_data); } static void props_finalize (GObject *object) { TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object); g_hash_table_unref (self->dbus_property_interfaces_retrieved); ((GObjectClass *) tp_tests_props_text_channel_parent_class)->finalize (object); } static void tp_tests_props_text_channel_class_init (TpTestsPropsTextChannelClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; static TpDBusPropertiesMixinPropImpl channel_props[] = { { "TargetHandleType", "handle-type", NULL }, { "TargetHandle", "handle", NULL }, { "ChannelType", "channel-type", NULL }, { "Interfaces", "interfaces", NULL }, { "TargetID", "target-id", NULL }, { "Requested", "requested", NULL }, { "InitiatorHandle", "initiator-handle", NULL }, { "InitiatorID", "initiator-id", NULL }, { NULL } }; static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { { TP_IFACE_CHANNEL, tp_tests_props_text_channel_getter_gobject_properties, NULL, channel_props, }, { NULL } }; object_class->finalize = props_finalize; klass->dbus_properties_class.interfaces = prop_interfaces; tp_dbus_properties_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsPropsTextChannelClass, dbus_properties_class)); } static void tp_tests_props_group_text_channel_init (TpTestsPropsGroupTextChannel *self) { } static void group_constructed (GObject *self) { TpBaseConnection *conn = TP_TESTS_TEXT_CHANNEL_NULL (self)->priv->conn; void (*chain_up) (GObject *) = ((GObjectClass *) tp_tests_props_group_text_channel_parent_class)->constructed; if (chain_up != NULL) chain_up (self); tp_group_mixin_init (self, G_STRUCT_OFFSET (TpTestsPropsGroupTextChannel, group), tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT), tp_base_connection_get_self_handle (conn)); tp_group_mixin_change_flags (self, TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); } static void group_finalize (GObject *self) { tp_group_mixin_finalize (self); ((GObjectClass *) tp_tests_props_group_text_channel_parent_class)->finalize (self); } static gboolean dummy_add_remove_member (GObject *obj, TpHandle handle, const gchar *message, GError **error) { return TRUE; } static void group_iface_props_getter (GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { TpTestsPropsTextChannel *self = TP_TESTS_PROPS_TEXT_CHANNEL (object); g_hash_table_insert (self->dbus_property_interfaces_retrieved, GUINT_TO_POINTER (interface), GUINT_TO_POINTER (interface)); tp_group_mixin_get_dbus_property (object, interface, name, value, getter_data); } static void tp_tests_props_group_text_channel_class_init (TpTestsPropsGroupTextChannelClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; static TpDBusPropertiesMixinPropImpl group_props[] = { { "GroupFlags", NULL, NULL }, { "HandleOwners", NULL, NULL }, { "LocalPendingMembers", NULL, NULL }, { "Members", NULL, NULL }, { "RemotePendingMembers", NULL, NULL }, { "SelfHandle", NULL, NULL }, { NULL } }; object_class->constructed = group_constructed; object_class->finalize = group_finalize; tp_group_mixin_class_init (object_class, G_STRUCT_OFFSET (TpTestsPropsGroupTextChannelClass, group_class), dummy_add_remove_member, dummy_add_remove_member); tp_dbus_properties_mixin_implement_interface (object_class, TP_IFACE_QUARK_CHANNEL_INTERFACE_GROUP, group_iface_props_getter, NULL, group_props); } static void channel_close (TpSvcChannel *iface, DBusGMethodInvocation *context) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); tp_tests_text_channel_null_close (self); tp_svc_channel_return_from_close (context); } static void channel_get_channel_type (TpSvcChannel *iface, DBusGMethodInvocation *context) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); self->get_channel_type_called++; tp_svc_channel_return_from_get_channel_type (context, TP_IFACE_CHANNEL_TYPE_TEXT); } static void channel_get_handle (TpSvcChannel *iface, DBusGMethodInvocation *context) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); self->get_handle_called++; tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT, self->priv->handle); } static void channel_get_interfaces (TpSvcChannel *iface, DBusGMethodInvocation *context) { TpTestsTextChannelNull *self = TP_TESTS_TEXT_CHANNEL_NULL (iface); self->get_interfaces_called++; tp_svc_channel_return_from_get_interfaces (context, tp_tests_text_channel_null_interfaces); } static void channel_iface_init (gpointer iface, gpointer data) { TpSvcChannelClass *klass = iface; #define IMPLEMENT(x) tp_svc_channel_implement_##x (klass, channel_##x) IMPLEMENT (close); IMPLEMENT (get_channel_type); IMPLEMENT (get_handle); IMPLEMENT (get_interfaces); #undef IMPLEMENT } static void text_send (TpSvcChannelTypeText *iface, guint type, const gchar *text, DBusGMethodInvocation *context) { /* silently swallow the message */ tp_svc_channel_type_text_return_from_send (context); } static void text_iface_init (gpointer iface, gpointer data) { TpSvcChannelTypeTextClass *klass = iface; tp_text_mixin_iface_init (iface, data); #define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (klass, text_##x) IMPLEMENT (send); #undef IMPLEMENT } GHashTable * tp_tests_text_channel_get_props (TpTestsTextChannelNull *self) { GHashTable *props; TpHandleType handle_type; TpHandle handle; gchar *target_id; gboolean requested; TpHandle initiator_handle; gchar *initiator_id; g_object_get (self, "handle-type", &handle_type, "handle", &handle, "target-id", &target_id, "requested", &requested, "initiator-handle", &initiator_handle, "initiator-id", &initiator_id, NULL); props = tp_asv_new ( TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, TP_PROP_CHANNEL_TARGET_HANDLE, G_TYPE_UINT, handle, TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, target_id, TP_PROP_CHANNEL_REQUESTED, G_TYPE_BOOLEAN, requested, TP_PROP_CHANNEL_INITIATOR_HANDLE, G_TYPE_UINT, initiator_handle, TP_PROP_CHANNEL_INITIATOR_ID, G_TYPE_STRING, initiator_id, NULL); g_free (target_id); g_free (initiator_id); return props; } contactsd-1.4.14/tests/libtelepathy/textchan-null.h000066400000000000000000000117031440357512200223570ustar00rootroot00000000000000/* * /dev/null as a text channel * * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TP_TESTS_TEXT_CHANNEL_NULL_H__ #define __TP_TESTS_TEXT_CHANNEL_NULL_H__ #include #include #include #include G_BEGIN_DECLS typedef struct _TpTestsTextChannelNull TpTestsTextChannelNull; typedef struct _TpTestsTextChannelNullClass TpTestsTextChannelNullClass; typedef struct _TpTestsTextChannelNullPrivate TpTestsTextChannelNullPrivate; GType tp_tests_text_channel_null_get_type (void); #define TP_TESTS_TYPE_TEXT_CHANNEL_NULL \ (tp_tests_text_channel_null_get_type ()) #define TP_TESTS_TEXT_CHANNEL_NULL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_NULL, \ TpTestsTextChannelNull)) #define TP_TESTS_TEXT_CHANNEL_NULL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_TEXT_CHANNEL_NULL, \ TpTestsTextChannelNullClass)) #define TP_TESTS_IS_TEXT_CHANNEL_NULL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_NULL)) #define TP_TESTS_IS_TEXT_CHANNEL_NULL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_TEXT_CHANNEL_NULL)) #define TP_TESTS_TEXT_CHANNEL_NULL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_TEXT_CHANNEL_NULL, \ TpTestsTextChannelNullClass)) struct _TpTestsTextChannelNullClass { GObjectClass parent_class; TpTextMixinClass text_class; }; struct _TpTestsTextChannelNull { GObject parent; TpTextMixin text; guint get_handle_called; guint get_interfaces_called; guint get_channel_type_called; TpTestsTextChannelNullPrivate *priv; }; /* Subclass with D-Bus properties */ typedef struct _TestPropsTextChannel TpTestsPropsTextChannel; typedef struct _TestPropsTextChannelClass TpTestsPropsTextChannelClass; struct _TestPropsTextChannel { TpTestsTextChannelNull parent; GHashTable *dbus_property_interfaces_retrieved; }; struct _TestPropsTextChannelClass { TpTestsTextChannelNullClass parent; TpDBusPropertiesMixinClass dbus_properties_class; }; GType tp_tests_props_text_channel_get_type (void); #define TP_TESTS_TYPE_PROPS_TEXT_CHANNEL \ (tp_tests_props_text_channel_get_type ()) #define TP_TESTS_PROPS_TEXT_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, \ TpTestsPropsTextChannel)) #define TP_TESTS_PROPS_TEXT_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, \ TpTestsPropsTextChannelClass)) #define TP_TESTS_IS_PROPS_TEXT_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL)) #define TP_TESTS_IS_PROPS_TEXT_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL)) #define TP_TESTS_PROPS_TEXT_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_PROPS_TEXT_CHANNEL, \ TpTestsPropsTextChannelClass)) /* Subclass with D-Bus properties and Group */ typedef struct _TestPropsGroupTextChannel TpTestsPropsGroupTextChannel; typedef struct _TestPropsGroupTextChannelClass TpTestsPropsGroupTextChannelClass; struct _TestPropsGroupTextChannel { TpTestsPropsTextChannel parent; TpGroupMixin group; }; struct _TestPropsGroupTextChannelClass { TpTestsPropsTextChannelClass parent; TpGroupMixinClass group_class; }; GType tp_tests_props_group_text_channel_get_type (void); #define TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL \ (tp_tests_props_group_text_channel_get_type ()) #define TP_TESTS_PROPS_GROUP_TEXT_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL, \ TpTestsPropsGroupTextChannel)) #define TP_TESTS_PROPS_GROUP_TEXT_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL, \ TpTestsPropsGroupTextChannelClass)) #define TP_TESTS_IS_PROPS_GROUP_TEXT_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL)) #define TP_TESTS_IS_PROPS_GROUP_TEXT_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL)) #define TP_TESTS_PROPS_GROUP_TEXT_CHANNEL_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), TP_TESTS_TYPE_PROPS_GROUP_TEXT_CHANNEL, \ TpTestsPropsGroupTextChannelClass)) void tp_tests_text_channel_null_close (TpTestsTextChannelNull *self); GHashTable * tp_tests_text_channel_get_props (TpTestsTextChannelNull *self); G_END_DECLS #endif /* #ifndef __TP_TESTS_TEXT_CHANNEL_NULL_H__ */ contactsd-1.4.14/tests/libtelepathy/util.c000066400000000000000000000150401440357512200205370ustar00rootroot00000000000000/* Simple utility code used by the regression tests. * * Copyright © 2008-2010 Collabora Ltd. * Copyright © 2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #include "util.h" void tp_tests_proxy_run_until_prepared (gpointer proxy, const GQuark *features) { GError *error = NULL; tp_tests_proxy_run_until_prepared_or_failed (proxy, features, &error); g_assert_no_error (error); } /* A GAsyncReadyCallback whose user_data is a GAsyncResult **. It writes a * reference to the result into that pointer. */ void tp_tests_result_ready_cb (GObject *object, GAsyncResult *res, gpointer user_data) { GAsyncResult **result = user_data; *result = g_object_ref (res); } /* Run until *result contains a result. Intended to be used with a pending * async call that uses tp_tests_result_ready_cb. */ void tp_tests_run_until_result (GAsyncResult **result) { /* not synchronous */ g_assert (*result == NULL); while (*result == NULL) g_main_context_iteration (NULL, TRUE); } gboolean tp_tests_proxy_run_until_prepared_or_failed (gpointer proxy, const GQuark *features, GError **error) { GAsyncResult *result = NULL; gboolean r; tp_proxy_prepare_async (proxy, features, tp_tests_result_ready_cb, &result); tp_tests_run_until_result (&result); r = tp_proxy_prepare_finish (proxy, result, error); g_object_unref (result); return r; } TpDBusDaemon * tp_tests_dbus_daemon_dup_or_die (void) { TpDBusDaemon *d = tp_dbus_daemon_dup (NULL); /* In a shared library, this would be very bad (see fd.o #18832), but in a * regression test that's going to be run under a temporary session bus, * it's just what we want. */ if (d == NULL) { g_error ("Unable to connect to session bus"); } return d; } static void introspect_cb (TpProxy *proxy G_GNUC_UNUSED, const gchar *xml G_GNUC_UNUSED, const GError *error G_GNUC_UNUSED, gpointer user_data, GObject *weak_object G_GNUC_UNUSED) { g_main_loop_quit (user_data); } void tp_tests_proxy_run_until_dbus_queue_processed (gpointer proxy) { GMainLoop *loop = g_main_loop_new (NULL, FALSE); tp_cli_dbus_introspectable_call_introspect (proxy, -1, introspect_cb, loop, NULL, NULL); g_main_loop_run (loop); g_main_loop_unref (loop); } typedef struct { GMainLoop *loop; TpHandle handle; } HandleRequestResult; static void handles_requested_cb (TpConnection *connection G_GNUC_UNUSED, TpHandleType handle_type G_GNUC_UNUSED, guint n_handles, const TpHandle *handles, const gchar * const *ids G_GNUC_UNUSED, const GError *error, gpointer user_data, GObject *weak_object G_GNUC_UNUSED) { HandleRequestResult *result = user_data; g_assert_no_error ((GError *) error); g_assert_cmpuint (n_handles, ==, 1); result->handle = handles[0]; } static void handle_request_result_finish (gpointer r) { HandleRequestResult *result = r; g_main_loop_quit (result->loop); } TpHandle tp_tests_connection_run_request_contact_handle (TpConnection *connection, const gchar *id) { HandleRequestResult result = { g_main_loop_new (NULL, FALSE), 0 }; const gchar * const ids[] = { id, NULL }; tp_connection_request_handles (connection, -1, TP_HANDLE_TYPE_CONTACT, ids, handles_requested_cb, &result, handle_request_result_finish, NULL); g_main_loop_run (result.loop); g_main_loop_unref (result.loop); return result.handle; } void _test_assert_empty_strv (const char *file, int line, gconstpointer strv) { const gchar * const *strings = strv; if (strv != NULL && strings[0] != NULL) { guint i; g_message ("%s:%d: expected empty strv, but got:", file, line); for (i = 0; strings[i] != NULL; i++) { g_message ("* \"%s\"", strings[i]); } g_error ("%s:%d: strv wasn't empty (see above for contents", file, line); } } void _tp_tests_assert_strv_equals (const char *file, int line, const char *expected_desc, gconstpointer expected_strv, const char *actual_desc, gconstpointer actual_strv) { const gchar * const *expected = expected_strv; const gchar * const *actual = actual_strv; guint i; g_assert (expected != NULL); g_assert (actual != NULL); for (i = 0; expected[i] != NULL || actual[i] != NULL; i++) { if (expected[i] == NULL) { g_error ("%s:%d: assertion failed: (%s)[%u] == (%s)[%u]: " "NULL == %s", file, line, expected_desc, i, actual_desc, i, actual[i]); } else if (actual[i] == NULL) { g_error ("%s:%d: assertion failed: (%s)[%u] == (%s)[%u]: " "%s == NULL", file, line, expected_desc, i, actual_desc, i, expected[i]); } else if (tp_strdiff (expected[i], actual[i])) { g_error ("%s:%d: assertion failed: (%s)[%u] == (%s)[%u]: " "%s == %s", file, line, expected_desc, i, actual_desc, i, expected[i], actual[i]); } } } void tp_tests_create_and_connect_conn (GType conn_type, const gchar *account, TpBaseConnection **service_conn, TpConnection **client_conn) { TpDBusDaemon *dbus; gchar *name; gchar *conn_path; GError *error = NULL; GQuark conn_features[] = { TP_CONNECTION_FEATURE_CONNECTED, 0 }; g_assert (service_conn != NULL); g_assert (client_conn != NULL); dbus = tp_tests_dbus_daemon_dup_or_die (); *service_conn = tp_tests_object_new_static_class ( conn_type, "account", account, "protocol", "simple", NULL); g_assert (*service_conn != NULL); g_assert (tp_base_connection_register (*service_conn, "simple", &name, &conn_path, &error)); g_assert_no_error (error); *client_conn = tp_connection_new (dbus, name, conn_path, &error); g_assert (*client_conn != NULL); g_assert_no_error (error); tp_cli_connection_call_connect (*client_conn, -1, NULL, NULL, NULL, NULL); tp_tests_proxy_run_until_prepared (*client_conn, conn_features); g_free (name); g_free (conn_path); g_object_unref (dbus); } /* This object exists solely so that tests/tests.supp can ignore "leaked" * classes. */ gpointer tp_tests_object_new_static_class (GType type, ...) { va_list ap; GObject *object; const gchar *first_property; va_start (ap, type); first_property = va_arg (ap, const gchar *); object = g_object_new_valist (type, first_property, ap); va_end (ap); return object; } contactsd-1.4.14/tests/libtelepathy/util.h000066400000000000000000000036061440357512200205510ustar00rootroot00000000000000/* Simple utility code used by the regression tests. * * Copyright © 2008-2010 Collabora Ltd. * Copyright © 2008 Nokia Corporation * * Copying and distribution of this file, with or without modification, * are permitted in any medium without royalty provided the copyright * notice and this notice are preserved. */ #ifndef __TP_TESTS_LIB_UTIL_H__ #define __TP_TESTS_LIB_UTIL_H__ #include #include G_BEGIN_DECLS TpDBusDaemon *tp_tests_dbus_daemon_dup_or_die (void); void tp_tests_proxy_run_until_dbus_queue_processed (gpointer proxy); TpHandle tp_tests_connection_run_request_contact_handle ( TpConnection *connection, const gchar *id); void tp_tests_proxy_run_until_prepared (gpointer proxy, const GQuark *features); gboolean tp_tests_proxy_run_until_prepared_or_failed (gpointer proxy, const GQuark *features, GError **error); #define test_assert_empty_strv(strv) \ _test_assert_empty_strv (__FILE__, __LINE__, strv) void _test_assert_empty_strv (const char *file, int line, gconstpointer strv); #define tp_tests_assert_strv_equals(actual, expected) \ _tp_tests_assert_strv_equals (__FILE__, __LINE__, \ #actual, actual, \ #expected, expected) void _tp_tests_assert_strv_equals (const char *file, int line, const char *actual_desc, gconstpointer actual_strv, const char *expected_desc, gconstpointer expected_strv); void tp_tests_create_and_connect_conn (GType conn_type, const gchar *account, TpBaseConnection **service_conn, TpConnection **client_conn); gpointer tp_tests_object_new_static_class (GType type, ...) G_GNUC_NULL_TERMINATED; void tp_tests_run_until_result (GAsyncResult **result); void tp_tests_result_ready_cb (GObject *object, GAsyncResult *res, gpointer user_data); G_END_DECLS #endif /* #ifndef __TP_TESTS_LIB_UTIL_H__ */ contactsd-1.4.14/tests/mktests.sh.in000077500000000000000000000047031440357512200173720ustar00rootroot00000000000000#!/bin/sh -e # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. package=contactsd-tests srcdir=`dirname "$0"` srcdir=`cd "$srcdir" && pwd` cat < Unit and regression tests for Contacts Daemon EOF for suite in "$@" do test -f "$suite/$suite.skip" && continue if [ -f $suite/$suite-wrapper.sh ]; then command="/opt/tests/contactsd/$suite/$suite-wrapper.sh" else command="/opt/tests/contactsd/$suite/$suite" fi cat < while tracker-control -p |grep -q '^Found process ID '; do tracker-control -t; sleep 1; done EOF "$suite/$suite" -functions | sed -ne 's/()$//p' | while read test do attributes="name=\"$suite-$test\"" description=`grep "^$suite::$test\\>" $srcdir/../EXPECTFAIL || true` if test -n "$description" then attributes="$attributes insignificant=\"true\"" else description="$suite::$test(): no description available" fi cat < $description /usr/sbin/run-blts-root /bin/su -g privileged -c 'CONTACTSD_DEBUG=1 $command $test' nemo EOF done cat < true true EOF done cat < EOF contactsd-1.4.14/tests/session.conf000066400000000000000000000020541440357512200172630ustar00rootroot00000000000000 session unix:tmpdir=/tmp contactsd-1.4.14/tests/tests.pro000066400000000000000000000035651440357512200166250ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. PACKAGENAME = contactsd TEMPLATE = subdirs SUBDIRS += libtelepathy ut_birthdayplugin ut_telepathyplugin ut_simplugin ut_telepathyplugin.depends = libtelepathy UNIT_TESTS += ut_birthdayplugin ut_telepathyplugin ut_simplugin testxml.target = tests.xml testxml.commands = sh $$PWD/mktests.sh $$UNIT_TESTS >$@ || rm -f $@ testxml.depends = $$UNIT_TESTS install_testxml.files = $$testxml.target install_testxml.path = /opt/tests/$${PACKAGENAME}/test-definition/ install_testxml.depends = $$testxml.target install_testxml.CONFIG = no_check_exist install_extrascripts.files = with-session-bus.sh session.conf install_extrascripts.path = /opt/tests/$${PACKAGENAME}/ install_extrascripts.depends = $$UNIT_TESTS install_extrascripts.CONFIG = no_check_exist INSTALLS += install_testxml install_extrascripts QMAKE_EXTRA_TARGETS += testxml QMAKE_DISTCLEAN += $$testxml.target POST_TARGETDEPS += $$testxml.target contactsd-1.4.14/tests/ut_birthdayplugin/000077500000000000000000000000001440357512200204655ustar00rootroot00000000000000contactsd-1.4.14/tests/ut_birthdayplugin/check.pri000066400000000000000000000035111440357512200222560ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. check_daemon.target = check-with-daemon.sh check_daemon.depends = $$PWD/with-daemon.sh.in check_daemon.commands = \ sed -e "s,@BINDIR@,$$TOP_BUILDDIR/src,g" \ -e "s,@PLUGINDIR@,$$TOP_BUILDDIR/plugins/birthday,g" \ $< > $@ && chmod +x $@ || rm -f $@ check_wrapper.target = check-ut_birthdayplugin-wrapper.sh check_wrapper.depends = $$PWD/ut_birthdayplugin-wrapper.sh.in check_wrapper.commands = \ sed -e "s,@SCRIPTDIR@,$$PWD/..,g" \ -e "s,@OUT_SCRIPTDIR@,$$OUT_PWD$$DESTDIR/..,g" \ -e "s,@BINDIR@,$$OUT_PWD$$DESTDIR,g" \ -e "s,@WITH_DAEMON@,$$check_daemon.target,g" \ $< > $@ && chmod +x $@ || rm -f $@ check.depends = $$TARGET check_wrapper check_daemon check.commands = sh $$check_wrapper.target QMAKE_EXTRA_TARGETS += check_wrapper check_daemon check QMAKE_CLEAN += $$check_daemon.target $$check_wrapper.target contactsd-1.4.14/tests/ut_birthdayplugin/test-birthday-plugin.cpp000066400000000000000000000325651440357512200252630ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "test-birthday-plugin.h" #include #include #include #include #include #include #include #include using namespace ML10N; // A random ID, from plugins/birthday/cdbirthdaycalendar.cpp. const QLatin1String calNotebookId("b1376da7-5555-1111-2222-227549c4e570"); static const int calendarTimeout = 12000; // ms static void loopWait(int ms) { QTimer timer; timer.setInterval(ms); qDebug() << "Waiting for" << (ms/1000) << "seconds"; QEventLoop loop; QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); timer.start(); loop.exec(); } TestBirthdayPlugin::TestBirthdayPlugin(QObject *parent) : QObject(parent), mManager(0) { } void TestBirthdayPlugin::init() { QMap parameters; parameters.insert(QStringLiteral("mergePresenceChanges"), QStringLiteral("false")); mManager = new QContactManager(QStringLiteral("org.nemomobile.contacts.sqlite"), parameters); } void TestBirthdayPlugin::initTestCase() { } void TestBirthdayPlugin::testAddAndRemoveBirthday() { const QString contactID = QUuid::createUuid().toString(); const QDateTime contactBirthDate = QDateTime::currentDateTime(); // Add contact with birthday to tracker. QContactName contactName; contactName.setFirstName(contactID); QContactBirthday contactBirthday; contactBirthday.setDateTime(contactBirthDate); QContact contact; QVERIFY(contact.saveDetail(&contactName)); QVERIFY(contact.saveDetail(&contactBirthday)); QVERIFY2(saveContact(contact), "Error saving contact to tracker"); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Open calendar database, which should have been created by the birthday plugin. mKCal::ExtendedCalendar::Ptr calendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mKCal::ExtendedStorage::Ptr storage = mKCal::ExtendedCalendar::defaultStorage(calendar); storage->open(); QVERIFY2(not storage->notebook(calNotebookId).isNull(), "No calendar database found"); // Check calendar database for contact. QVERIFY2(storage->loadNotebookIncidences(calNotebookId), "Unable to load events from notebook"); KCalendarCore::Event::List eventList = calendar->events(); QCOMPARE(countCalendarEvents(eventList, contact), 1); // Delete the contact and see if the birthday is also deleted. QVERIFY2(mManager->removeContact(contact.id()), "Unable to delete test contact from tracker database"); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Search for any events in the calendar. QVERIFY2(storage->loadNotebookIncidences(calNotebookId), "Unable to load events from notebook"); eventList = calendar->events(); QCOMPARE(countCalendarEvents(eventList, contact), 0); // Close the calendar. QVERIFY2(storage->close(), "Error closing the calendar"); } void TestBirthdayPlugin::testChangeBirthday() { const QString contactID = QUuid::createUuid().toString(); const QDateTime contactBirthDate = QDateTime::currentDateTime(); // Add contact with birthday to tracker. QContactName contactName; contactName.setFirstName(contactID); QContactBirthday contactBirthday; contactBirthday.setDateTime(contactBirthDate); QContact contact; QVERIFY(contact.saveDetail(&contactName)); QVERIFY(contact.saveDetail(&contactBirthday)); QVERIFY2(saveContact(contact), "Error saving contact to tracker"); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Open calendar database. mKCal::ExtendedCalendar::Ptr calendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mKCal::ExtendedStorage::Ptr storage = mKCal::ExtendedCalendar::defaultStorage(calendar); storage->open(); QVERIFY2(not storage->notebook(calNotebookId).isNull(), "No calendar database found"); // Check calendar database for contact. QVERIFY2(storage->loadNotebookIncidences(calNotebookId), "Unable to load events from notebook"); KCalendarCore::Event::List eventList = calendar->events(); QCOMPARE(countCalendarEvents(eventList, contact), 1); // Change the contact and see if the birthday is updated. contactBirthday.setDateTime(contactBirthDate.addDays(-3)); QVERIFY(contact.saveDetail(&contactBirthday)); QVERIFY2(saveContact(contact), "Unable to update test contact in tracker"); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Search for any events in the calendar. QVERIFY2(storage->loadNotebookIncidences(calNotebookId), "Unable to load events from notebook"); eventList = calendar->events(); QCOMPARE(countCalendarEvents(eventList, contact), 1); // Close the calendar. QVERIFY2(storage->close(), "Error closing the calendar"); } void TestBirthdayPlugin::testChangeName() { const QString contactID = QUuid::createUuid().toString(); const QDateTime contactBirthDate = QDateTime::currentDateTime(); // Add contact with birthday to tracker. QContactName contactName; contactName.setFirstName(contactID); QContactBirthday contactBirthday; contactBirthday.setDateTime(contactBirthDate); QContact contact; QVERIFY(contact.saveDetail(&contactName)); QVERIFY(contact.saveDetail(&contactBirthday)); QVERIFY2(saveContact(contact), "Error saving contact to tracker"); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Open calendar database. mKCal::ExtendedCalendar::Ptr calendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mKCal::ExtendedStorage::Ptr storage = mKCal::ExtendedCalendar::defaultStorage(calendar); storage->open(); QVERIFY2(not storage->notebook(calNotebookId).isNull(), "No calendar database found"); // Check calendar database for contact. QVERIFY2(storage->loadNotebookIncidences(calNotebookId), "Unable to load events from notebook"); KCalendarCore::Event::List eventList = calendar->events(); QCOMPARE(countCalendarEvents(eventList, contact), 1); // Change the contact name and see if the calendar is updated. const QString newContactID = QUuid::createUuid().toString(); contactName.setFirstName(newContactID); QVERIFY(contact.saveDetail(&contactName)); // TODO: Should it be necessary to refetch the contact to get the synthesised displayLabel? contact = mManager->contact(contact.id()); QVERIFY2(saveContact(contact), "Unable to update test contact in tracker"); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Search for any events in the calendar. QVERIFY2(storage->loadNotebookIncidences(calNotebookId), "Unable to load events from notebook"); eventList = calendar->events(); QCOMPARE(countCalendarEvents(eventList, contact), 1); // Close the calendar. QVERIFY2(storage->close(), "Error closing the calendar"); } void TestBirthdayPlugin::testLocaleChange() { // Is this the infrastructure for this tets still available? QSKIP("MeegoTouch language setting change is not reflected in KCalendarCore..."); MGConfItem store(QLatin1String("/meegotouch/i18n/language")); store.set(QLatin1String("en")); // Leave the time to react to locale change loopWait(1000); // Use the C locale so it can be changed to a different locale later. MLocale locale; QVERIFY2(locale.isValid(), "Invalid locale"); if (not locale.isInstalledTrCatalog(QLatin1String("calendar"))) { locale.installTrCatalog(QLatin1String("calendar")); } locale.connectSettings(); MLocale::setDefault(locale); // Open calendar database, which should have been created by the birthday plugin. mKCal::ExtendedCalendar::Ptr calendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mKCal::ExtendedStorage::Ptr storage = mKCal::ExtendedCalendar::defaultStorage(calendar); storage->open(); QVERIFY2(not storage->notebook(calNotebookId).isNull(), "No calendar database found"); // Check if locale name for calendar matches calendar name. //QVERIFY2(locale.isInstalledTrCatalog(QLatin1String("calendar")), "Calendar locale catalog not installed"); const QString cLocaleCalendarName = qtTrId("qtn_caln_birthdays"); QCOMPARE(storage->notebook(calNotebookId)->name(), cLocaleCalendarName); // Change locale and check name again. store.set(QLatin1String("fi")); loopWait(calendarTimeout); const QString finnishLocaleCalendarName = qtTrId("qtn_caln_birthdays"); QVERIFY2(storage->notebook(calNotebookId)->name() != cLocaleCalendarName, "Calendar name was not updated on locale change"); QCOMPARE(storage->notebook(calNotebookId)->name(), finnishLocaleCalendarName); // Close the calendar. QVERIFY2(storage->close(), "Error closing the calendar"); } void TestBirthdayPlugin::testLeapYears_data() { QTest::addColumn("contactBirthDate"); QTest::newRow("leap-day") << QDate(2008, 2, 29); QTest::newRow("regular-day") << QDate(2008, 2, 1); } void TestBirthdayPlugin::testLeapYears() { const QString contactID = QUuid::createUuid().toString(); QFETCH(QDate, contactBirthDate); // Add contact with birthday to tracker. QContactName contactName; contactName.setFirstName(contactID); QContactBirthday contactBirthday; contactBirthday.setDate(contactBirthDate); QContact contact; QVERIFY(contact.saveDetail(&contactName)); QVERIFY(contact.saveDetail(&contactBirthday)); QVERIFY(saveContact(contact)); // Wait until calendar event gets to calendar. loopWait(calendarTimeout); // Open calendar database. mKCal::ExtendedCalendar::Ptr calendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(QTimeZone::systemTimeZone())); mKCal::ExtendedStorage::Ptr storage = mKCal::ExtendedCalendar::defaultStorage(calendar); QVERIFY(storage->open()); QVERIFY(not storage->notebook(calNotebookId).isNull()); // Check calendar database for contact. QVERIFY(storage->loadNotebookIncidences(calNotebookId)); const KCalendarCore::Event::List eventList = findCalendarEvents(calendar->events(), contact); QCOMPARE(eventList.count(), 1); const KCalendarCore::Event::Ptr event = eventList.first(); QCOMPARE(event->summary(), contactID); QCOMPARE(event->dtStart().date(), contactBirthDate); QCOMPARE(event->allDay(), true); // Check number of recurrences and their values. const QDateTime first(QDate(2000, 1, 1)); const QDateTime last(QDate(2020, 12, 31)); const KCalendarCore::DateTimeList instances = event->recurrence()->timesInInterval(first, last); QCOMPARE(instances.length(), 13); for(int i = 0; i < instances.length(); ++i) { QCOMPARE(instances.at(i).date(), contactBirthDate.addYears(i)); } } void TestBirthdayPlugin::cleanupTestCase() { } void TestBirthdayPlugin::cleanup() { // Remove all contacts modified during the test run. // This could fail if the contacts were already removed, so the response is ignored. mManager->removeContacts(mContactIDs.toList()); mContactIDs.clear(); delete mManager; mManager = 0; } int TestBirthdayPlugin::countCalendarEvents(const KCalendarCore::Event::List &eventList, const QContact &contact) const { return findCalendarEvents(eventList, contact).count(); } KCalendarCore::Event::List TestBirthdayPlugin::findCalendarEvents(const KCalendarCore::Event::List &eventList, const QContact &contact) const { KCalendarCore::Event::List matches; Q_FOREACH(const KCalendarCore::Event::Ptr event, eventList) { if(event->dtStart().date() == contact.detail().date()) { if(event->summary() == contact.detail().label()) { matches += event; } } } return matches; } bool TestBirthdayPlugin::saveContact(QContact &contact) { const bool success = mManager->saveContact(&contact); if (success) { mContactIDs.insert(contact.id()); } return success; } CONTACTSD_TEST_MAIN(TestBirthdayPlugin) contactsd-1.4.14/tests/ut_birthdayplugin/test-birthday-plugin.h000066400000000000000000000042321440357512200247160ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef TEST_BIRTHDAYPLUGIN_H #define TEST_BIRTHDAYPLUGIN_H #include #include #include #include #include QTCONTACTS_USE_NAMESPACE class TestBirthdayPlugin : public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.test-birthday") public: explicit TestBirthdayPlugin(QObject *parent = 0); private Q_SLOTS: void init(); void initTestCase(); void testAddAndRemoveBirthday(); void testChangeBirthday(); void testChangeName(); void testLocaleChange(); void testLeapYears_data(); void testLeapYears(); void cleanupTestCase(); void cleanup(); private: int countCalendarEvents(const KCalendarCore::Event::List &eventList, const QContact &contact) const; KCalendarCore::Event::List findCalendarEvents(const KCalendarCore::Event::List &eventList, const QContact &contact) const; bool saveContact(QContact &contact); private: QContactManager *mManager; QSet mContactIDs; }; #endif // TEST_BIRTHDAYPLUGIN_H contactsd-1.4.14/tests/ut_birthdayplugin/tests.pri000066400000000000000000000041411440357512200223430ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. daemon.target = with-daemon.sh daemon.depends = $$PWD/with-daemon.sh.in daemon.path = /opt/tests/$${PACKAGENAME} daemon.commands = \ sed -e \"s,@BINDIR@,$$BINDIR,g\" \ -e \"s,@PLUGINDIR@,$$LIBDIR/$${PACKAGENAME}-1.0/plugins,g\" \ $< > $@ && chmod +x $@ || rm -f $@ wrapper.target = ut_birthdayplugin-wrapper.sh wrapper.depends = $$PWD/ut_birthdayplugin-wrapper.sh.in wrapper.path = /opt/tests/$${PACKAGENAME}/ut_birthdayplugin wrapper.commands = \ sed -e \"s,@SCRIPTDIR@,/opt/tests/$${PACKAGENAME},g\" \ -e \"s,@OUT_SCRIPTDIR@,/opt/tests/$${PACKAGENAME},g\" \ -e \"s,@BINDIR@,$$BINDIR,g\" \ -e \"s,@WITH_DAEMON@,$$daemon.target,g\" \ $< > $@ && chmod +x $@ || rm -f $@ install_extrascripts.files = $$wrapper.target $$daemon.target install_extrascripts.path = /opt/tests/$${PACKAGENAME}/ut_birthdayplugin install_extrascripts.depends = daemon wrapper install_extrascripts.CONFIG = no_check_exist QMAKE_INSTALL_FILE = cp -p QMAKE_EXTRA_TARGETS += daemon wrapper QMAKE_CLEAN += $$daemon.target $$wrapper.target PRE_TARGETDEPS += $$daemon.target $$wrapper.target INSTALLS += install_extrascripts contactsd-1.4.14/tests/ut_birthdayplugin/ut_birthdayplugin-wrapper.sh.in000066400000000000000000000027031440357512200266430ustar00rootroot00000000000000#! /bin/sh # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. tmpdir=$(mktemp -d) trap "rm -rf $tmpdir" INT TERM EXIT export XDG_DATA_HOME=$tmpdir/local export XDG_CACHE_HOME=$tmpdir/cache export XDG_CONFIG_HOME=$tmpdir/config mkdir -p $XDG_CONFIG_HOME/tracker cat <$XDG_CONFIG_HOME/tracker/tracker-store.cfg [General] Verbosity=3 EOF @SCRIPTDIR@/with-session-bus.sh --config-file=@SCRIPTDIR@/session.conf -- \ @OUT_SCRIPTDIR@/ut_birthdayplugin/@WITH_DAEMON@ \ @OUT_SCRIPTDIR@/ut_birthdayplugin/ut_birthdayplugin $@ contactsd-1.4.14/tests/ut_birthdayplugin/ut_birthdayplugin.pro000066400000000000000000000043571440357512200247550ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../common/test-common.pri) TARGET = ut_birthdayplugin target.path = /opt/tests/$${PACKAGENAME} include(check.pri) include(tests.pri) CONFIG += test link_pkgconfig QT -= gui QT += testlib PKGCONFIG += Qt5Contacts PKGCONFIG += mlite5 mlocale5 libmkcal-qt5 KF5CalendarCore CONFIG(coverage):{ QMAKE_CXXFLAGS += -ftest-coverage -fprofile-arcs LIBS += -lgcov } HEADERS += test-birthday-plugin.h SOURCES += test-birthday-plugin.cpp #gcov stuff CONFIG(coverage):{ INCLUDEPATH += $$TOP_SOURCEDIR/src HEADERS += $$TOP_SOURCEDIR/plugins/birthday/cdbirthdaycalendar.h \ $$TOP_SOURCEDIR/plugins/birthday/cdbirthdaycontroller.h \ $$TOP_SOURCEDIR/plugins/birthday/cdbirthdayplugin.h SOURCES += $$TOP_SOURCEDIRplugins/birthday/cdbirthdaycalendar.cpp \ $$TOP_SOURCEDIR/plugins/birthday/cdbirthdaycontroller.cpp \ $$TOP_SOURCEDIR/plugins/birthday/cdbirthdayplugin.cpp DEFINES += CONTACTSD_PLUGINS_DIR=\\\"$$TOP_SOURCEDIR/plugins/telepathy/\\\" QMAKE_CLEAN += *.gcov $(OBJECTS_DIR)*.gcda $(OBJECTS_DIR)*.gcno gcov.analysis gcov.analysis.summary gcov.target = gcov cov.CONFIG = recursive gcov.commands = for d in $$SOURCES; do (gcov -b -a -u -o $(OBJECTS_DIR) \$$$$d >> gcov.analysis ); done; gcov.depends = $(TARGET) QMAKE_EXTRA_TARGETS += gcov } INSTALLS += target contactsd-1.4.14/tests/ut_birthdayplugin/with-daemon.sh.in000066400000000000000000000040741440357512200236470ustar00rootroot00000000000000#! /bin/sh # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. # gconftool-2 lines got commented, but probably can be removed, since # it seems scratchbox is not used any more to run tests and gconf it self is # also not used in nemo based systems. Tests run just fine w/o. cleanup () { # Restore the original language # gconftool-2 --type string --set /meegotouch/i18n/language $CURRENT_LANG kill $contactsd_pid tracker-control -r > /dev/null } trap cleanup INT HUP TERM #CURRENT_LANG=$(gconftool-2 --get /meegotouch/i18n/language) tracker-control -r > /dev/null /usr/lib/tracker/tracker-store >/dev/null 2>&1 & sleep 3 # Set the language manually since the key is missing in scratchbox # gconftool-2 --type string --set /meegotouch/i18n/language en UNITTEST="$@" # Start Contacts Daemon in background and wait a few seconds for it to launch export CONTACTSD_PLUGINS_DIRS=@PLUGINDIR@ export CONTACTSD_DIRECT_GC=1 # We load only the needed plugins (to avoid eg. voicemail creating contacts) @BINDIR@/contactsd --plugins birthday --enable-debug >contactsd.log 2>&1 & contactsd_pid=$! sleep 5 $UNITTEST e=$? trap - INT HUP TERM cleanup exit $e contactsd-1.4.14/tests/ut_contactsd/000077500000000000000000000000001440357512200174225ustar00rootroot00000000000000contactsd-1.4.14/tests/ut_contactsd/test-contactsd.cpp000066400000000000000000000161161440357512200230720ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include "test-contactsd.h" #include "importstate.h" #include #include #include const QString telepathyString("telepathy"); void TestContactsd::init() { mLoader = new ContactsdPluginLoader(); } void TestContactsd::envTest() { qputenv("CONTACTSD_PLUGINS_DIRS", "/usr/lib/contactsd-1.0/plugins/:/usr/lib/contactsd-1.0/plgins/"); mLoader->loadPlugins(QStringList()); QVERIFY2(mLoader->loadedPlugins().count() > 0, "failed to load plugins from evn variable"); qDebug() << mLoader->loadedPlugins(); qputenv("CONTACTSD_PLUGINS_DIRS", ""); mLoader->loadPlugins(QStringList()); } void TestContactsd::instanceTest() { ContactsdPluginLoader * daemon = new ContactsdPluginLoader(); daemon->~ContactsdPluginLoader(); QVERIFY2(daemon,0); } void TestContactsd::importNonPlugin() { /* This is just a coverage test */ const QString path(QDir::currentPath() + "/data/"); qputenv("CONTACTSD_PLUGINS_DIRS", path.toAscii()); mLoader->loadPlugins(QStringList()); QStringList pluginList = mLoader->loadedPlugins(); QCOMPARE(pluginList.size(), 0); qputenv("CONTACTSD_PLUGINS_DIRS", ""); } void TestContactsd::importTest() { const QString host("com.nokia.contactsd"); QDBusConnection bus = QDBusConnection::sessionBus(); QDBusInterface *interface = new QDBusInterface("com.nokia.contactsd", "/","com.nokia.contacts.importprogress",bus,this); QDBusReply result = interface->call("hasActiveImports"); QVERIFY2(result.isValid() == true, result.error().message().toLatin1()); QCOMPARE(result.value().count(), 0); } void TestContactsd::testFakePlugin() { mLoader->loadPlugins(QStringList() << "fakeplugin"); QStringList pluginList = mLoader->loadedPlugins(); QVERIFY2(pluginList.size() == 0, QString("%1 plugins loaded, expecting 0") .arg(pluginList.size()).toLatin1()); QVERIFY2(not pluginList.contains("fakeplugin"), QString("%1-plugin not Loaded!") .arg(telepathyString).toLatin1()); } void TestContactsd::testDbusPlugin() { mLoader->loadPlugins(QStringList() << "dbusplugin"); QStringList pluginList = mLoader->loadedPlugins(); QVERIFY2(pluginList.size() == 1, QString("%1 plugins loaded, expecting 1") .arg(pluginList.size()).toLatin1()); QVERIFY2(pluginList.contains("dbusplugin"), QString("%1-plugin not Loaded!") .arg(telepathyString).toLatin1()); } void TestContactsd::testLoadAllPlugins() { mLoader->loadPlugins(QStringList()); QStringList pluginList = mLoader->loadedPlugins(); QVERIFY2(pluginList.contains(telepathyString), QString("%1-plugin not Loaded!") .arg(telepathyString).toLatin1()); } void TestContactsd::testLoadPlugins() { mLoader->loadPlugins(QStringList() << telepathyString); QStringList pluginList = mLoader->loadedPlugins(); QVERIFY2(pluginList.size() == 1, QString("%1 plugins loaded, expecting 1") .arg(pluginList.size()).toLatin1()); QVERIFY2(pluginList.contains(telepathyString), QString("%1-plugin not Loaded!") .arg(telepathyString).toLatin1()); } void TestContactsd::testImportState() { ImportState state; QCOMPARE(state.hasActiveImports(), false); state.addImportingAccount("gtalk", "gtalk-account1"); QCOMPARE(state.hasActiveImports(), true); QCOMPARE(state.serviceHasActiveImports("gtalk"), true); state.addImportingAccount("msn", "msn-account1"); QCOMPARE(state.hasActiveImports(), true); QCOMPARE(state.serviceHasActiveImports("msn"), true); QCOMPARE(state.serviceHasActiveImports("gtalk"), true); QStringList activeServices = state.activeImportingServices(); QCOMPARE(activeServices.size(), 2); QVERIFY(activeServices.contains("gtalk")); QVERIFY(activeServices.contains("msn")); state.removeImportingAccount("gtalk", "gtalk-account1", 10, 0, 3); QCOMPARE(state.hasActiveImports(), true); QCOMPARE(state.serviceHasActiveImports("gtalk"), false); QCOMPARE(state.serviceHasActiveImports("msn"), true); QCOMPARE(state.activeImportingServices().size(), 1); state.addImportingAccount("msn", "msn-account2"); QCOMPARE(state.hasActiveImports(), true); QCOMPARE(state.serviceHasActiveImports("msn"), true); state.addImportingAccount("qq", "qq-account1"); state.removeImportingAccount("msn", "msn-account1", 20, 1, 4); QCOMPARE(state.hasActiveImports(), true); QCOMPARE(state.serviceHasActiveImports("msn"), true); QCOMPARE(state.serviceHasActiveImports("qq"), true); state.removeImportingAccount("qq", "qq-account1", 5, 0, 1); QCOMPARE(state.hasActiveImports(), true); QCOMPARE(state.serviceHasActiveImports("qq"), false); QCOMPARE(state.serviceHasActiveImports("msn"), true); state.removeImportingAccount("msn", "msn-account2", 2, 0, 1); QCOMPARE(state.hasActiveImports(), false); QCOMPARE(state.serviceHasActiveImports("msn"), false); QCOMPARE(state.activeImportingServices().size(), 0); QCOMPARE(state.contactsAdded(), 10+20+5+2); QCOMPARE(state.contactsRemoved(), 1); QCOMPARE(state.contactsMerged(), 3+4+1+1); state.reset(); QCOMPARE(state.hasActiveImports(), false); QCOMPARE(state.contactsAdded(), 0); QCOMPARE(state.contactsRemoved(), 0); QCOMPARE(state.contactsMerged(), 0); } void TestContactsd::testDbusRegister() { QVERIFY2(not mLoader->registerNotificationService(), "Re registry failed as expected"); } void TestContactsd::testInvalid() { mLoader->loadPlugins(QStringList() << "invalid"); QStringList pluginList = mLoader->loadedPlugins(); QVERIFY2(pluginList.size() == 0, QString("%1 plugins loaded, expecting 0") .arg(pluginList.size()).toLatin1()); QVERIFY2(not pluginList.contains(telepathyString), QString("%1-plugin not Loaded!") .arg(telepathyString).toLatin1()); } void TestContactsd::cleanup() { if (mLoader) delete mLoader; } CONTACTSD_TEST_MAIN(TestContactsd) contactsd-1.4.14/tests/ut_contactsd/test-contactsd.h000066400000000000000000000031671440357512200225410ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef TEST_CONTACTSD_H #define TEST_CONTACTSD_H #include #include #include class TestContactsd : public QObject { Q_OBJECT private Q_SLOTS: void init(); void envTest(); void instanceTest(); void importNonPlugin(); void importTest(); void testFakePlugin(); void testDbusPlugin(); void testLoadAllPlugins(); void testLoadPlugins(); void testInvalid(); void testImportState(); void testDbusRegister(); void cleanup(); private: ContactsdPluginLoader *mLoader; }; #endif // TEST_CONTACTSD_H contactsd-1.4.14/tests/ut_contactsd/ut_contactsd.pro000066400000000000000000000040051440357512200226350ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. CONFIG += test QT -= gui QT += testlib QT += dbus CONFIG(coverage):{ QMAKE_CXXFLAGS += -ftest-coverage -fprofile-arcs LIBS += -lgcov } include(../common/test-common.pri) TARGET = ut_contactsd target.path = $$BINDIR INCLUDEPATH += $$TOP_SOURCEDIR/src HEADERS += test-contactsd.h \ $$TOP_SOURCEDIR/src/contactsimportprogressadaptor.h \ $$TOP_SOURCEDIR/src/contactsdpluginloader.h \ $$TOP_SOURCEDIR/src/importstate.h SOURCES += test-contactsd.cpp \ $$TOP_SOURCEDIR/src/contactsimportprogressadaptor.cpp \ $$TOP_SOURCEDIR/src/contactsdpluginloader.cpp \ $$TOP_SOURCEDIR/src/importstate.cpp DEFINES += CONTACTSD_PLUGINS_DIR=\\\"$$LIBDIR/$${PACKAGENAME}-1.0/plugins\\\" #gcov stuff CONFIG(coverage):{ QMAKE_CLEAN += *.gcov $(OBJECTS_DIR)*.gcda $(OBJECTS_DIR)*.gcno gcov.analysis gcov.analysis.summary gcov.target = gcov cov.CONFIG = recursive gcov.commands = for d in $$SOURCES; do (gcov -b -a -u -o $(OBJECTS_DIR) \$$$$d >> gcov.analysis ); done; gcov.depends = $(TARGET) QMAKE_EXTRA_TARGETS += gcov } INSTALLS += target contactsd-1.4.14/tests/ut_contactsd/ut_contactsd.skip000066400000000000000000000000001440357512200227720ustar00rootroot00000000000000contactsd-1.4.14/tests/ut_simplugin/000077500000000000000000000000001440357512200174475ustar00rootroot00000000000000contactsd-1.4.14/tests/ut_simplugin/test-sim-plugin.cpp000066400000000000000000000557421440357512200232310ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2013 Jolla Ltd. ** ** Contact: Matt Vogt ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #include "test-sim-plugin.h" #include #include #include #include QTCONTACTS_USE_NAMESPACE namespace { const QString DummyModemPath = QStringLiteral("dummy-cardId"); } // In this test, we bypass the contacts daemon entirely, and test the controller // class which contains all the logic, without involving the real SIM at all. TestSimPlugin::TestSimPlugin(QObject *parent) : QObject(parent) { } QList TestSimPlugin::getAllSimContacts(const QContactManager &m) { QContactCollectionFilter filter; filter.setCollectionId(m_collection.id()); return m.contacts(filter); } QContact TestSimPlugin::createTestContact() { QContact rv; rv.setCollectionId(m_collection.id()); return rv; } void TestSimPlugin::init() { } void TestSimPlugin::initTestCase() { m_controller = new CDSimController(this, false); m_controller->setModemPaths(QStringList() << DummyModemPath); m_controller->m_modems.first()->setReady(true); m_collection = m_controller->contactCollection(DummyModemPath); } void TestSimPlugin::testAdd() { QContactManager &m(m_controller->contactManager()); QCOMPARE(getAllSimContacts(m).count(), 0); QCOMPARE(m_controller->busy(), false); // We don't have a modem path configured, so the controller can't query the SIM m_controller->m_modems.first()->setReady(true); QCOMPARE(m_controller->busy(), false); // Normally, a sim-present notification would cause the controller to fetch // VCard data from the SIM. Instead, we bypass the first part here, and supply // the VCard data directly m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).detail().number(), QStringLiteral("(404) 555-1212")); QVERIFY(simContacts.at(0).detail().contexts().contains(QContactDetail::ContextHome)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } void TestSimPlugin::testAppend() { QContactManager &m(m_controller->contactManager()); // Add the Forrest Gump contact manually QContact forrest(createTestContact()); QContactNickname n; n.setNickname("Forrest Gump"); forrest.saveDetail(&n); QVERIFY(m.saveContact(&forrest)); QCOMPARE(getAllSimContacts(m).count(), 1); QCOMPARE(m_controller->busy(), false); // Process a VCard set containing Forrest Gump and another contact m_controller->m_modems.first()->setReady(true); m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "END:VCARD\n" "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Whittaker\n" "TEL;TYPE=HOME,VOICE:(404) 555-1234\n" "END:VCARD\n")); QTRY_VERIFY(m_controller->busy() == false); // Forrest Whittaker should be added QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 2); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(1).detail().nickname(), QStringLiteral("Forrest Whittaker")); } void TestSimPlugin::testRemove() { QContactManager &m(m_controller->contactManager()); // Add two contacts manually QContact gump(createTestContact()); QContactNickname n; n.setNickname("Forrest Gump"); gump.saveDetail(&n); QContact whittaker(createTestContact()); n.setNickname("Forrest Whittaker"); whittaker.saveDetail(&n); QVERIFY(m.saveContact(&gump)); QVERIFY(m.saveContact(&whittaker)); QCOMPARE(getAllSimContacts(m).count(), 2); QCOMPARE(m_controller->busy(), false); // Process a VCard set containing only Forrest Whittaker m_controller->m_modems.first()->setReady(true); m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Whittaker\n" "TEL;TYPE=HOME,VOICE:(404) 555-1234\n" "END:VCARD\n")); QTRY_VERIFY(m_controller->busy() == false); // Forrest Gump should be removed QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Whittaker")); } void TestSimPlugin::testAddAndRemove() { QContactManager &m(m_controller->contactManager()); // Add the Forrest Gump contact manually QContact forrest(createTestContact()); QContactNickname n; n.setNickname("Forrest Gump"); forrest.saveDetail(&n); QVERIFY(m.saveContact(&forrest)); QCOMPARE(getAllSimContacts(m).count(), 1); QCOMPARE(m_controller->busy(), false); // Process a VCard set not containing Forrest Gump but another contact m_controller->m_modems.first()->setReady(true); m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Whittaker\n" "TEL;TYPE=HOME,VOICE:(404) 555-1234\n" "END:VCARD\n")); QTRY_VERIFY(m_controller->busy() == false); // Forrest Gump should be removed, Forrest Whittaker added QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Whittaker")); } void TestSimPlugin::testChangedNumber() { QContactManager &m(m_controller->contactManager()); QCOMPARE(getAllSimContacts(m).count(), 0); QCOMPARE(m_controller->busy(), false); m_controller->m_modems.first()->setReady(true); QCOMPARE(m_controller->busy(), false); m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).detail().number(), QStringLiteral("(404) 555-1212")); QVERIFY(simContacts.at(0).detail().contexts().contains(QContactDetail::ContextHome)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeVoice)); QContactId existingId = simContacts.at(0).id(); // Change the number and verify that it is updated in the database, but the contact was not recreated m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6789\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); simContacts = getAllSimContacts(m); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).id(), existingId); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).detail().number(), QStringLiteral("(404) 555-6789")); QVERIFY(simContacts.at(0).detail().contexts().contains(QContactDetail::ContextWork)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeVideo)); // Change the context and verify that it is updated in the database m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VIDEO:(404) 555-6789\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); simContacts = getAllSimContacts(m); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).id(), existingId); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).detail().number(), QStringLiteral("(404) 555-6789")); QVERIFY(simContacts.at(0).detail().contexts().contains(QContactDetail::ContextHome)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeVideo)); // Change the subtype and verify that it is updated in the database m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE,CELL:(404) 555-6789\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); simContacts = getAllSimContacts(m); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).id(), existingId); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).detail().number(), QStringLiteral("(404) 555-6789")); QVERIFY(simContacts.at(0).detail().contexts().contains(QContactDetail::ContextHome)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeVoice)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeMobile)); } void TestSimPlugin::testMultipleNumbers() { QContactManager &m(m_controller->contactManager()); QCOMPARE(getAllSimContacts(m).count(), 0); QCOMPARE(m_controller->busy(), false); m_controller->m_modems.first()->setReady(true); QCOMPARE(m_controller->busy(), false); // Add a contact with two numbers m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6789\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 2); foreach (const QContactPhoneNumber &number, simContacts.at(0).details()) { if (number.number() == QStringLiteral("(404) 555-1212")) { QVERIFY(number.contexts().contains(QContactDetail::ContextHome)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } else { QCOMPARE(number.number(), QStringLiteral("(404) 555-6789")); QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVideo)); } } QContactId existingId = simContacts.at(0).id(); // Change the set of numbers m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6789\n" "TEL;TYPE=HOME,CELL:(404) 555-5555\n" "TEL;TYPE=WORK,VOICE:(404) 555-4321\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); // Verify that numbers were added and removed, but the contact was not recreated simContacts = getAllSimContacts(m); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).id(), existingId); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 3); foreach (const QContactPhoneNumber &number, simContacts.at(0).details()) { if (number.number() == QStringLiteral("(404) 555-6789")) { QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVideo)); } else if (number.number() == QStringLiteral("(404) 555-5555")) { QVERIFY(number.contexts().contains(QContactDetail::ContextHome)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeMobile)); } else { QCOMPARE(number.number(), QStringLiteral("(404) 555-4321")); QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } } } void TestSimPlugin::testMultipleIdenticalNumbers() { QContactManager &m(m_controller->contactManager()); QCOMPARE(getAllSimContacts(m).count(), 0); QCOMPARE(m_controller->busy(), false); m_controller->m_modems.first()->setReady(true); QCOMPARE(m_controller->busy(), false); // Add a contact with two very similar numbers m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=WORK,VIDEO:(404) 555-1212\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 2); foreach (const QContactPhoneNumber &number, simContacts.at(0).details()) { if (number.contexts().contains(QContactDetail::ContextHome)) { QVERIFY(number.number() == QStringLiteral("(404) 555-1212")); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } else { QCOMPARE(number.number(), QStringLiteral("(404) 555-1212")); QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVideo)); } } QContactId existingId = simContacts.at(0).id(); // Modify contact so that the number is now identical m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); // Verify that the contact was not recreated, but instead updated, and that the // duplicate phone number detail has not been duplicated to the stored contact. simContacts = getAllSimContacts(m); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).id(), existingId); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 1); QVERIFY(simContacts.at(0).detail().number() == QStringLiteral("(404) 555-1212")); QVERIFY(simContacts.at(0).detail().contexts().contains(QContactDetail::ContextHome)); QVERIFY(simContacts.at(0).detail().subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } void TestSimPlugin::testTrimWhitespace() { QContactManager &m(m_controller->contactManager()); QCOMPARE(getAllSimContacts(m).count(), 0); QCOMPARE(m_controller->busy(), false); m_controller->m_modems.first()->setReady(true); QCOMPARE(m_controller->busy(), false); // Add a contact with two numbers m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6789\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 2); foreach (const QContactPhoneNumber &number, simContacts.at(0).details()) { if (number.number() == QStringLiteral("(404) 555-1212")) { QVERIFY(number.contexts().contains(QContactDetail::ContextHome)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } else { QCOMPARE(number.number(), QStringLiteral("(404) 555-6789")); QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVideo)); } } QContactId existingId = simContacts.at(0).id(); // Add another contact with identical + whitespace name, changed number m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump \n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6888\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); // Verify that the contact was not recreated, but instead updated simContacts = getAllSimContacts(m); QCOMPARE(simContacts.count(), 1); QCOMPARE(simContacts.at(0).id(), existingId); QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 2); foreach (const QContactPhoneNumber &number, simContacts.at(0).details()) { if (number.number() == QStringLiteral("(404) 555-1212")) { QVERIFY(number.contexts().contains(QContactDetail::ContextHome)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } else { QCOMPARE(number.number(), QStringLiteral("(404) 555-6888")); QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVideo)); } } } void TestSimPlugin::testCoalescing() { QContactManager &m(m_controller->contactManager()); QCOMPARE(getAllSimContacts(m).count(), 0); QCOMPARE(m_controller->busy(), false); m_controller->m_modems.first()->setReady(true); QCOMPARE(m_controller->busy(), false); // Add the same contact a bunch of times m_controller->m_modems.first()->vcardDataAvailable(QStringLiteral( "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6789\n" "END:VCARD\n" "BEGIN:VCARD\n" "VERSION:3.0\n" "FN:Forrest Gump \n" /* whitespace shouldn't affect coalescing */ "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "TEL;TYPE=WORK,VIDEO:(404) 555-6789\n" "TEL;TYPE=WORK,VOICE:(404) 555-6888\n" "END:VCARD\n" "FN:Forrest Gump\n" "TEL;TYPE=HOME,VOICE:(404) 555-1212\n" "END:VCARD\n")); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 1); // should all be coalesced into one. QCOMPARE(simContacts.at(0).detail().nickname(), QStringLiteral("Forrest Gump")); QCOMPARE(simContacts.at(0).details().count(), 3); foreach (const QContactPhoneNumber &number, simContacts.at(0).details()) { if (number.number() == QStringLiteral("(404) 555-1212")) { QVERIFY(number.contexts().contains(QContactDetail::ContextHome)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } else if (number.number() == QStringLiteral("(404) 555-6888")) { QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVoice)); } else { QCOMPARE(number.number(), QStringLiteral("(404) 555-6789")); QVERIFY(number.contexts().contains(QContactDetail::ContextWork)); QVERIFY(number.subTypes().contains(QContactPhoneNumber::SubTypeVideo)); } } } void TestSimPlugin::testEmpty() { QContactManager &m(m_controller->contactManager()); // Add the Forrest Gump contact manually QContact forrest(createTestContact()); QContactNickname n; n.setNickname("Forrest Gump"); forrest.saveDetail(&n); QVERIFY(m.saveContact(&forrest)); QCOMPARE(getAllSimContacts(m).count(), 1); QCOMPARE(m_controller->busy(), false); // Process an empty VCard m_controller->m_modems.first()->setReady(true); m_controller->m_modems.first()->vcardDataAvailable(QString()); QCOMPARE(m_controller->busy(), true); QTRY_VERIFY(m_controller->busy() == false); // No-longer-present contact should be removed QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 0); } void TestSimPlugin::testClear() { QContactManager &m(m_controller->contactManager()); // Add two contacts manually QContact gump(createTestContact()); QContactNickname n; n.setNickname("Forrest Gump"); gump.saveDetail(&n); QContact whittaker(createTestContact()); n.setNickname("Forrest Whittaker"); whittaker.saveDetail(&n); QVERIFY(m.saveContact(&gump)); QVERIFY(m.saveContact(&whittaker)); QCOMPARE(getAllSimContacts(m).count(), 2); QCOMPARE(m_controller->busy(), false); // Report the SIM card removed m_controller->m_modems.first()->phonebookValidChanged(false); QTRY_VERIFY(m_controller->busy() == false); // All sim contacts should be removed QList simContacts(getAllSimContacts(m)); QCOMPARE(simContacts.count(), 0); } void TestSimPlugin::cleanupTestCase() { if (CDSimModemData::removeCollections(&m_controller->contactManager(), QList() << m_collection.id())) { qDebug() << "Remove test collection"; } } void TestSimPlugin::cleanup() { QContactManager &m(m_controller->contactManager()); foreach (const QContact &contact, getAllSimContacts(m)) { QVERIFY(m.removeContact(contact.id())); } } CONTACTSD_TEST_MAIN(TestSimPlugin) contactsd-1.4.14/tests/ut_simplugin/test-sim-plugin.h000066400000000000000000000032061440357512200226620ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2013 Jolla Ltd. ** ** Contact: Matt Vogt ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. **/ #ifndef TEST_SIM_PLUGIN_H #define TEST_SIM_PLUGIN_H #include #include // "unprotect" m_phonebook.onModemInterfacesChanged #define private public #define protected public #include "cdsimcontroller.h" #undef private #undef protected class TestSimPlugin : public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.test-sim") public: explicit TestSimPlugin(QObject *parent = 0); private Q_SLOTS: void init(); void initTestCase(); void testAdd(); void testAppend(); void testRemove(); void testAddAndRemove(); void testChangedNumber(); void testMultipleNumbers(); void testMultipleIdenticalNumbers(); void testTrimWhitespace(); void testCoalescing(); void testEmpty(); void testClear(); void cleanupTestCase(); void cleanup(); private: QList getAllSimContacts(const QContactManager &m); QContact createTestContact(); CDSimController *m_controller; QContactCollection m_collection; }; #endif // TEST_SIM_PLUGIN_H contactsd-1.4.14/tests/ut_simplugin/ut_simplugin.pro000066400000000000000000000010031440357512200227020ustar00rootroot00000000000000include(../common/test-common.pri) TARGET = ut_simplugin target.path = /opt/tests/$${PACKAGENAME}/$$TARGET CONFIG += test link_pkgconfig QT -= gui QT += dbus testlib PKGCONFIG += mlite5 Qt5Contacts Qt5Versit qofono-qt5 PKGCONFIG += qtcontacts-sqlite-qt5-extensions qofonoext INCLUDEPATH += \ ../../plugins/sim \ ../../src HEADERS += \ test-sim-plugin.h \ ../../plugins/sim/cdsimcontroller.h SOURCES += \ test-sim-plugin.cpp \ ../../plugins/sim/cdsimcontroller.cpp INSTALLS += target contactsd-1.4.14/tests/ut_telepathyplugin/000077500000000000000000000000001440357512200206565ustar00rootroot00000000000000contactsd-1.4.14/tests/ut_telepathyplugin/check.pri000066400000000000000000000034071440357512200224530ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. check_daemon.target = check-with-daemon.sh check_daemon.depends = $$PWD/with-daemon.sh.in check_daemon.commands = \ sed -e "s,@BINDIR@,$$TOP_BUILDDIR/src,g" \ -e "s,@PLUGINDIR@,$$TOP_BUILDDIR/plugins/telepathy,g" \ $< > $@ && chmod +x $@ || rm -f $@ check_wrapper.target = check-ut_telepathyplugin-wrapper.sh check_wrapper.depends = $$PWD/ut_telepathyplugin-wrapper.sh.in check_wrapper.commands = \ sed -e "s,@SCRIPTDIR@,$$PWD/..,g" \ -e "s,@BINDIR@,$$PWD,g" \ -e "s,@WITH_DAEMON@,$$check_daemon.target,g" \ $< > $@ && chmod +x $@ || rm -f $@ check.depends = $$TARGET check_wrapper check_daemon check.commands = sh $$check_wrapper.target QMAKE_EXTRA_TARGETS += check_wrapper check_daemon check QMAKE_CLEAN += $$check_daemon.target $$check_wrapper.target contactsd-1.4.14/tests/ut_telepathyplugin/debug.cpp000066400000000000000000000016461440357512200224570ustar00rootroot00000000000000/* * Copyright (C) 2008-2009 Collabora Ltd. * Copyright (C) 2008-2009 Nokia Corporation * * 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 */ #include "debug.h" Q_LOGGING_CATEGORY(lcContactsd2, "contactsdFOO") contactsd-1.4.14/tests/ut_telepathyplugin/debug.h000066400000000000000000000017311440357512200221170ustar00rootroot00000000000000/* * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2008 Nokia Corporation * * 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 */ #ifndef CONTACTSD_DEBUG_H #define CONTACTSD_DEBUG_H #include Q_DECLARE_LOGGING_CATEGORY(lcContactsd) #endif contactsd-1.4.14/tests/ut_telepathyplugin/test-expectation.cpp000066400000000000000000000405411440357512200246660ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "test-expectation.h" #include "debug.h" // --- TestExpectation --- void TestExpectation::verify(Event event, const QList &contactIds) { new TestFetchContacts(contactIds, event, this); } void TestExpectation::verify(Event event, const QList &contacts) { Q_UNUSED(event); Q_UNUSED(contacts); emitFinished(); } void TestExpectation::verify(Event event, const QList &contactIds, QContactManager::Error error) { Q_UNUSED(event); Q_UNUSED(contactIds); Q_UNUSED(error); QVERIFY2(false, "Error fetching contacts"); emitFinished(); } void TestExpectation::emitFinished() { Q_EMIT finished(); } // --- TestFetchContacts --- TestFetchContacts::TestFetchContacts(const QList &contactIds, Event event, TestExpectation *exp) : QObject(exp), mContactIds(contactIds), mEvent(event), mExp(exp) { QContactFetchByIdRequest *request = new QContactFetchByIdRequest(); connect(request, SIGNAL(resultsAvailable()), SLOT(onContactsFetched())); request->setManager(mExp->contactManager()); request->setIds(contactIds); QVERIFY(request->start()); } void TestFetchContacts::onContactsFetched() { QContactFetchByIdRequest *req = qobject_cast(sender()); if (req == 0 || !req->isFinished()) { return; } if (req->error() == QContactManager::NoError) { QList contacts; Q_FOREACH (const QContact &contact, req->contacts()) { qCDebug(lcContactsd) << "Contact fetched:\n\n" << contact << "\n"; // For some reason, we get VoiceMail signals sometimes. Ignore them. if (contact.detail().tag() == QLatin1String("voicemail")) { qCDebug(lcContactsd) << "Ignoring voicemail contact"; continue; } contacts << contact; } if (!contacts.isEmpty()) { mExp->verify(mEvent, contacts); } } else { mExp->verify(mEvent, mContactIds, req->error()); } deleteLater(); req->deleteLater(); } // --- TestExpectationInit --- void TestExpectationInit::verify(Event event, const QList &contacts) { if (event != EventChanged) return; QVERIFY(contacts.count() >= 1); QList changedIds; Q_FOREACH (const QContact &contact, contacts) changedIds.append(contact.id()); QVERIFY(changedIds.contains(contactManager()->selfContactId())); emitFinished(); } void TestExpectationInit::verify(Event event, const QList &contactIds, QContactManager::Error error) { QCOMPARE(event, EventRemoved); QCOMPARE(contactIds.count(), 1); QCOMPARE(error, QContactManager::DoesNotExistError); emitFinished(); } // --- TestExpectationCleanup --- TestExpectationCleanup::TestExpectationCleanup(int nContacts) : mNContacts(nContacts), mSelfChanged(false) { } void TestExpectationCleanup::verify(Event event, const QList &contacts) { QCOMPARE(event, EventChanged); Q_FOREACH (const QContact &contact, contacts) { if (contact.id() == contactManager()->selfContactId()) { QVERIFY(!mSelfChanged); mSelfChanged = true; mNContacts--; continue; } mNContacts--; } maybeEmitFinished(); } void TestExpectationCleanup::verify(Event event, const QList &contactIds, QContactManager::Error error) { QCOMPARE(event, EventRemoved); QCOMPARE(error, QContactManager::DoesNotExistError); mNContacts -= contactIds.count(); maybeEmitFinished(); } void TestExpectationCleanup::maybeEmitFinished() { QVERIFY(mNContacts >= 0); if (mNContacts == 0 && mSelfChanged) { emitFinished(); } } // --- TestExpectationContact --- TestExpectationContact::TestExpectationContact(Event event, QString accountUri): mAccountUri(accountUri), mEvent(event), mFlags(0), mPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET), mContactInfo(0) { } void TestExpectationContact::verify(Event event, const QList &contacts) { if (event != mEvent) return; QVERIFY(contacts.count() >= 1); mContact = QContact(); Q_FOREACH (const QContact &contact, contacts) { if (contact.collectionId().isNull() || mCollectionId == contact.collectionId()) { mContact = contact; } } verify(mContact); emitFinished(); } void TestExpectationContact::verify(Event event, const QList &contactIds, QContactManager::Error error) { QCOMPARE(event, EventRemoved); QVERIFY(contactIds.count() >= 1); QCOMPARE(error, QContactManager::DoesNotExistError); emitFinished(); } void TestExpectationContact::verify(const QContact &contact) { if (!mAccountUri.isEmpty()) { QList details = contact.details(); QCOMPARE(details.count(), 1); QCOMPARE(details[0].value(QContactOnlineAccount__FieldAccountPath), QString(ACCOUNT_PATH)); } if (mFlags & VerifyAlias) { QString nickname = contact.detail().nickname(); QCOMPARE(nickname, mAlias); } if (mFlags & VerifyPresence) { QContactPresence::PresenceState presence; if (mAccountUri.isEmpty()) { QContactGlobalPresence presenceDetail = contact.detail(); presence = presenceDetail.presenceState(); } else { QList details = contact.details(); QCOMPARE(details.count(), 1); presence = details[0].presenceState(); } switch (mPresence) { case TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE: QCOMPARE(presence, QContactPresence::PresenceAvailable); break; case TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY: QCOMPARE(presence, QContactPresence::PresenceBusy); break; case TP_TESTS_CONTACTS_CONNECTION_STATUS_AWAY: QCOMPARE(presence, QContactPresence::PresenceAway); break; case TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE: QCOMPARE(presence, QContactPresence::PresenceOffline); break; case TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN: case TP_TESTS_CONTACTS_CONNECTION_STATUS_ERROR: case TP_TESTS_CONTACTS_CONNECTION_STATUS_UNSET: QCOMPARE(presence, QContactPresence::PresenceUnknown); break; } } if (mFlags & VerifyAvatar) { QString avatarFileName = contact.detail().imageUrl().path(); if (mAvatarData.isEmpty()) { QVERIFY2(avatarFileName.isEmpty(), "Expected empty avatar filename"); } else { QFile file(avatarFileName); file.open(QIODevice::ReadOnly); QCOMPARE(file.readAll(), mAvatarData); file.close(); } } if (mFlags & VerifyAuthorization) { qWarning() << "Unsupported functionality: presence authorization"; } if (mFlags & VerifyInfo) { uint nMatchedField = 0; Q_FOREACH (const QContactDetail &detail, contact.details()) { QStringList params; if (detail.contexts().contains(QContactDetail::ContextWork)) { params << QLatin1String("type=work"); } if (detail.contexts().contains(QContactDetail::ContextHome)) { params << QLatin1String("type=home"); } if (detail.type() ==QContactPhoneNumber::Type) { QContactPhoneNumber phoneNumber = static_cast(detail); Q_FOREACH (int type, phoneNumber.subTypes()) { if (type == QContactPhoneNumber::SubTypeMobile) { params << QLatin1String("type=cell"); } else if (type == QContactPhoneNumber::SubTypeVideo) { params << QLatin1String("type=video"); } } verifyContactInfo("tel", QStringList() << phoneNumber.number(), params); nMatchedField++; } else if (detail.type() == QContactAddress::Type) { QContactAddress address = static_cast(detail); verifyContactInfo("adr", QStringList() << address.postOfficeBox() << address.street() << address.locality() << address.region() << address.postcode() << address.country(), params); nMatchedField++; } else if (detail.type() ==QContactEmailAddress::Type) { QContactEmailAddress emailAddress = static_cast(detail); verifyContactInfo("email", QStringList() << emailAddress.emailAddress(), params); nMatchedField++; } } if (mContactInfo != NULL) { QCOMPARE(nMatchedField, mContactInfo->len); } } if (mFlags & VerifyContactId) { QCOMPARE(contact.id(), mContactId); } if (mFlags & VerifyGenerator) { QContactCollectionId aggregateCollectionId = QtContactsSqliteExtensions::aggregateCollectionId(contactManager()->managerUri()); QVERIFY(contact.collectionId() == mGenerator || contact.collectionId() == aggregateCollectionId); } } void TestExpectationContact::verifyContactInfo(const QString name, const QStringList values, const QStringList params) const { QVERIFY2(mContactInfo != NULL, "Found ContactInfo field, was expecting none"); /* All well known params that must be matched */ static QStringList knownParams; if (knownParams.isEmpty()) { knownParams << QLatin1String("type=work"); knownParams << QLatin1String("type=home"); knownParams << QLatin1String("type=cell"); knownParams << QLatin1String("type=video"); } bool found = false; for (uint i = 0; i < mContactInfo->len; i++) { gchar *c_name; gchar **c_parameters; gchar **c_values; tp_value_array_unpack((GValueArray*) g_ptr_array_index(mContactInfo, i), 3, &c_name, &c_parameters, &c_values); /* if c_values_len < values.count() it could still be OK if all * additional values are empty. */ gint c_values_len = g_strv_length(c_values); if (QString(c_name) != name || c_values_len > values.count()) { continue; } /* Verify values match */ bool match = true; for (int j = 0; j < values.count(); j++) { if (values[j] == QString("unmapped")) { continue; } if ((j < c_values_len && values[j] != QString(c_values[j])) || (j >= c_values_len && !values[j].isEmpty())) { match = false; break; } } if (!match) { continue; } /* Verify params match */ QStringList expectedParams = params; while (c_parameters && *c_parameters) { QLatin1String c_param(*c_parameters); if (!expectedParams.removeOne(c_param) && knownParams.contains(c_param)) { match = false; break; } c_parameters++; } if (!expectedParams.isEmpty()) { match = false; } if (match) { found = true; break; } } QVERIFY2(found, "Unexpected ContactInfo field"); } // --- TestExpectationDisconnect --- TestExpectationDisconnect::TestExpectationDisconnect(int nContacts) : TestExpectationContact(EventChanged), mNContacts(nContacts), mSelfChanged(false) { } void TestExpectationDisconnect::verify(Event event, const QList &contacts) { Q_FOREACH (const QContact &contact, contacts) { if (contact.collectionId() == QtContactsSqliteExtensions::aggregateCollectionId(contactManager()->managerUri())) { mNContacts--; } else { if (contact.id() == contactManager()->selfContactId()) { QCOMPARE(event, EventChanged); verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_OFFLINE); mSelfChanged = true; mNContacts--; } else { QCOMPARE(event, EventChanged); verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN); mNContacts--; } TestExpectationContact::verify(contact); } } QVERIFY(mNContacts >= 0); if (mNContacts == 0 && mSelfChanged) { emitFinished(); } } // --- TestExpectationMerge --- TestExpectationMerge::TestExpectationMerge(const QContactId &masterId, const QList &mergeIds, const QList expectations) : mMasterId(masterId), mMergeIds(mergeIds), mGotMergedContact(false), mContactExpectations(expectations) { } void TestExpectationMerge::verify(Event event, const QList &contacts) { QCOMPARE(event, EventChanged); QCOMPARE(contacts.count(), 1); QCOMPARE(contacts[0].id(), mMasterId); mGotMergedContact = true; Q_FOREACH (TestExpectationContactPtr exp, mContactExpectations) { exp->verify(contacts[0]); } maybeEmitFinished(); } void TestExpectationMerge::verify(Event event, const QList &contactIds, QContactManager::Error error) { QCOMPARE(event, EventRemoved); QCOMPARE(error, QContactManager::DoesNotExistError); Q_FOREACH (const QContactId &id, contactIds) { QVERIFY(mMergeIds.contains(id)); mMergeIds.removeOne(id); } maybeEmitFinished(); } void TestExpectationMerge::maybeEmitFinished() { if (mMergeIds.isEmpty() && mGotMergedContact) { emitFinished(); } } // --- TestExpectationMass --- TestExpectationMass::TestExpectationMass(int nAdded, int nChanged, int nRemoved) : mAdded(nAdded), mChanged(nChanged), mRemoved(nRemoved) { } void TestExpectationMass::verify(Event event, const QList &contacts) { if (event == EventAdded) { mAdded -= contacts.count(); QVERIFY(mAdded >= 0); } if (event == EventChanged) { mChanged -= contacts.count(); QVERIFY(mChanged >= 0); } maybeEmitFinished(); } void TestExpectationMass::verify(Event event, const QList &contactIds, QContactManager::Error error) { QCOMPARE(event, EventRemoved); QCOMPARE(error, QContactManager::DoesNotExistError); mRemoved -= contactIds.count(); QVERIFY(mRemoved >= 0); maybeEmitFinished(); } void TestExpectationMass::maybeEmitFinished() { if (mAdded == 0 && mChanged == 0 && mRemoved == 0) { emitFinished(); } } contactsd-1.4.14/tests/ut_telepathyplugin/test-expectation.h000066400000000000000000000167071440357512200243420ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef TEST_EXPECTATION_H #define TEST_EXPECTATION_H #include #include #include #include #include #include #include "libtelepathy/contacts-conn.h" #define ACCOUNT_PATH TP_ACCOUNT_OBJECT_PATH_BASE "fakecm/fakeproto/fakeaccount" QTCONTACTS_USE_NAMESPACE typedef enum { EventAdded, EventChanged, EventPresenceChanged, EventRemoved } Event; // --- TestExpectation --- class TestExpectation : public QObject, public Tp::RefCounted { Q_OBJECT public: void verify(Event event, const QList &contactIds); void setContactManager(QContactManager *contactManager) { mContactManager = contactManager; }; QContactManager *contactManager() { return mContactManager; }; Q_SIGNALS: void finished(); protected: virtual void verify(Event event, const QList &contacts); virtual void verify(Event event, const QList &contactIds, QContactManager::Error error); void emitFinished(); private: friend class TestFetchContacts; QContactManager *mContactManager; }; typedef Tp::SharedPtr TestExpectationPtr; // --- TestFetchContacts --- class TestFetchContacts : public QObject { Q_OBJECT public: TestFetchContacts(const QList &contactIds, Event event, TestExpectation *exp); private Q_SLOTS: void onContactsFetched(); private: QList mContactIds; Event mEvent; TestExpectation *mExp; }; // --- TestExpectationInit --- class TestExpectationInit : public TestExpectation { Q_OBJECT protected: void verify(Event event, const QList &contacts); void verify(Event event, const QList &contactIds, QContactManager::Error error); }; typedef Tp::SharedPtr TestExpectationInitPtr; // --- TestExpectationCleanup --- class TestExpectationCleanup : public TestExpectation { Q_OBJECT public: TestExpectationCleanup(int nContacts); protected: void verify(Event event, const QList &contacts); void verify(Event event, const QList &contactIds, QContactManager::Error error); private: void maybeEmitFinished(); private: int mNContacts; bool mSelfChanged; }; typedef Tp::SharedPtr TestExpectationCleanupPtr; // --- TestExpectationContact --- class TestExpectationContact : public TestExpectation { Q_OBJECT public: TestExpectationContact(Event event, QString accountUri = QString()); QContact contact() { return mContact; }; void setEvent(const Event &event) { mEvent = event; }; void resetVerifyFlags() { mFlags = 0; }; void skipUnlessGeneratorIs(const QContactCollectionId &generator) { mCollectionId = generator; }; void verifyAlias(const QString &alias) { mAlias = alias; mFlags |= VerifyAlias; }; void verifyPresence(TpTestsContactsConnectionPresenceStatusIndex presence) { mPresence = presence; mFlags |= VerifyPresence; }; void verifyAvatar(const QByteArray &avatarData) { mAvatarData = avatarData; mFlags |= VerifyAvatar; }; void verifyAuthorization(const QString &subscriptionState, const QString &publishState) { mSubscriptionState = subscriptionState; mPublishState = publishState; mFlags |= VerifyAuthorization; }; void verifyInfo(GPtrArray *contactInfo) { mContactInfo = contactInfo; mFlags |= VerifyInfo; }; void verifyContactId(const QContact &contact) { mContactId = contact.id(); mFlags |= VerifyContactId; }; void verifyGenerator(const QContactCollectionId &generator) { mGenerator = generator; mFlags |= VerifyGenerator; skipUnlessGeneratorIs(mGenerator); } void verify(const QContact &contact); protected: void verify(Event event, const QList &contacts); void verify(Event event, const QList &contactIds, QContactManager::Error error); private: enum VerifyFlags { VerifyNone = 0, VerifyAlias = (1 << 0), VerifyPresence = (1 << 1), VerifyAvatar = (1 << 2), VerifyAuthorization = (1 << 3), VerifyInfo = (1 << 4), VerifyContactId = (1 << 5), VerifyGenerator = (1 << 6), VerifyAll = (1 << 7) - 1 }; void verifyContactInfo(const QString name, const QStringList values, const QStringList params) const; QString mAccountUri; Event mEvent; int mFlags; QString mAlias; TpTestsContactsConnectionPresenceStatusIndex mPresence; QByteArray mAvatarData; QString mSubscriptionState; QString mPublishState; GPtrArray *mContactInfo; QContactId mContactId; QContactCollectionId mGenerator; QContactCollectionId mCollectionId; QContact mContact; }; typedef Tp::SharedPtr TestExpectationContactPtr; // --- TestExpectationDisconnect --- class TestExpectationDisconnect : public TestExpectationContact { Q_OBJECT public: TestExpectationDisconnect(int nContacts); protected: void verify(Event event, const QList &contacts); private: int mNContacts; bool mSelfChanged; }; typedef Tp::SharedPtr TestExpectationDisconnectPtr; // --- TestExpectationMerge --- class TestExpectationMerge : public TestExpectation { Q_OBJECT public: TestExpectationMerge(const QContactId &masterId, const QList &mergeIds, const QList expectations = QList()); protected: void verify(Event event, const QList &contacts); void verify(Event event, const QList &contactIds, QContactManager::Error error); private: void maybeEmitFinished(); QContactId mMasterId; QList mMergeIds; bool mGotMergedContact; QList mContactExpectations; }; typedef Tp::SharedPtr TestExpectationMergePtr; // --- TestExpectationMass --- class TestExpectationMass : public TestExpectation { Q_OBJECT public: TestExpectationMass(int nAdded, int nChanged, int nRemoved); protected: void verify(Event event, const QList &contacts); void verify(Event event, const QList &contactIds, QContactManager::Error error); private: void maybeEmitFinished(); int mAdded; int mChanged; int mRemoved; }; typedef Tp::SharedPtr TestExpectationMassPtr; #endif contactsd-1.4.14/tests/ut_telepathyplugin/test-telepathy-plugin.cpp000066400000000000000000000761531440357512200256460ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** Copyright (c) 2012 - 2019 Jolla Ltd. ** Copyright (c) 2020 Open Mobile Platform LLC. ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #include #include #include #include #include #include #include #include #include #include #include "libtelepathy/util.h" #include "libtelepathy/debug.h" #include #include "test-telepathy-plugin.h" #include "buddymanagementinterface.h" #include "debug.h" namespace { const int QContactOnlineAccount__FieldAccountPath = (QContactOnlineAccount::FieldSubTypes+1); } TestTelepathyPlugin::TestTelepathyPlugin(QObject *parent) : Test(parent), mNOnlyLocalContacts(0), mCheckLeakedResources(true) { } void TestTelepathyPlugin::initTestCase() { initTestCaseImpl(); qRegisterMetaType("QContactAbstractRequest::State"); g_set_prgname("test-telepathy-plugin"); if (!qgetenv("CONTACTSD_DEBUG").isEmpty()) { tp_debug_set_flags("all"); test_debug_enable(TRUE); } dbus_g_bus_get(DBUS_BUS_STARTER, 0); /* Create a QContactManager and track added/changed contacts */ QMap parameters; parameters.insert(QStringLiteral("mergePresenceChanges"), QStringLiteral("true")); mContactManager = new QContactManager(QStringLiteral("org.nemomobile.contacts.sqlite"), parameters); connect(mContactManager, &QContactManager::contactsAdded, this, &TestTelepathyPlugin::contactsAdded); connect(mContactManager, &QContactManager::contactsChanged, this, &TestTelepathyPlugin::contactsChanged); connect(mContactManager, &QContactManager::contactsRemoved, this, &TestTelepathyPlugin::contactsRemoved); mContactIds += mContactManager->selfContactId(); /* Create a fake AccountManager */ TpDBusDaemon *dbus = tp_dbus_daemon_dup(NULL); mAccountManager = (TpTestsSimpleAccountManager *) tp_tests_object_new_static_class( TP_TESTS_TYPE_SIMPLE_ACCOUNT_MANAGER, NULL); tp_dbus_daemon_register_object(dbus, TP_ACCOUNT_MANAGER_OBJECT_PATH, mAccountManager); tp_dbus_daemon_request_name (dbus, TP_ACCOUNT_MANAGER_BUS_NAME, FALSE, NULL); g_object_unref(dbus); } void TestTelepathyPlugin::cleanupTestCase() { cleanupTestCaseImpl(); delete mContactManager; g_object_unref(mAccountManager); } void TestTelepathyPlugin::init() { initImpl(); mNOnlyLocalContacts = 0; mCheckLeakedResources = true; /* Create a fake Connection */ tp_tests_create_and_connect_conn(TP_TESTS_TYPE_CONTACTS_CONNECTION, "fakeaccount", &mConnService, &mConnection); QVERIFY(mConnService); QVERIFY(mConnection); mListManager = tp_tests_contacts_connection_get_contact_list_manager( TP_TESTS_CONTACTS_CONNECTION(mConnService)); /* Define the self contact */ TpTestsContactsConnectionPresenceStatusIndex presence = TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE; const gchar *message = "Running unit tests"; tp_tests_contacts_connection_change_presences( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &mConnService->self_handle, &presence, &message); const gchar *alias = "badger"; tp_tests_contacts_connection_change_aliases( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &mConnService->self_handle, &alias); /* Create a fake Account */ TpDBusDaemon *dbus = tp_dbus_daemon_dup(NULL); mAccount = (TpTestsSimpleAccount *) tp_tests_object_new_static_class( TP_TESTS_TYPE_SIMPLE_ACCOUNT, NULL); tp_dbus_daemon_register_object(dbus, ACCOUNT_PATH, mAccount); tp_tests_simple_account_manager_add_account (mAccountManager, ACCOUNT_PATH, TRUE); tp_tests_simple_account_set_connection (mAccount, mConnService->object_path); g_object_unref(dbus); /* Wait for self contact to appear */ runExpectation(TestExpectationInitPtr(new TestExpectationInit())); } void TestTelepathyPlugin::cleanup() { cleanupImpl(); tp_cli_connection_call_disconnect(mConnection, -1, NULL, NULL, NULL, NULL); tp_tests_simple_account_manager_remove_account(mAccountManager, ACCOUNT_PATH); tp_tests_simple_account_removed(mAccount); int removed = mContactIds.count() - mNOnlyLocalContacts; /* Wait for all contacts to disappear, and local contacts to get updated */ runExpectation(TestExpectationCleanupPtr(new TestExpectationCleanup(removed))); /* Remove remaining local contacts */ QList contactsToRemove = mContactIds; contactsToRemove.removeOne(mContactManager->selfContactId()); if (!contactsToRemove.isEmpty()) { QContactRemoveRequest *request = new QContactRemoveRequest(); request->setContactIds(contactsToRemove); startRequest(request); // Removing the telepathy self constituent will change the self aggregate int changed = 1; removed = contactsToRemove.count(); runExpectation(TestExpectationMassPtr(new TestExpectationMass(0, changed, removed))); } QVERIFY(mContactIds.count() == 1); g_object_unref(mConnService); g_object_unref(mConnection); g_object_unref(mAccount); } void TestTelepathyPlugin::testBasicUpdates() { /* Create a new contact */ TpHandle handle = ensureHandle("testbasicupdates"); /* Set alias to "Alice" */ const char *alias = "Alice"; tp_tests_contacts_connection_change_aliases( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &handle, &alias); /* Add it in the ContactList */ test_contact_list_manager_request_subscription(mListManager, 1, &handle, "wait"); TestExpectationContactPtr exp(new TestExpectationContact(EventAdded, "testbasicupdates")); exp->verifyAlias(alias); exp->verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN); exp->verifyAuthorization("Requested", "No"); runExpectation(exp); /* Change the presence to busy */ TpTestsContactsConnectionPresenceStatusIndex presence = TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY; const gchar *message = "Making coffee"; tp_tests_contacts_connection_change_presences( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &handle, &presence, &message); exp->setEvent(EventChanged); exp->verifyPresence(presence); runExpectation(exp); } void TestTelepathyPlugin::testSelfContact() { QSKIP("Self-contact's nickname is not updated on TP alias change"); const gchar *alias = "Unit Test"; tp_tests_contacts_connection_change_aliases( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &mConnService->self_handle, &alias); TestExpectationContactPtr exp(new TestExpectationContact(EventChanged)); exp->verifyAlias(alias); exp->verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_AVAILABLE); exp->verifyAvatar(QByteArray()); runExpectation(exp); } void TestTelepathyPlugin::testAuthorization() { #ifdef USING_QTPIM QSKIP("Presence authorization not supported by qtpim"); #endif TpHandle handle; /* Create a new contact "romeo" */ TestExpectationContactPtr exp = createContact("romeo", handle); /* Ask again for subscription, say "please" this time so it gets accepted */ test_contact_list_manager_request_subscription(mListManager, 1, &handle, "please"); exp->setEvent(EventChanged); exp->verifyAuthorization("Yes", "Requested"); runExpectation(exp); /* Create a new contact "juliette" */ exp = createContact("juliette", handle); /* Ask again for subscription, but this time it will be rejected */ test_contact_list_manager_request_subscription(mListManager, 1, &handle, "no"); exp->setEvent(EventChanged); exp->verifyAuthorization("No", "No"); runExpectation(exp); } GPtrArray *TestTelepathyPlugin::createContactInfoTel(const gchar *number) { GPtrArray *infoPtrArray; infoPtrArray = g_ptr_array_new_with_free_func((GDestroyNotify) g_value_array_free); const gchar *fieldValues[] = { number, NULL }; g_ptr_array_add (infoPtrArray, tp_value_array_build(3, G_TYPE_STRING, "tel", G_TYPE_STRV, NULL, G_TYPE_STRV, fieldValues, G_TYPE_INVALID)); return infoPtrArray; } void TestTelepathyPlugin::testContactInfo() { TpHandle handle; /* Create a contact with no ContactInfo */ TestExpectationContactPtr exp = createContact("testcontactinfo", handle); /* Set some ContactInfo on the contact */ GPtrArray *infoPtrArray = createContactInfoTel("123"); { const gchar *fieldValues[] = { "email@foo.com", NULL }; const gchar *fieldParams[] = { "type=work", "type=something totally wrong", "some garbage", NULL }; g_ptr_array_add (infoPtrArray, tp_value_array_build(3, G_TYPE_STRING, "email", G_TYPE_STRV, fieldParams, G_TYPE_STRV, fieldValues, G_TYPE_INVALID)); } tp_tests_contacts_connection_change_contact_info( TP_TESTS_CONTACTS_CONNECTION(mConnService), handle, infoPtrArray); exp->setEvent(EventChanged); exp->verifyInfo(infoPtrArray); runExpectation(exp); g_ptr_array_unref(infoPtrArray); /* Change the ContactInfo */ infoPtrArray = createContactInfoTel("456"); { const gchar *fieldValues[] = { "789", NULL }; const gchar *fieldParams[] = { "type=home", "type=cell", "type=video", NULL }; g_ptr_array_add (infoPtrArray, tp_value_array_build(3, G_TYPE_STRING, "tel", G_TYPE_STRV, fieldParams, G_TYPE_STRV, fieldValues, G_TYPE_INVALID)); } { const gchar *fieldValues[] = { "789", NULL }; const gchar *fieldParams[] = { "type=work", NULL }; g_ptr_array_add (infoPtrArray, tp_value_array_build(3, G_TYPE_STRING, "tel", G_TYPE_STRV, fieldParams, G_TYPE_STRV, fieldValues, G_TYPE_INVALID)); } tp_tests_contacts_connection_change_contact_info( TP_TESTS_CONTACTS_CONNECTION(mConnService), handle, infoPtrArray); exp->verifyInfo(infoPtrArray); runExpectation(exp); g_ptr_array_unref(infoPtrArray); } void TestTelepathyPlugin::testContactPhoneNumber() { TpHandle handle; /* Create a contact with no ContactInfo */ TestExpectationContactPtr exp = createContact("failphone", handle); /* Set some ContactInfo on the contact */ GPtrArray *infoPtrArray = createContactInfoTel("+8888"); tp_tests_contacts_connection_change_contact_info( TP_TESTS_CONTACTS_CONNECTION(mConnService), handle, infoPtrArray); exp->setEvent(EventChanged); exp->verifyInfo(infoPtrArray); runExpectation(exp); g_ptr_array_unref(infoPtrArray); /* Change the ContactInfo */ infoPtrArray = createContactInfoTel("+8887"); tp_tests_contacts_connection_change_contact_info( TP_TESTS_CONTACTS_CONNECTION(mConnService), handle, infoPtrArray); exp->verifyInfo(infoPtrArray); runExpectation(exp); g_ptr_array_unref(infoPtrArray); /* Change the ContactInfo */ infoPtrArray = createContactInfoTel("+8888"); tp_tests_contacts_connection_change_contact_info( TP_TESTS_CONTACTS_CONNECTION(mConnService), handle, infoPtrArray); exp->verifyInfo(infoPtrArray); runExpectation(exp); g_ptr_array_unref(infoPtrArray); } void TestTelepathyPlugin::testBug220851() { TpHandle handle; /* Create a contact with no ContactInfo */ TestExpectationContactPtr exp = createContact("testbug220851", handle); /* An address has 7 fields normally. Verify it's fine to give less */ GPtrArray *infoPtrArray = g_ptr_array_new_with_free_func((GDestroyNotify) g_value_array_free); const gchar *fieldValues[] = { "pobox", "extendedaddress", "street", NULL }; g_ptr_array_add (infoPtrArray, tp_value_array_build(3, G_TYPE_STRING, "adr", G_TYPE_STRV, NULL, G_TYPE_STRV, fieldValues, G_TYPE_INVALID)); tp_tests_contacts_connection_change_contact_info( TP_TESTS_CONTACTS_CONNECTION(mConnService), handle, infoPtrArray); /* QtContacts does not support extended address - it will be combined into the street address */ g_ptr_array_unref(infoPtrArray); infoPtrArray = g_ptr_array_new_with_free_func((GDestroyNotify) g_value_array_free); const gchar *resultValues[] = { "pobox", "extendedaddress\nstreet", NULL }; g_ptr_array_add (infoPtrArray, tp_value_array_build(3, G_TYPE_STRING, "adr", G_TYPE_STRV, NULL, G_TYPE_STRV, resultValues, G_TYPE_INVALID)); exp->setEvent(EventChanged); exp->verifyInfo(infoPtrArray); runExpectation(exp); g_ptr_array_unref(infoPtrArray); } void TestTelepathyPlugin::testRemoveContacts() { TpHandle handle; TestExpectationContactPtr exp = createContact("testremovecontacts", handle, true); test_contact_list_manager_remove(mListManager, 1, &handle); exp->setEvent(EventRemoved); runExpectation(exp); } void TestTelepathyPlugin::testRemoveBuddyDBusAPI() { // Create 2 contacts const char *buddy1 = "removebuddy1"; TpHandle handle1; TestExpectationContactPtr exp1 = createContact(buddy1, handle1); const char *buddy2 = "removebuddy2"; TpHandle handle2; TestExpectationContactPtr exp2 = createContact(buddy2, handle2); // Remove buddy1 when account is online BuddyManagementInterface *buddyIf = new BuddyManagementInterface("com.nokia.contactsd", "/telepathy", QDBusConnection::sessionBus(), 0); { QDBusPendingReply<> async = buddyIf->removeBuddies(ACCOUNT_PATH, QStringList() << buddy1); QDBusPendingCallWatcher watcher(async, this); watcher.waitForFinished(); QVERIFY2(not async.isError(), async.error().message().toLatin1()); QVERIFY(watcher.isValid()); } exp1->setEvent(EventRemoved); runExpectation(exp1); // Set account offline to test offline removal tp_cli_connection_call_disconnect(mConnection, -1, NULL, NULL, NULL, NULL); int count = mContactIds.count(); count *= 2; // Two contacts for each logical entity TestExpectationDisconnectPtr exp3(new TestExpectationDisconnect(count)); runExpectation(exp3); // Remove buddy2 when account is offline { QDBusPendingReply<> async = buddyIf->removeBuddies(ACCOUNT_PATH, QStringList() << buddy2); QDBusPendingCallWatcher watcher(async, this); watcher.waitForFinished(); QVERIFY2(not async.isError(), async.error().message().toLatin1()); QVERIFY(watcher.isValid()); } exp2->setEvent(EventRemoved); runExpectation(exp2); // FIXME: We should somehow verify that when setting account back online, // buddy2 gets removed from roster } void TestTelepathyPlugin::testInviteBuddyDBusAPI() { const QString buddy("invitebuddy"); BuddyManagementInterface *buddyIf = new BuddyManagementInterface("com.nokia.contactsd", "/telepathy", QDBusConnection::sessionBus(), 0); { QDBusPendingReply<> async = buddyIf->inviteBuddies(ACCOUNT_PATH, QStringList() << buddy); QDBusPendingCallWatcher watcher(async, this); watcher.waitForFinished(); QVERIFY2(not async.isError(), async.error().message().toLatin1()); QVERIFY(watcher.isValid()); } runExpectation(TestExpectationContactPtr(new TestExpectationContact(EventAdded, buddy))); } void TestTelepathyPlugin::testSetOffline() { createContact("testsetoffline", true); tp_cli_connection_call_disconnect(mConnection, -1, NULL, NULL, NULL, NULL); int count = mContactIds.count(); count *= 2; // Two contacts for each logical entity runExpectation(TestExpectationDisconnectPtr(new TestExpectationDisconnect(count))); } void TestTelepathyPlugin::testAvatar() { const gchar avatarData[] = "fake-avatar-data"; const gchar avatarToken[] = "fake-avatar-token"; const gchar avatarMimeType[] = "fake-avatar-mime-type"; /* Create a contact with an avatar */ TpHandle handle = ensureHandle("testavatar"); GArray *array = g_array_new(FALSE, FALSE, sizeof(gchar)); g_array_append_vals(array, avatarData, strlen(avatarData)); tp_tests_contacts_connection_change_avatar_data( TP_TESTS_CONTACTS_CONNECTION (mConnService), handle, array, avatarMimeType, avatarToken); g_array_unref(array); test_contact_list_manager_request_subscription(mListManager, 1, &handle, "please"); TestExpectationContactPtr exp(new TestExpectationContact(EventChanged, "testavatar")); exp->verifyAvatar(QByteArray(avatarData)); runExpectation(exp); /* Change avatar */ const gchar avatarData2[] = "fake-avatar-data-2"; const gchar avatarToken2[] = "fake-avatar-token-2"; const gchar avatarMimeType2[] = "fake-avatar-mime-type-2"; array = g_array_new(FALSE, FALSE, sizeof(gchar)); g_array_append_vals(array, avatarData2, strlen(avatarData2)); tp_tests_contacts_connection_change_avatar_data( TP_TESTS_CONTACTS_CONNECTION (mConnService), handle, array, avatarMimeType2, avatarToken2); g_array_unref(array); exp->setEvent(EventChanged); exp->verifyAvatar(QByteArray(avatarData2)); runExpectation(exp); /* Set back initial avatar */ array = g_array_new(FALSE, FALSE, sizeof(gchar)); g_array_append_vals(array, avatarData, strlen(avatarData)); tp_tests_contacts_connection_change_avatar_data( TP_TESTS_CONTACTS_CONNECTION (mConnService), handle, array, avatarMimeType, avatarToken); g_array_unref(array); exp->verifyAvatar(QByteArray(avatarData)); runExpectation(exp); /* Create another contact with the same avatar, they will share the same * nfo:FileObjectData in tracker */ TpHandle handle2 = ensureHandle("testavatar2"); array = g_array_new(FALSE, FALSE, sizeof(gchar)); g_array_append_vals(array, avatarData, strlen(avatarData)); tp_tests_contacts_connection_change_avatar_data( TP_TESTS_CONTACTS_CONNECTION (mConnService), handle2, array, avatarMimeType, avatarToken); g_array_unref(array); test_contact_list_manager_request_subscription(mListManager, 1, &handle2, "please"); TestExpectationContactPtr exp2(new TestExpectationContact(EventAdded, "testavatar2")); exp2->verifyAvatar(QByteArray(avatarData)); runExpectation(exp2); /* Change avatar of the new contact */ array = g_array_new(FALSE, FALSE, sizeof(gchar)); g_array_append_vals(array, avatarData2, strlen(avatarData2)); tp_tests_contacts_connection_change_avatar_data( TP_TESTS_CONTACTS_CONNECTION (mConnService), handle2, array, avatarMimeType2, avatarToken2); g_array_unref(array); exp2->setEvent(EventChanged); exp2->verifyAvatar(QByteArray(avatarData2)); runExpectation(exp2); /* Change the alias of first contact, this is to force fetching again * the contact, so verify the shared nfo:FileObjectData for the avatar is * still there */ const char *alias = "Where is my Avatar ?"; tp_tests_contacts_connection_change_aliases( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &handle, &alias); exp->verifyAlias(alias); runExpectation(exp); /* Remove avatar from first contact */ tp_tests_contacts_connection_change_avatar_data( TP_TESTS_CONTACTS_CONNECTION (mConnService), handle, NULL, NULL, NULL); exp->verifyAvatar(QByteArray()); runExpectation(exp); } void TestTelepathyPlugin::testDisable() { QSKIP("This test does not apply to qtpim using aggregation semantics"); /* Create a pure-im contact, it will have to be deleted */ createContact("testdisable-im"); /* Create an im contact and set it as local contact */ TestExpectationContactPtr exp = createContact("testdisable-local"); QContact contact = exp->contact(); contact.setCollectionId(collectionIdForName(QStringLiteral("addressbook"))); QContactSaveRequest *request = new QContactSaveRequest(); request->setContact(contact); startRequest(request); exp->setEvent(EventChanged); exp->verifyGenerator(collectionIdForName(QStringLiteral("addressbook"))); runExpectation(exp); tp_tests_simple_account_set_enabled (mAccount, FALSE); // self contact and testdisable-local gets updated, testdisable-im gets removed int changed = 2; int removed = 1; changed *= 2; // Two contacts for each logical entity removed *= 2; runExpectation(TestExpectationMassPtr(new TestExpectationMass(0, changed, removed))); mNOnlyLocalContacts++; /* FIXME: we can't verify leaked resource because some are qct's responsability */ mCheckLeakedResources = false; } void TestTelepathyPlugin::testIRIEncode() { /* Create a contact with a special id that could confuse tracker */ // FIXME: NB#257949 - disabled for now because qct does not decode the id // and test is failing. //createContact(""); } void TestTelepathyPlugin::testBug253679() { QSKIP("This test does not apply to qtpim using aggregation semantics"); const char *id = "testbug253679"; /* Create a local contact with an OnlineAccount */ QContact contact; QContactOnlineAccount onlineAccount; onlineAccount.setAccountUri(id); onlineAccount.setValue(QContactOnlineAccount__FieldAccountPath, QLatin1String(ACCOUNT_PATH)); contact.saveDetail(&onlineAccount); QContactSaveRequest *request = new QContactSaveRequest(); request->setContact(contact); startRequest(request); TestExpectationContactPtr exp(new TestExpectationContact(EventAdded, id)); exp->verifyGenerator(collectionIdForName(QStringLiteral("addressbook"))); runExpectation(exp); /* Now add the same OnlineAccount in our telepathy account, we expect this * to update existing contact and not create a new one */ TpHandle handle = ensureHandle(id); test_contact_list_manager_request_subscription(mListManager, 1, &handle, "wait"); const char *alias = "NB#253679"; tp_tests_contacts_connection_change_aliases( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &handle, &alias); exp->setEvent(EventChanged); exp->verifyAlias(alias); runExpectation(exp); /* FIXME: we can't verify leaked resource because some are qct's responsability */ mCheckLeakedResources = false; } void TestTelepathyPlugin::testMergedContact() { /* create new contact */ TpHandle handle1; TestExpectationContactPtr exp1 = createContact("merge1", handle1); /* create another contact */ TpHandle handle2; TestExpectationContactPtr exp2 = createContact("merge2", handle2); /* Merge contact2 into contact1 */ QContact contact1 = exp1->contact(); QContact contact2 = exp2->contact(); /* Explicit contact merging does not exist with qtcontacts-sqlite, and requires * direct use of qtcontacts-tracker-extensions. Disabled until refactoring is possible. */ #if 0 const QList mergeIds = QList() << contact2.localId(); mergeContacts(contact1, mergeIds); exp1->verifyContactId(contact1); exp1->verifyGenerator("telepathy"); exp2->verifyContactId(contact1); exp2->verifyGenerator("telepathy"); const QList expectations = QList() << exp1 << exp2; runExpectation(TestExpectationMergePtr(new TestExpectationMerge(contact1.localId(), mergeIds, expectations))); #endif /* Change presence of contact1, verify it modify the global presence */ TpTestsContactsConnectionPresenceStatusIndex presence = TP_TESTS_CONTACTS_CONNECTION_STATUS_BUSY; const gchar *message = "Testing merged contact"; tp_tests_contacts_connection_change_presences( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &handle1, &presence, &message); TestExpectationContactPtr exp4(new TestExpectationContact(EventChanged)); exp4->verifyContactId(contact1); exp4->verifyPresence(presence); exp4->skipUnlessGeneratorIs(collectionIdForName(QStringLiteral("telepathy"))); runExpectation(exp4); #if 0 /* Change alias of contact2, verify it modify the global nickname */ const char *alias = "Merged contact 2"; tp_tests_contacts_connection_change_aliases( TP_TESTS_CONTACTS_CONNECTION (mConnService), 1, &handle2, &alias); exp4.verifyAlias(alias); runExpectation(&exp4); /* NB#223264 - Contactsd incorrectly handle the removal of merged IM contacts */ tp_tests_simple_account_disabled (mAccount, FALSE); QContactFetchRequest *request = new QContactFetchRequest(); request->setManager(mContactManager); QContactLocalIdFilter filter; filter.setIds(QList() << contact1.localId()); request->setFilter(filter); connect(request, SIGNAL(resultsAvailable()), SLOT(onContactsFetched())); QVERIFY(request->start()); #endif } void TestTelepathyPlugin::startRequest(QContactAbstractRequest *request) { connect(request, SIGNAL(stateChanged(QContactAbstractRequest::State)), SLOT(requestStateChanged(QContactAbstractRequest::State))); request->setManager(mContactManager); QVERIFY(request->start()); } QContactCollectionId TestTelepathyPlugin::collectionIdForName(const QString &name) { const QList collections = mContactManager->collections(); for (const QContactCollection &collection : collections) { if (collection.metaData(QContactCollection::KeyName).toString() == name) { return collection.id(); } } return QContactCollectionId(); } void TestTelepathyPlugin::requestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::InactiveState || newState == QContactAbstractRequest::ActiveState) return; QContactAbstractRequest *request = qobject_cast(sender()); QVERIFY(request != 0); QVERIFY(request->isFinished()); QCOMPARE(request->error(), QContactManager::NoError); request->deleteLater(); } void TestTelepathyPlugin::onContactsFetched() { QContactFetchRequest *req = qobject_cast(sender()); QVERIFY(req); QVERIFY(req->isFinished()); QCOMPARE (req->error(), QContactManager::NoError); QVERIFY(req->contacts().count() >= 1); } #define LETTERS "abcdefghijklmnopqrstuvwxyz" #define NUMBERS "0123456789" #define ALNUM LETTERS NUMBERS #define TEXT ALNUM " " static gchar *randomString(int len, const gchar *chars = LETTERS) { gchar *result = g_new0(gchar, len + 1); for (int i = 0; i < len; i++) { result[i] = chars[qrand() % strlen(chars)]; } return result; } #define N_CONTACTS 100 void TestTelepathyPlugin::testBenchmark() { /* create lots of new contacts */ GArray *handles = g_array_new(FALSE, FALSE, sizeof(TpHandle)); for (int i = 0; i < N_CONTACTS; i++) { TpHandle handle = ensureHandle(randomString(20)); g_array_append_val(handles, handle); } test_contact_list_manager_request_subscription(mListManager, handles->len, (TpHandle *) handles->data, "wait"); int added = N_CONTACTS; added *= 2; // Two contacts for each logical entity runExpectation(TestExpectationMassPtr(new TestExpectationMass(added, 0, 0))); /* Set account offline */ tp_cli_connection_call_disconnect(mConnection, -1, NULL, NULL, NULL, NULL); int count = mContactIds.count(); count *= 2; // Two contacts for each logical entity runExpectation(TestExpectationDisconnectPtr(new TestExpectationDisconnect(count))); } TpHandle TestTelepathyPlugin::ensureHandle(const gchar *id) { TpHandleRepoIface *serviceRepo = tp_base_connection_get_handles(mConnService, TP_HANDLE_TYPE_CONTACT); TpHandle handle = tp_handle_ensure(serviceRepo, id, NULL, NULL); return handle; } TestExpectationContactPtr TestTelepathyPlugin::createContact(const gchar *id, TpHandle &handle, bool please) { handle = ensureHandle(id); test_contact_list_manager_request_subscription(mListManager, 1, &handle, please ? "please" : "wait"); TestExpectationContactPtr exp(new TestExpectationContact(EventAdded, id)); if (please) { exp->verifyAuthorization("Yes", "Requested"); } else { exp->verifyAuthorization("Requested", "No"); } exp->verifyPresence(TP_TESTS_CONTACTS_CONNECTION_STATUS_UNKNOWN); exp->verifyGenerator(collectionIdForName(QStringLiteral("telepathy"))); runExpectation(exp); return exp; } TestExpectationContactPtr TestTelepathyPlugin::createContact(const gchar *id, bool please) { TpHandle handle; return createContact(id, handle, please); } void TestTelepathyPlugin::runExpectation(TestExpectationPtr exp) { QVERIFY(mExpectation.isNull()); mExpectation = exp; mExpectation->setContactManager(mContactManager); connect(mExpectation.data(), SIGNAL(finished()), mLoop, SLOT(quit())); QCOMPARE(mLoop->exec(), 0); // Wait for any further signals to be emitted from the contact manager QTest::qWait(500); mExpectation = TestExpectationPtr(); } void TestTelepathyPlugin::contactsAdded(const QList& contactIds) { qCDebug(lcContactsd) << "Got contactsAdded"; Q_FOREACH (const QContactId &id, contactIds) { QVERIFY(!mContactIds.contains(id)); mContactIds << id; } verify(EventAdded, contactIds); } void TestTelepathyPlugin::contactsChanged(const QList& contactIds, const QList &) { qCDebug(lcContactsd) << "Got contactsChanged"; Q_FOREACH (const QContactId &id, contactIds) { if (!mContactIds.contains(id)) { qCWarning(lcContactsd) << "Unknown contact ID changed:" << id; mContactIds << id; } } verify(EventChanged, contactIds); } void TestTelepathyPlugin::contactsRemoved(const QList& contactIds) { qCDebug(lcContactsd) << "Got contactsRemoved"; Q_FOREACH (const QContactId &id, contactIds) { if (!mContactIds.contains(id)) { qCWarning(lcContactsd) << "Unknown contact ID removed:" << id; } else { mContactIds.removeOne(id); } } verify(EventRemoved, contactIds); } void TestTelepathyPlugin::verify(Event event, const QList &contactIds) { QVERIFY(mExpectation != 0); mExpectation->verify(event, contactIds); } CONTACTSD_TEST_MAIN(TestTelepathyPlugin) contactsd-1.4.14/tests/ut_telepathyplugin/test-telepathy-plugin.h000066400000000000000000000072051440357512200253030ustar00rootroot00000000000000/** This file is part of Contacts daemon ** ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public License ** version 2.1 as published by the Free Software Foundation and appearing in the ** file LICENSE.LGPL included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License version ** 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional rights. ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included ** in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. **/ #ifndef TEST_TELEPATHY_PLUGIN_H #define TEST_TELEPATHY_PLUGIN_H #include #include #include #include #include #include #include #include "libtelepathy/contacts-conn.h" #include "libtelepathy/contact-list-manager.h" #include "libtelepathy/simple-account-manager.h" #include "libtelepathy/simple-account.h" #include "test.h" #include "test-expectation.h" QTCONTACTS_USE_NAMESPACE /** * Telepathy plugin's unit test */ class TestTelepathyPlugin : public Test { Q_OBJECT Q_PLUGIN_METADATA(IID "org.nemomobile.contactsd.test-telepathy") public: TestTelepathyPlugin(QObject *parent = 0); protected Q_SLOTS: void contactsAdded(const QList& contactIds); void contactsChanged(const QList& contactIds, const QList &typesChanged); void contactsRemoved(const QList& contactIds); void onContactsFetched(); void requestStateChanged(QContactAbstractRequest::State newState); private Q_SLOTS: void initTestCase(); void init(); /* Generic tests */ void testBasicUpdates(); void testSelfContact(); void testAuthorization(); void testContactInfo(); void testContactPhoneNumber(); void testRemoveContacts(); void testRemoveBuddyDBusAPI(); void testInviteBuddyDBusAPI(); void testSetOffline(); void testAvatar(); void testDisable(); /* Specific tests */ void testBug253679(); void testMergedContact(); void testBug220851(); void testIRIEncode(); /* Benchmark */ void testBenchmark(); void cleanup(); void cleanupTestCase(); private: TpHandle ensureHandle(const gchar *id); TestExpectationContactPtr createContact(const gchar *id, TpHandle &handle, bool please = false); TestExpectationContactPtr createContact(const gchar *id, bool please = false); GPtrArray *createContactInfoTel(const gchar *number); void verify(Event event, const QList &contactIds); void runExpectation(TestExpectationPtr expectation); void startRequest(QContactAbstractRequest *request); QContactCollectionId collectionIdForName(const QString &name); private: QContactManager *mContactManager; TpTestsSimpleAccountManager *mAccountManager; TpTestsSimpleAccount *mAccount; TpBaseConnection *mConnService; TpConnection *mConnection; TestContactListManager *mListManager; QList mContactIds; int mNOnlyLocalContacts; TestExpectationPtr mExpectation; bool mCheckLeakedResources; }; #endif contactsd-1.4.14/tests/ut_telepathyplugin/test.cpp000066400000000000000000000054461440357512200223520ustar00rootroot00000000000000#include "test.h" #include #include #include #include #include #include #include #include using Tp::PendingOperation; using Tp::PendingVoid; using Tp::Client::DBus::PeerInterface; Test::Test(QObject *parent) : QObject(parent), mLoop(new QEventLoop(this)) { QTimer::singleShot(10 * 60 * 1000, this, SLOT(onWatchdog())); } Test::~Test() { delete mLoop; } void Test::initTestCaseImpl() { Tp::registerTypes(); Tp::enableDebug(true); Tp::enableWarnings(true); QVERIFY(QDBusConnection::sessionBus().isConnected()); } void Test::initImpl() { } void Test::cleanupImpl() { } void Test::cleanupTestCaseImpl() { // To allow for cleanup code to run (e.g. PendingOperation cleanup after they finish) mLoop->processEvents(); } void Test::expectSuccessfulCall(PendingOperation *op) { if (op->isError()) { qWarning().nospace() << op->errorName() << ": " << op->errorMessage(); mLoop->exit(1); return; } mLoop->exit(0); } void Test::expectSuccessfulCall(QDBusPendingCallWatcher *watcher) { if (watcher->isError()) { qWarning().nospace() << watcher->error().name() << ": " << watcher->error().message(); mLoop->exit(1); return; } mLoop->exit(0); } void Test::expectFailure(PendingOperation *op) { if (!op->isError()) { qWarning() << "expectFailure(): should have been an error, but wasn't"; mLoop->exit(1); return; } mLoop->exit(0); } void Test::expectSuccessfulProperty(PendingOperation *op) { if (op->isError()) { qWarning().nospace() << op->errorName() << ": " << op->errorMessage(); mPropertyValue = QVariant(); mLoop->exit(1); } else { Tp::PendingVariant *pv = qobject_cast(op); mPropertyValue = pv->result(); mLoop->exit(0); } } void Test::processDBusQueue(Tp::DBusProxy *proxy) { // Call method Ping on the D-Bus Peer interface PeerInterface peer(proxy); PendingVoid *call = new PendingVoid(peer.Ping(), Tp::SharedPtr()); // Wait for the reply to the Ping call while (!call->isFinished()) { mLoop->processEvents(); } QVERIFY(call->isFinished()); QVERIFY(call->isValid()); // Do one more processEvents so the PendingVoid is always freed mLoop->processEvents(); } void Test::onWatchdog() { // We can't use QFAIL because the test would then go to cleanup() and/or cleanupTestCase(), // which would often hang too - so let's just use abort qWarning() << "Test took over 10 minutes to finish, it's probably hung up - aborting"; std::abort(); } contactsd-1.4.14/tests/ut_telepathyplugin/test.h000066400000000000000000000025071440357512200220120ustar00rootroot00000000000000#include #include #include #include #include namespace Tp { class DBusProxy; } class Test : public QObject { Q_OBJECT public: Test(QObject *parent = 0); virtual ~Test(); QEventLoop *mLoop; void processDBusQueue(Tp::DBusProxy *proxy); protected: template bool waitForProperty(Tp::PendingVariant *pv, T *value); protected Q_SLOTS: void expectSuccessfulCall(QDBusPendingCallWatcher*); void expectSuccessfulCall(Tp::PendingOperation*); void expectFailure(Tp::PendingOperation*); void expectSuccessfulProperty(Tp::PendingOperation *op); void onWatchdog(); virtual void initTestCaseImpl(); virtual void initImpl(); virtual void cleanupImpl(); virtual void cleanupTestCaseImpl(); private: // The property retrieved by expectSuccessfulProperty() QVariant mPropertyValue; }; template bool Test::waitForProperty(Tp::PendingVariant *pv, T *value) { connect(pv, SIGNAL(finished(Tp::PendingOperation*)), SLOT(expectSuccessfulProperty(Tp::PendingOperation*))); if (mLoop->exec() == 0) { *value = qdbus_cast(mPropertyValue); return true; } else { *value = T(); return false; } } contactsd-1.4.14/tests/ut_telepathyplugin/tests.pri000066400000000000000000000040461440357512200225400ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. daemon.target = with-daemon.sh daemon.depends = $$PWD/with-daemon.sh.in daemon.path = /opt/tests/$${PACKAGENAME} daemon.commands = \ sed -e \"s,@BINDIR@,$$BINDIR,g\" \ -e \"s,@PLUGINDIR@,$$LIBDIR/$${PACKAGENAME}-1.0/plugins,g\" \ $< > $@ && chmod +x $@ || rm -f $@ wrapper.target = ut_telepathyplugin-wrapper.sh wrapper.depends = $$PWD/ut_telepathyplugin-wrapper.sh.in wrapper.path = /opt/tests/$${PACKAGENAME}/ut_telepathyplugin wrapper.commands = \ sed -e \"s,@SCRIPTDIR@,/opt/tests/$${PACKAGENAME},g\" \ -e \"s,@BINDIR@,$$BINDIR,g\" \ -e \"s,@WITH_DAEMON@,$$daemon.target,g\" \ $< > $@ && chmod +x $@ || rm -f $@ install_extrascripts.files = $$wrapper.target $$daemon.target install_extrascripts.path = /opt/tests/$${PACKAGENAME}/ut_telepathyplugin install_extrascripts.depends = daemon wrapper install_extrascripts.CONFIG = no_check_exist QMAKE_INSTALL_FILE = cp -p QMAKE_EXTRA_TARGETS += daemon wrapper QMAKE_CLEAN += $$daemon.target $$wrapper.target PRE_TARGETDEPS += $$daemon.target $$wrapper.target INSTALLS += install_extrascripts contactsd-1.4.14/tests/ut_telepathyplugin/ut_telepathyplugin-wrapper.sh.in000066400000000000000000000026761440357512200272360ustar00rootroot00000000000000#! /bin/sh # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. tmpdir=$(mktemp -d) trap "rm -rf $tmpdir" INT TERM EXIT export XDG_DATA_HOME=$tmpdir/local export XDG_CACHE_HOME=$tmpdir/cache export XDG_CONFIG_HOME=$tmpdir/config mkdir -p $XDG_CONFIG_HOME/tracker cat <$XDG_CONFIG_HOME/tracker/tracker-store.cfg [General] Verbosity=3 EOF @SCRIPTDIR@/with-session-bus.sh --config-file=@SCRIPTDIR@/session.conf -- \ @SCRIPTDIR@/ut_telepathyplugin/@WITH_DAEMON@ \ @SCRIPTDIR@/ut_telepathyplugin/ut_telepathyplugin $@ contactsd-1.4.14/tests/ut_telepathyplugin/ut_telepathyplugin.pro000066400000000000000000000073671440357512200253430ustar00rootroot00000000000000# This file is part of Contacts daemon # # Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. include(../common/test-common.pri) PRE_TARGETDEPS += ../libtelepathy/libtelepathy.a TARGET = ut_telepathyplugin target.path = /opt/tests/$${PACKAGENAME} include(check.pri) include(tests.pri) TEMPLATE = app CONFIG += test qt QT += testlib dbus QT -= gui CONFIG += link_pkgconfig DEFINES += QT_NO_KEYWORDS PKGCONFIG += Qt5Contacts qtcontacts-sqlite-qt5-extensions TelepathyQt5 telepathy-glib dbus-glib-1 gio-2.0 system(cp $$PWD/../../plugins/telepathy/com.nokia.contacts.buddymanagement.xml .) system(qdbusxml2cpp -c BuddyManagementInterface -p buddymanagementinterface.h:buddymanagementinterface.cpp com.nokia.contacts.buddymanagement.xml) INCLUDEPATH += .. QMAKE_LIBDIR += ../libtelepathy LIBS += -ltelepathy CONFIG(coverage): { QMAKE_CXXFLAGS += -c -g --coverage -ftest-coverage -fprofile-arcs LIBS += -lgcov } HEADERS += \ test-telepathy-plugin.h \ test-expectation.h \ test.h \ buddymanagementinterface.h SOURCES += \ test-telepathy-plugin.cpp \ test-expectation.cpp \ test.cpp \ buddymanagementinterface.cpp #for gcov stuff CONFIG(coverage): { INCLUDEPATH += $$TOP_SOURCEDIR/src HEADERS += $$TOP_SOURCEDIR/plugins/telepathy/cdtpaccount.h \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpcontact.h \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpcontroller.h \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpplugin.h \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpstorage.h \ $$TOP_SOURCEDIR/plugins/telepathy/buddymanagementadaptor.h \ $$TOP_SOURCEDIR/plugins/telepathy/redliststorage.h SOURCES += $$TOP_SOURCEDIR/plugins/telepathy/cdtpaccount.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpcontact.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpcontroller.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpplugin.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/cdtpstorage.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/redliststorage.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/buddymanagementadaptor.cpp \ $$TOP_SOURCEDIR/plugins/telepathy/redliststorage.cpp #to use start the daemon from code HEADERS += $$TOP_SOURCEDIR/src/contactsd.h \ $$TOP_SOURCEDIR/src/contactsimportprogressadaptor.h \ $$TOP_SOURCEDIR/src/contactsdpluginloader.h \ $$TOP_SOURCEDIR/src/importstate.h SOURCES += $$TOP_SOURCEDIR/src/contactsd.cpp \ $$TOP_SOURCEDIR/src/contactsimportprogressadaptor.cpp \ $$TOP_SOURCEDIR/src/contactsdpluginloader.cpp \ $$TOP_SOURCEDIR/src/importstate.cpp DEFINES += CONTACTSD_PLUGINS_DIR=\\\"$$TOP_SOURCEDIR/plugins/telepathy/\\\" QMAKE_CLEAN += *.gcov $(OBJECTS_DIR)*.gcda $(OBJECTS_DIR)*.gcno gcov.analysis gcov.analysis.summary gcov.target = gcov gcov.CONFIG = recursive gcov.commands += for d in $$SOURCES; do (gcov -a -c -o $(OBJECTS_DIR) \$$$$d >> gcov.analysis ); done; gcov.depends = $(TARGET) QMAKE_EXTRA_TARGETS += gcov } INSTALLS += target contactsd-1.4.14/tests/ut_telepathyplugin/ut_telepathyplugin.skip000066400000000000000000000000001440357512200254620ustar00rootroot00000000000000contactsd-1.4.14/tests/ut_telepathyplugin/with-daemon.sh.in000066400000000000000000000032511440357512200240340ustar00rootroot00000000000000#! /bin/sh # This file is part of Contacts daemon # # Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). # # Contact: Nokia Corporation (info@qt.nokia.com) # # GNU Lesser General Public License Usage # This file may be used under the terms of the GNU Lesser General Public License # version 2.1 as published by the Free Software Foundation and appearing in the # file LICENSE.LGPL included in the packaging of this file. Please review the # following information to ensure the GNU Lesser General Public License version # 2.1 requirements will be met: # http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. # # In addition, as a special exception, Nokia gives you certain additional rights. # These rights are described in the Nokia Qt LGPL Exception version 1.1, included # in the file LGPL_EXCEPTION.txt in this package. # # Other Usage # Alternatively, this file may be used in accordance with the terms and # conditions contained in a signed written agreement between you and Nokia. set -e cleanup () { kill $contactsd_pid tracker-control -r > /dev/null } trap cleanup INT HUP TERM tracker-control -r > /dev/null /usr/lib/tracker/tracker-store >/dev/null 2>&1 & # Start the unit test in background so it register the AM "$@" & ut_pid=$! # Wait for AM to appear on the bus mc-wait-for-name "org.freedesktop.Telepathy.AccountManager" # Start Contacts Daemon in background export CONTACTSD_PLUGINS_DIRS=@PLUGINDIR@ export CONTACTSD_DIRECT_GC=1 # We load only the needed plugins (to avoid eg. voicemail creating contacts) @BINDIR@/contactsd --plugins telepathy & contactsd_pid=$! # wait for unit test to finish e=0 wait $ut_pid || e=$? trap - INT HUP TERM cleanup exit $e contactsd-1.4.14/tests/with-session-bus.sh000077500000000000000000000045261440357512200205210ustar00rootroot00000000000000#!/bin/sh # with-session-bus.sh - run a program with a temporary D-Bus session daemon # # The canonical location of this program is the telepathy-glib tools/ # directory, please synchronize any changes with that copy. # # Copyright (C) 2007-2008 Collabora Ltd. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. set -e me=with-session-bus dbus_daemon_args="--print-address=5 --print-pid=6 --fork" sleep=0 usage () { echo "usage: $me [options] -- program [program_options]" >&2 echo "Requires write access to the current directory." >&2 echo "" >&2 echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2 echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2 echo "The output of dbus-monitor is saved in $me-.dbus-monitor-logs" >&2 exit 2 } while test "z$1" != "z--"; do case "$1" in --sleep=*) sleep="$1" sleep="${sleep#--sleep=}" shift ;; --session) dbus_daemon_args="$dbus_daemon_args --session" shift ;; --config-file=*) # FIXME: assumes config file doesn't contain any special characters dbus_daemon_args="$dbus_daemon_args $1" shift ;; *) usage ;; esac done shift if test "z$1" = "z"; then usage; fi exec 5> $me-$$.address exec 6> $me-$$.pid cleanup () { pid=`head -n1 $me-$$.pid` if test -n "$pid" ; then if [ -n "$VERBOSE_TESTS" ]; then echo "Killing temporary bus daemon: $pid" >&2 fi kill -INT "$pid" fi rm -f $me-$$.address rm -f $me-$$.pid } trap cleanup INT HUP TERM dbus-daemon $dbus_daemon_args if [ -n "$VERBOSE_TESTS" ]; then { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2 { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2 fi e=0 DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" export DBUS_SESSION_BUS_ADDRESS DBUS_SESSION_BUS_PID="`cat $me-$$.pid`" export DBUS_SESSION_BUS_PID if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ > $me-$$.dbus-monitor-logs 2>&1 & fi "$@" || e=$? if test $sleep != 0; then sleep $sleep fi trap - INT HUP TERM cleanup exit $e contactsd-1.4.14/translations/000077500000000000000000000000001440357512200163075ustar00rootroot00000000000000contactsd-1.4.14/translations/translations.pro000066400000000000000000000047141440357512200215600ustar00rootroot00000000000000# CONFIG PARAMS -------------------------------------------------------------- INSTALL_PREFIX = /usr # Name of the catalog: CATALOGNAME = contactsd # Paths of source code files: SOURCEPATHS = $${_PRO_FILE_PWD_}/../src $${_PRO_FILE_PWD_}/../plugins # Installation paths: QM_INSTALL_PATH = $${INSTALL_PREFIX}/share/translations/ TS_INSTALL_PATH = $${INSTALL_PREFIX}/share/translations/source/ #----------------------------------------------------------------------------- # DO NOT EDIT THIS PART ------------------------------------------------------ #----------------------------------------------------------------------------- TEMPLATE = aux SUBDIRS = CONFIG = warn_on # "unarm" the primary target generation by disabling linking QMAKE_LFLAGS = --version # translation input/output TS_FILENAME = $${_PRO_FILE_PWD_}/$${CATALOGNAME}.ts QM_FILENAME = $${_PRO_FILE_PWD_}/$${CATALOGNAME}.qm # LUPDATE and LRELEASE -------------------------------------------------------- LUPDATE_CMD = lupdate \ $${SOURCEPATHS} \ -ts $${TS_FILENAME} && \ lrelease \ -idbased \ $${TS_FILENAME} \ -qm $${QM_FILENAME} # extra target for generating the .qm file lupdate.target = .lupdate lupdate.commands = $$LUPDATE_CMD QMAKE_EXTRA_TARGETS += lupdate PRE_TARGETDEPS += .lupdate # installation target for the .ts file tsfile.CONFIG += no_check_exist no_link tsfile.files = $${TS_FILENAME} tsfile.path = $${TS_INSTALL_PATH} tsfile.target = tsfile INSTALLS += tsfile # installation target for the .qm file qmfile.CONFIG += no_check_exist no_link qmfile.files = $${QM_FILENAME} qmfile.path = $${QM_INSTALL_PATH} qmfile.target = qmfile INSTALLS += qmfile QMAKE_CLEAN += $${TS_FILENAME} $${QM_FILENAME} # Engineering english fallback ---------------------- EE_QM_FILENAME = $${_PRO_FILE_PWD_}/$${CATALOGNAME}_eng_en.qm engineering_english.commands += lrelease -idbased $${TS_FILENAME} -qm $${EE_QM_FILENAME} engineering_english.CONFIG += no_check_exist no_link engineering_english.depends = lupdate engineering_english.input = $${TS_FILENAME} engineering_english.output = $${EE_QM_FILENAME} engineering_english_install.path = $${QM_INSTALL_PATH} engineering_english_install.files = $${EE_QM_FILENAME} engineering_english_install.CONFIG += no_check_exist QMAKE_EXTRA_TARGETS += engineering_english PRE_TARGETDEPS += engineering_english INSTALLS += engineering_english_install # End of File