diff -Nru akonadi-1.1.1/AkonadiConfig.cmake.in akonadi-1.2.0/AkonadiConfig.cmake.in --- akonadi-1.1.1/AkonadiConfig.cmake.in 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/AkonadiConfig.cmake.in 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,36 @@ +# AkonadiConfig.cmake is generated by CMake from akonadi/AkonadiConfig.cmake.in. +# Any changed value in this file will be overwritten by CMake. + +# set the akonadi version number +set(AKONADI_VERSION_MAJOR @AKONADI_VERSION_MAJOR@) +set(AKONADI_VERSION_MINOR @AKONADI_VERSION_MINOR@) +set(AKONADI_VERSION_PATCH @AKONADI_VERSION_PATCH@) +set(AKONADI_VERSION @AKONADI_VERSION@) +set(AKONADI_VERSION_STRING "@AKONADI_VERSION_STRING@") + +# set the directories +if(NOT AKONADI_INSTALL_DIR) + set(AKONADI_INSTALL_DIR "@CMAKE_INSTALL_PREFIX@") +endif(NOT AKONADI_INSTALL_DIR) + +set(AKONADI_BIN_DIR "@AKONADI_BIN_DIR@") +set(AKONADI_CONFIG_DIR "@AKONADI_CONFIG_DIR@") +set(AKONADI_DBUS_INTERFACES_DIR "@AKONADI_DBUS_INTERFACES_DIR@") +set(AKONADI_DBUS_SERVICES_DIR "@AKONADI_DBUS_SERVICES_DIR@") +set(AKONADI_INCLUDE_DIR "@AKONADI_INCLUDE_DIR@") +set(AKONADI_LIB_DIR "@AKONADI_LIB_DIR@") +set(AKONADI_SHARE_DIR "@SHARE_INSTALL_PREFIX@") +set(AKONADI_XDG_MIME_INSTALL_DIR "@AKONADI_XDG_MIME_DIR@") + +# Compatibility +if(WIN32) +if(MINGW) + set(AKONADI_COMMON_LIBRARIES "@AKONADI_LIB_DIR@/libakonadiprotocolinternals.dll.a") +else(MINGW) + set(AKONADI_COMMON_LIBRARIES "@AKONADI_LIB_DIR@/akonadiprotocolinternals.lib") +endif(MINGW) +elseif(APPLE) + set(AKONADI_COMMON_LIBRARIES "@AKONADI_LIB_DIR@/libakonadiprotocolinternals.dylib") +else() + set(AKONADI_COMMON_LIBRARIES "@AKONADI_LIB_DIR@/libakonadiprotocolinternals.so") +endif() diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/akonadi.pc.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/akonadi.pc.cmake --- akonadi-1.1.1/akonadi.pc.cmake 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/akonadi.pc.cmake 2009-07-28 17:52:13.000000000 +0100 @@ -5,7 +5,7 @@ Name: Akonadi Description: Akonadi server and infrastructure needed to build client libraries and applications -Version: @AKONADI_LIB_VERSION_STRING@ +Version: @AKONADI_VERSION_STRING@ Requires: QtCore QtSql QtDBus Libs: -L${libdir} -lakonadiprotocolinternals Cflags: -I${includedir} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/ChangeLog /tmp/x5zlUfQlMV/akonadi-1.2.0/ChangeLog --- akonadi-1.1.1/ChangeLog 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/ChangeLog 2009-07-28 17:52:13.000000000 +0100 @@ -1,32 +1,1681 @@ -2009-01-16 09:46 +0000 [r911844] Volker Krause vkrause@kde.org +2009-07-28 16:34 +0000 [r1003699] Volker Krause vkrause@kde.org - * branches/akonadi/1.1/server/shared/akdebug.cpp: Backport SVN - commit 908591 by vkrause from trunk: Don't put essential code - into assert(), doesn't seem to be executed in release builds. + * branches/akonadi/1.2/CMakeLists.txt, + branches/akonadi/1.2/cmake/modules/FindQt4.cmake: Backport SVN + commit 993358 by mikearthur from trunk: Add QT_USE_* options to + fix building on OSX with Qt SDK. Backport SVN commit 993403 by + mikearthur from trunk: Add missing QtDBus Framework include for + Qt SDK compilation on OSX. Backported from CMake 2.6.4. -2009-01-09 21:34 +0000 [r908531] Tom Albers tomalbers@kde.nl +2009-07-28 16:31 +0000 [r1003694] Volker Krause vkrause@kde.org - * branches/akonadi/1.1/CMakeLists.txt, - branches/akonadi/1.1/cmake/modules/FindBoost.cmake (removed): - Backport: SVN commit 906112 by dfaure: Require cmake-2.6.0 and - its much nicer FindBoost.cmake + * branches/akonadi/1.2/server/control/CMakeLists.txt: Backport SVN + commit 992351 by pokrzywka from trunk: run akonadi_control + silently on windows (no console popup) Backport SVN commit 992355 + by pokrzywka: one WIN32 flag is enough -2009-01-04 22:14 +0000 [r905716] Tom Albers tomalbers@kde.nl +2009-07-28 16:27 +0000 [r1003692] Volker Krause vkrause@kde.org - * branches/akonadi/1.1/cmake/modules/FindBoost.cmake: Backport SVN - commit 905714 by sengels: add 1.37 release + * branches/akonadi/1.2/server/src/handlerhelper.cpp: Backport SVN + commit 986123 by vkrause from trunk: Join collection attributes + correctly. Not that it makes any difference for the parser, but + it's much less confusing to read for humans in the akonadiconsole + debugger. -2009-01-04 16:26 +0000 [r905559] Volker Krause vkrause@kde.org +2009-07-28 16:24 +0000 [r1003689] Volker Krause vkrause@kde.org - * branches/akonadi/1.1/server/control/agentmanager.cpp: Backport - SVN commit 905553 by vkrause from trunk: Don't try to restart an - agent that has been deleted. + * branches/akonadi/1.2/makechangelog: Create changelog for the + correct branch. -2009-01-03 16:17 +0000 [r905029] Tom Albers tomalbers@kde.nl +2009-06-23 19:13 +0000 [r985928] Volker Krause vkrause@kde.org - * branches/akonadi/1.1 (added): Branch off Akonadi 1.1 + * branches/akonadi/1.2 (added): Create Akonadi 1.2 branch. -2009-01-03 16:10 +0000 [r905016] Tom Albers tomalbers@kde.nl +2009-06-23 18:27 +0000 [r985910] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/ChangeLog, + trunk/kdesupport/akonadi/NEWS, + trunk/kdesupport/akonadi/CMakeLists.txt: Prepare for the 1.1.95 + release. + +2009-06-23 16:03 +0000 [r985873] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handlerhelper.cpp, + trunk/kdesupport/akonadi/server/src/handlerhelper.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp: Correctly + check if the new cache policy really differs from the old one, + and only then send a change notification. + +2009-06-23 09:29 +0000 [r985611] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp: Less + brutal way to update collection content types. Avoids a whole lot + of unecessary database writes and thus change notifications. + +2009-06-21 19:06 +0000 [r984939] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.h, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: Also + allow to delete collections based on their remote identifier. + +2009-06-21 01:44 +0000 [r984547] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/libs/imapparser.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: When + creating a collection with CR and/or LF in attribute values, the + CollectionCreateJob gets stuck forever. That's because ImapParser + doesn't quote CR and LF at all when creating the quoted string + for the attribute value. According to [1], CR and LF are not + allowed inside a quoted string. This patch fixes this by escaping + LF as "\n" and CR as "\r" in ImapParser::quote() and resolving + them again in ImapParser::parseQuotedString and + ImapStreamParser;:parseQuotedString(). Approved by Volker. + +2009-06-19 17:57 +0000 [r984003] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/collectionqueryhelper.cpp: + Selection context is only relevant for items, so ignore it here. + +2009-06-19 17:45 +0000 [r984000] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/libs/tests/CMakeLists.txt: find the + .moc, add binary dir to include path + +2009-06-19 16:39 +0000 [r983981] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.cpp: oops, fix + creation of new collection attributes + +2009-06-19 15:35 +0000 [r983965] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/libs/tests/notificationmessagetest.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/entities-source.xsl, + trunk/kdesupport/akonadi/libs/tests/notificationmessagetest.h, + trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.cpp, + trunk/kdesupport/akonadi/server/src/storage/entities-header.xsl, + trunk/kdesupport/akonadi/server/src/handler/move.cpp, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.h, + trunk/kdesupport/akonadi/libs/notificationmessage.cpp: Improve + the MODIFY command handler to trigger less database writes and + thus change notifications, about 30% less during a normal IMAP + sync. + +2009-06-19 15:14 +0000 [r983962] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/libs/tests/CMakeLists.txt (added): add + missing file + +2009-06-18 23:23 +0000 [r983741] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Change the logic when + we're running CMake in kdesupport: * We will use Soprano from + kdesupport even if some headers are installed * We will look for + installed headers if we're not running CMake in kdesupport _OR_ + if BUILD_Soprano is false (ie: it is explicitely disabled or was + excluded from the svn checkout) This should cover all the + possible cases. + +2009-06-17 13:54 +0000 [r983001] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.h, + trunk/kdesupport/akonadi/server/src/storage/queryhelper.cpp + (added), + trunk/kdesupport/akonadi/server/src/storage/collectionqueryhelper.h + (added), trunk/kdesupport/akonadi/server/src/handler/scope.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/storage/queryhelper.h + (added), trunk/kdesupport/akonadi/server/src/handler/modify.cpp, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/storage/collectionqueryhelper.cpp + (added), + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.cpp: + Extract stuff from the item query helper code that is also useful + for collection queries and use that to support RID-based + collection modification. + +2009-06-17 12:16 +0000 [r982972] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/colmove.cpp: Fix + moving into the root collection. + +2009-06-16 20:23 +0000 [r982804] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Display the Soprano URL + if it can't be found + +2009-06-16 14:14 +0000 [r982648] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/src/handler/colmove.cpp (added), + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/colmove.h (added): + Factor out collection moving from the collection modification + command, like we did for the corresponding item commands already, + to get rid of the look-ahead hack in its parsing code. + +2009-06-15 20:13 +0000 [r982416] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Don't hardcode + SOPRANO_LIBRARIES + +2009-06-15 11:31 +0000 [r982248] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp: Only send + change notifications if something did actually change. + +2009-06-12 18:00 +0000 [r980962] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Bump version to 1.1.91 + +2009-06-12 15:28 +0000 [r980832] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.h, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: When + creating a collection, allow to identify the parent collection by + its remote identifier as well. + +2009-06-10 14:23 +0000 [r979786] Kevin Ottens ervin@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handlerhelper.cpp, + trunk/kdesupport/akonadi/server/src/handler/status.cpp, + trunk/kdesupport/akonadi/server/src/handlerhelper.h: Add support + for SIZE in the STATUS and X-AKLIST commands. So now for + statistics we can get the total size of a collection. + +2009-06-10 09:27 +0000 [r979596] Kevin Ottens ervin@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handlerhelper.cpp, + trunk/kdesupport/akonadi/server/src/handlerhelper.h, + trunk/kdesupport/akonadi/server/src/handler/aklist.cpp: Extend + the server protocol so that X-AKLIST and X-AKLSUB can return the + collection statistics along with the collections (avoid issuing a + first listing and then a storm of STATUS request when you need + the statistics). This protocol modification is an extension and + is backward compatible. + +2009-06-07 10:36 +0000 [r978475] Carlo Segato brandon.ml@gmail.com + + * trunk/kdesupport/akonadi/AkonadiConfig.cmake.in: use .dll.a for + mingw + +2009-06-06 09:28 +0000 [r978167] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp: More fixes + for the item size handling. + +2009-06-06 08:18 +0000 [r978140] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/kdesupport/akonadi/server/src/handler/append.cpp: Fix + updating of items sizes. + +2009-06-03 17:33 +0000 [r977248] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/ChangeLog, + trunk/kdesupport/akonadi/NEWS, + trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/makechangelog: Prepare for the 1.1.90 + release. Also change makechangelog to use anonsvn instead of a + hard-coded svn account. + +2009-05-28 17:58 +0000 [r974197] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/libs/notificationmessage.cpp: Show the + list of changed parts in the akonadiconsole debugger as well. + +2009-05-28 16:59 +0000 [r974177] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.cpp, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.h: + Add a list of actually modified item parts to the change + notifications, or rather put useful data in the already available + field for that in the notification message. + +2009-05-28 12:23 +0000 [r974000] Patrick Spendrin ps_ml@gmx.de + + * trunk/kdesupport/akonadi/AkonadiConfig.cmake.in: msvc needs its + import library + +2009-05-28 09:25 +0000 [r973949] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/libs/tests/notificationmessagetest.cpp, + trunk/kdesupport/akonadi/libs/tests/notificationmessagetest.h, + trunk/kdesupport/akonadi/libs/notificationmessage.cpp: Fix change + notification compression for item changes affecting different + item parts. + +2009-05-28 08:56 +0000 [r973935-973937] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/libs/tests/notificationmessagetest.cpp + (added), + trunk/KDE/kdepimlibs/akonadi/tests/notificationmessagetest.h + (removed), trunk/kdesupport/akonadi/libs/tests (added), + trunk/kdesupport/akonadi/libs/tests/notificationmessagetest.h + (added), + trunk/KDE/kdepimlibs/akonadi/tests/notificationmessagetest.cpp + (removed), trunk/KDE/kdepimlibs/akonadi/tests/CMakeLists.txt, + trunk/kdesupport/akonadi/libs/CMakeLists.txt: Move test to the + corresponding code. + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h: Cleanup + remote id changing, less code and less database writes. + +2009-05-27 20:35 +0000 [r973800] Patrick Spendrin ps_ml@gmx.de + + * trunk/kdesupport/akonadi/CMakeLists.txt: fix msvc build + +2009-05-27 17:40 +0000 [r973701] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/handler/move.cpp: Move stuff + where it belongs. Also reduce the number of database reads a bit. + +2009-05-27 07:56 +0000 [r973441] Till Adam adam@kde.org + + * trunk/kdesupport/akonadi/CMakeLists.txt: Fix the build with cmake + 2.6.2. The version check that checks for > 2.6.2 is in 2, not in + 0. + +2009-05-26 14:27 +0000 [r973201] Rex Dieter rdieter@math.unl.edu + + * trunk/kdesupport/akonadi/akonadi.pc.cmake: + s/AKONADI_LIB_VERSION_STRING/AKONADI_VERSION_STRING/ (..._LIB_... + isn't defined anywhere) + +2009-05-25 23:30 +0000 [r972904] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: akonadi doesn't know the + pimlibs location (and shouldn't) + +2009-05-20 17:56 +0000 [r970762] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Appending the akonadi + version to the directory name where the cmake files are installed + may lead to include or linking errors when different versions are + installed. From now on, the cmake files will be installed in + LIB_INSTALL_DIR/cmake/Akonadi (or /akonadi/cmake depending on the + version). + +2009-05-20 08:22 +0000 [r970440] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/server/akonadictl/CMakeLists.txt, + trunk/kdesupport/akonadi/server/control/CMakeLists.txt: Cosmetic: + Use Akonadi_SOURCE_DIR when it's possible + +2009-05-20 07:38 +0000 [r970355] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml + (added), + trunk/kdesupport/akonadi/server/control/agentmanager.cpp, + trunk/kdesupport/akonadi/server/src/resourcemanager.cpp, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/control/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/resourcemanager.h: fix create + agent instace race condition + +2009-05-17 16:54 +0000 [r969207] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/libs/protocol_p.h: Cleanup the STORE + handler, mainly getting rid of the look-ahead hack. Also reduce + the number of database writes a bit. + +2009-05-17 15:48 +0000 [r969184] Raphael Kubo da Costa kubito@gmail.com + + * trunk/kdesupport/akonadi/server/CMakeLists.txt: Include FreeBSD's + /usr/local/libexec in mysqld's search path. CCMAIL: + kde-freebsd@kde.org + +2009-05-17 12:33 +0000 [r969046] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/libs/protocol_p.h: - add documentation + for the STORE command - add support for remote id based + operations - improved error handling/syntax checking - cleanup + first part of the parsing code + +2009-05-16 17:36 +0000 [r968836] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp: add + more detailed error messages for debugging + +2009-05-16 16:31 +0000 [r968823] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: Deal + with stray newlines caused by zero-sized literals. The real fix + for this would be on the client side, but being more robust + against received garbage can't hurt either. This fixes most of + the unknown command/empty command errors for me, as well as most + of the unit tests. + +2009-05-16 15:29 +0000 [r968735] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: Add + useful error messages to debug unknown/empty command errors. + +2009-05-15 19:16 +0000 [r968466] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/itemretriever.cpp, + trunk/kdesupport/akonadi/server/src/handler/scope.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretriever.h, + trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/src/handler/move.cpp (added), + trunk/kdesupport/akonadi/server/tests/unittest/scopetest.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/scope.cpp, + trunk/kdesupport/akonadi/server/src/handler/move.h (added): - Fix + an infinite loop when parsing rid lists. - After item deletion, + also factor item moving out of the store handler. This will + eventually allow us to get rid of the evil look-ahead hack in the + parsing code there. + +2009-05-14 20:39 +0000 [r968058] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/AkonadiConfig.cmake.in: + AKONADI_COMMON_LIBRARIES was created by FindAkonadi.cmake which + did ensure the correct library extension was added depending on + the system. This patch will resolve issues when building the + akonadi server on mac or windows. This is a temporary fix, I'm + still thinking about the best solution. (ie: define it here or in + FindAkonadi.cmake) + +2009-05-14 18:21 +0000 [r968014] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/remove.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.h, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.cpp: + Factor out query generation code for item sets. + +2009-05-14 18:02 +0000 [r968010] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/remove.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/server/src/handler/scope.h, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.h, + trunk/kdesupport/akonadi/server/src/handler/remove.h, + trunk/kdesupport/akonadi/server/tests/unittest/scopetest.cpp + (added), trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/src/handler/scope.cpp (added), + trunk/kdesupport/akonadi/server/tests/unittest/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.cpp: + Factor out the item set parsing code, which got a bit too complex + now that we support rid-based operations. + +2009-05-13 07:47 +0000 [r967295] Sebastian Trueg sebastian@trueg.de + + * trunk/kdesupport/akonadi/cmake/modules/FindSoprano.cmake: ported + changes from kdelibs + +2009-05-09 15:41 +0000 [r965720] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/handlertest.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp (removed), + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/handler/uid.h (removed), + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/handlertest.cpp: + Cleanup handler creation/deletion and improve the error handling + a bit. + +2009-05-06 22:19 +0000 [r964571] Kevin Ottens ervin@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/akonadidb.xml, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.cpp, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp: When + mapping with the db the remoteId on items should be interpreted + as QString (just like for collections). Also consider remote ids + as UTF8 on the wire. CCMAIL: vkrause@kde.org + +2009-05-06 16:21 +0000 [r964414] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Send the + collection id with FETCH. + +2009-05-05 21:55 +0000 [r964066] Tom Albers toma@kde.org + + * trunk/kdesupport/akonadi/ChangeLog, + trunk/kdesupport/akonadi/NEWS: Update Changelog and News. Ready + for release. + +2009-05-04 04:18 +0000 [r963176] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/AkonadiConfig.cmake.in: Add quotes + around @AKONADI_VERSION_STRING@ or cmake <2.6.3 won't read + AkonadiConfig.cmake + +2009-05-03 13:30 +0000 [r962920] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Generate + AkonadiConfigVersion.cmake + +2009-05-03 13:19 +0000 [r962917] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/cmake/modules/MacroWriteBasicCMakeVersionFile.cmake + (added), + trunk/kdesupport/akonadi/cmake/modules/BasicFindPackageVersion.cmake.in + (added): These two files will ensure the required version is + *really* found + +2009-05-03 11:47 +0000 [r962785] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/AkonadiConfig.cmake.in: Also define + AKONADI_COMMON_LIBRARIES + +2009-05-01 12:44 +0000 [r962047] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/AkonadiConfig.cmake.in (added): Adding + AkonadiConfig.cmake.in which will be used by FindAkonadi.cmake. + Also added an option to install the cmake file in + lib/cmake/Akonadi- (requires cmake >2.6.2) + +2009-04-30 21:04 +0000 [r961878] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/cmake/modules/InstallSettings.cmake: + Remove unused vars. + +2009-04-30 19:55 +0000 [r961844] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/config-akonadi.h.cmake, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/libs/CMakeLists.txt: AKONADI_XXX_VERSION + -> AKONADI_VERSION_XXX + +2009-04-30 18:33 +0000 [r961808] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/config-akonadi.h.cmake, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/libs/CMakeLists.txt: CMakeLists cleanup. + - Remove old comments - Renamed AKONADI_LIB_FOO_VERSION to + AKONADI_FOO_VERSION - Replaced some + ${CMAKE_CURRENT_SOURCE_DIR}/../foo with ${Akonadi_SOURCE_DIR}/foo + The next step is to fix FindAkonadi.cmake. + +2009-04-30 09:46 +0000 [r961537] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Bump version: 1.1.80 -> + 1.1.85 + +2009-04-27 09:48 +0000 [r959814-959815] Till Adam adam@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbinitializer.cpp: + Greatly simplify the check for the existance of tables. Reviewed + by Volker. + + * trunk/kdesupport/akonadi/server/src/main.cpp: Fix my email + address + +2009-04-25 15:50 +0000 [r959089] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/remove.cpp: Report an + error when no items were found. + +2009-04-25 14:33 +0000 [r959046] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/remove.cpp (added), + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/handler/remove.h (added), + trunk/kdesupport/akonadi/server/src/storage/querybuilder.cpp, + trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/src/handler/resourceselect.cpp, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.cpp: + Add a dedicated item deletion command, to get rid of the old + STORE/EXPUNGE hack which was extremely inefficient. The new + command now also supports deletion of more than one item at a + time, and deletion based on a remote identifiers. + +2009-04-22 09:08 +0000 [r957493] Mirko Boehm mirko@kde.org + + * trunk/kdesupport/akonadi/cmake/modules/FindSoprano.cmake: - + enable package config support for finding Soprano + +2009-04-20 23:28 +0000 [r956924] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/server/CMakeLists.txt: MySQL is only + required at runtime. Do not use macro_log_feature if it's not + installed + +2009-04-18 16:26 +0000 [r955842] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.h, + trunk/kdesupport/akonadi/server/src/handler/resourceselect.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: Only + allow the owning resource to change remote identifiers. Based on + a patch by Thomas McGuire. + +2009-04-14 14:30 +0000 [r953783] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/src/cachecleaner.cpp: fix last + commit: change substr(%1, 4) by substr(%1, 1, 4) + +2009-04-13 19:28 +0000 [r953343] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/src/storage/dbupdate.xml, + trunk/kdesupport/akonadi/server/src/storage/itemretriever.cpp, + trunk/kdesupport/akonadi/server/src/cachecleaner.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: change + left(x,y) to substr(x, 1, y) in SQL code(sqlite does not work + with left). CCMAIL: Christian Gaggl + +2009-04-13 14:04 +0000 [r953248] Allen Winter winter@kde.org + + * trunk/kdesupport/akonadi/cmake/modules/FindSoprano.cmake: no need + to look in KDE4 locations for includes and libs + +2009-04-12 20:15 +0000 [r952903] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/CMakeLists.txt: Oops, it's Soprano_FOUND + +2009-04-12 20:01 +0000 [r952898] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/CMakeLists.txt, + trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/search/term.h, + trunk/kdesupport/akonadi/server/src/search/result.cpp, + trunk/kdesupport/akonadi/server/src/search/result.h: Fix + kdesupport compilation : - move akonadi below soprano in the top + CMakeLists.txt - if SOPRANO_FOUND is false, akonadi will use + KDESupport_SOURCE_DIR/soprano and + KDESupport_BINARY_DIR/soprano/soprano/libsoprano.so 3 CamelCase + headers had to be replaced to avoid errors. + +2009-04-08 23:39 +0000 [r951342] Allen Winter winter@kde.org + + * trunk/kdesupport/akonadi/CMakeLists.txt: Qt 4.5.0 or higher is + required now. + +2009-04-07 17:49 +0000 [r950742] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/handler/aklist.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: + Support for collection retrieval by remote identifier. + +2009-04-07 14:01 +0000 [r950620] David Faure faure@kde.org + + * trunk/kdesupport/akonadi/cmake/modules/FindSoprano.cmake: + WIN32_DEBUG_POSTFIX is only implemented by + find_library_with_debug. Calling find_library not only ignored + it, but was looking for libd.so! This cause much trouble to + on IRC (ok, his nick calls for it, but still) + +2009-04-07 12:53 +0000 [r950535] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/resourceselect.h + (added), trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.h, + trunk/kdesupport/akonadi/server/src/handler/scope.h (added), + trunk/kdesupport/akonadi/server/src/handler/select.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.h, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/handler/select.h, + trunk/kdesupport/akonadi/server/src/storage/entities-header.xsl, + trunk/kdesupport/akonadi/server/src/handler/uid.h, + trunk/kdesupport/akonadi/server/src/handler/resourceselect.cpp + (added), trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Add + support for item retrieval based on the remote identifier. + +2009-04-06 13:13 +0000 [r950045] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handler/link.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.cpp, + trunk/kdesupport/akonadi/server/src/handler/rename.cpp, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp, + trunk/kdesupport/akonadi/server/src/handler/login.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp, + trunk/kdesupport/akonadi/server/src/handler/expunge.cpp, + trunk/kdesupport/akonadi/server/src/handler/list.cpp, + trunk/kdesupport/akonadi/server/src/handler/select.cpp, + trunk/kdesupport/akonadi/server/src/handler/subscribe.cpp, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/kdesupport/akonadi/server/src/handler/status.cpp, + trunk/kdesupport/akonadi/server/src/handler/transaction.cpp, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.cpp, + trunk/kdesupport/akonadi/server/src/handler/subscribe.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp, + trunk/kdesupport/akonadi/server/src/handler/aklist.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/src/handler/transaction.h: Avoid + the re-insertion of command names in the input stream. Also, get + rid of some excessive debug output. + +2009-04-06 09:27 +0000 [r949928] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp: Allow to enable + the dummy search manager again, for testing. At least until I + find a way to get Nepomuk running in the test environment. + +2009-04-05 15:40 +0000 [r949629] Kevin Ottens ervin@kde.org + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Add the + extra CRLF cleanup workaround also to the akonadiserver copy of + the stream parser. CCMAIL: amantia@kde.org + +2009-04-05 10:28 +0000 [r949411] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/server/control/processcontrol.cpp: make + it work with multiple arguments, too + +2009-04-05 10:21 +0000 [r949410] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/server/control/processcontrol.cpp: d'oh + +2009-04-05 00:35 +0000 [r949349] Sebastian Sauer mail@dipe.org + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp: typo-- (to land + at least one commit today :) SVN_SILENT + +2009-04-04 17:50 +0000 [r949242] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/server/control/processcontrol.cpp: add + AKONADI_VALGRIND_OPTIONS to specify additional options (besides + the skin used) for valgrind + +2009-04-04 17:15 +0000 [r949236] Christophe Giboudeaux cgiboudeaux@gmail.com + + * trunk/kdesupport/akonadi/server/src/search/queryserviceclient.cpp: + Fix compilation + +2009-04-04 15:02 +0000 [r949146] Allen Winter winter@kde.org + + * trunk/kdesupport/akonadi/cmake/modules/FindSoprano.cmake (added), + trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/cmake/modules/FindLibraryWithDebug.cmake + (added), trunk/kdesupport/akonadi/server/src/search/term.h, + trunk/kdesupport/akonadi/server/src/search/result.cpp, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/search/result.h: Soprano is + required now. Make the buildsystem aware of that, and adjust + includes accordingly. + +2009-04-04 13:03 +0000 [r949064] Tobias Koenig tokoe@kde.org + + * trunk/kdesupport/akonadi/server/src/nepomukmanager.cpp, + trunk/kdesupport/akonadi/server/src/search/query.cpp (added), + trunk/kdesupport/akonadi/server/src/search/term.cpp (added), + trunk/kdesupport/akonadi/server/src/nepomukmanager.h, + trunk/kdesupport/akonadi/server/src/search/org.kde.nepomuk.QueryService.xml + (added), trunk/kdesupport/akonadi/server/src/search/query.h + (added), trunk/kdesupport/akonadi/server/src/search/term.h + (added), trunk/kdesupport/akonadi/server/src/search/result.cpp + (added), trunk/kdesupport/akonadi/server/queryserver (removed), + trunk/kdesupport/akonadi/server/src/search (added), + trunk/kdesupport/akonadi/server/src/search/result.h (added), + trunk/kdesupport/akonadi/server/src/search/querymetatype.h + (added), + trunk/kdesupport/akonadi/server/src/search/queryserviceclient.cpp + (added), + trunk/kdesupport/akonadi/server/src/search/org.kde.nepomuk.Query.xml + (added), + trunk/kdesupport/akonadi/server/src/search/queryserviceclient.h + (added), + trunk/kdesupport/akonadi/server/src/search/dbusoperators.cpp + (added), trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/search/dbusoperators.h + (added), trunk/kdesupport/akonadi/server/src/akonadi.cpp: Add + search support via Nepomuk search service + +2009-04-03 14:49 +0000 [r948702] Till Adam adam@kde.org + + * trunk/kdesupport/akonadi/server/src/exception.h: Silence the + compiler on OSX. + +2009-04-01 10:37 +0000 [r947740-947741] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: Read + the command until the end in case of handling errors. Mandate the + handler to leave the trailing newline in the stream buffer. Fixes + the (harmless) BAD COMMAND error output after a STORE/FETCH with + invalid UID. + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Request + literal also for {0}. Fixes some hangs. + +2009-03-31 12:22 +0000 [r947310] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/control/agentinstance.cpp: Avoid + DBUS lockups + +2009-03-30 14:37 +0000 [r946928] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbinitializer.cpp: + Patch by Christian Gaggl to fix the + database setup for sqlite. + +2009-03-23 22:19 +0000 [r943480-943481] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: + already done in the initializer list now + + * trunk/kdesupport/akonadi, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: don't + leak socket on error. Better initialize members (not strictly + necessary here, though) + +2009-03-22 16:36 +0000 [r942834] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Handle + quoted strings correctly in readUntilCommandEnd(). + +2009-03-22 15:46 +0000 [r942813] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.h: + Add test case for an infinite loop in readUntilCommandEnd() if a + quoted string starts with a '{'. + +2009-03-19 19:55 +0000 [r941495] Kevin Krammer kevin.krammer@gmx.at + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/handler/expunge.cpp: Move the + functionality from DataStore::listPimItems() to the Expunge + handler, its only user. Don't include the selected collection in + the query. Lead to expunges failing, thus items only be marked as + deleted. + +2009-03-19 10:41 +0000 [r941276] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.h: + test readUntilCommandEnd() + +2009-03-19 10:34 +0000 [r941275] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/transaction.cpp, + trunk/kdesupport/akonadi/server/src/storage/transaction.h: Begin + the transaction only after ItemRetriever is executed. + +2009-03-18 20:45 +0000 [r941084] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/expunge.cpp: Read the + command from the string for EXPUNGE, otherwise we will get a bad + command error after. + +2009-03-17 17:57 +0000 [r940575] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/handler/append.cpp: Rewrite + the APPENDhandle, so in case of large payload data and when + external payload storage is enabled, the data is written out to + the payload file as soon as it is read from the socket, thus the + memory usage is reduced. + +2009-03-17 16:03 +0000 [r940546] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h: Rewrite + the STORE handle, so in case of large payload data and when + external payload storage is enabled, the data is written out to + the payload file as soon as it is read from the socket, thus the + memory usage is reduced. + +2009-03-16 19:06 +0000 [r940173] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.h, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/handler/link.h, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/handler/delete.h, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/src/handler/rename.cpp, + trunk/kdesupport/akonadi/server/src/handler/login.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.h, + trunk/kdesupport/akonadi/server/src/handler/logout.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp, + trunk/kdesupport/akonadi/server/src/handler/noop.cpp, + trunk/kdesupport/akonadi/server/src/handler/list.cpp, + trunk/kdesupport/akonadi/server/src/handler/colcopy.h, + trunk/kdesupport/akonadi/server/src/handler/expunge.h, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/handler/select.h, + trunk/kdesupport/akonadi/server/src/handler/transaction.cpp, + trunk/kdesupport/akonadi/server/src/handler/subscribe.h, + trunk/kdesupport/akonadi/server/src/handler/capability.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp, + trunk/kdesupport/akonadi/server/src/handler/status.h, + trunk/kdesupport/akonadi/server/src/handler/akappend.h, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.h, + trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handler/link.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h, + trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp, + trunk/kdesupport/akonadi/server/src/handler/expunge.cpp, + trunk/kdesupport/akonadi/server/src/handler/append.h, + trunk/kdesupport/akonadi/server/src/handler/rename.h, + trunk/kdesupport/akonadi/server/src/handler/login.h, + trunk/kdesupport/akonadi/server/src/handler/logout.h, + trunk/kdesupport/akonadi/server/src/handler/copy.h, + trunk/kdesupport/akonadi/server/src/handler/select.cpp, + trunk/kdesupport/akonadi/server/src/handler/list.h, + trunk/kdesupport/akonadi/server/src/handler/noop.h, + trunk/kdesupport/akonadi/server/src/handler/capability.cpp, + trunk/kdesupport/akonadi/server/src/handler/subscribe.cpp, + trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/kdesupport/akonadi/server/src/handler/status.cpp, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.cpp, + trunk/kdesupport/akonadi/server/src/handler/uid.h, + trunk/kdesupport/akonadi/server/src/handler/aklist.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/src/handler/transaction.h, + trunk/kdesupport/akonadi/server/tests/unittest/handlertest.cpp: + Clean up the handler from the old imapparser code (except Modify + that uses lookahead, might need a rewrite). Change + akonadiconnection to use the ImapStreamParser only when new data + arrives on the socket. All relevant unit tests passes. + +2009-03-15 14:03 +0000 [r939685] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp: With the + stream parser UID sub-commands no longer see the UID token in the + input, we therefore need to pass that info to them by other + means. Fixes failing STORE commands during manual tests, the unit + tests apparently don't catch this. + +2009-03-15 00:47 +0000 [r939511] Tom Albers toma@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbinitializer.cpp: + Spotted a double inclusion of a header file, added in r937343. + +2009-03-13 22:06 +0000 [r939136] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: Add + the posibility to use the streaming parser for handlers (see the + comment in the commit). + +2009-03-13 21:13 +0000 [r939117] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/transaction.cpp, + trunk/kdesupport/akonadi/server/src/handler/transaction.h: Port + the Transaction handler. Now all handlers can use the streaming + parser. + +2009-03-13 21:10 +0000 [r939113-939114] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/append.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/capability.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.cpp, + trunk/kdesupport/akonadi/server/src/handler/status.cpp, + trunk/kdesupport/akonadi/server/src/handler/capability.h, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.h, + trunk/kdesupport/akonadi/server/src/handler/status.h: Port other + handlers. + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Fix + large literal reading, kudos for the unit tests. + +2009-03-13 19:59 +0000 [r939085-939086] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp: Partly + port the Modify handler. Full port is not possible at this + moment, as the code needs to be rewritten (it uses lookahead, + which is not possible with the streaming parser, unless with the + workaround commited here). + + * trunk/kdesupport/akonadi/server/src/handler/rename.h, + trunk/kdesupport/akonadi/server/src/handler/select.cpp, + trunk/kdesupport/akonadi/server/src/handler/subscribe.cpp, + trunk/kdesupport/akonadi/server/src/handler/select.h, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.cpp, + trunk/kdesupport/akonadi/server/src/handler/subscribe.h, + trunk/kdesupport/akonadi/server/src/handler/rename.cpp, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.h: + Port another bunch of handler to imapstreamparser. + +2009-03-13 19:33 +0000 [r939077] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handler/list.h, + trunk/kdesupport/akonadi/server/src/handler/noop.h, + trunk/kdesupport/akonadi/server/src/handler/aklist.cpp, + trunk/kdesupport/akonadi/server/src/handler/list.cpp, + trunk/kdesupport/akonadi/server/src/handler/noop.cpp: Port + AkList, List, Noop. + +2009-03-13 19:22 +0000 [r939073] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/login.h, + trunk/kdesupport/akonadi/server/src/handler/logout.h, + trunk/kdesupport/akonadi/server/src/handler/login.cpp, + trunk/kdesupport/akonadi/server/src/handler/logout.cpp: Port + login/logout. + +2009-03-13 18:02 +0000 [r939050-939052] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/kdesupport/akonadi/server/src/handler/akappend.h: Port + X-AKAPPEND. + + * trunk/kdesupport/akonadi/server/src/handler/colcopy.h, + trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp: Port + COLCOPY (fixes the akonadi-db-collectioncopy test) + + * trunk/kdesupport/akonadi/server/src/handler/link.cpp: Link is + used for UNLINK as well. + +2009-03-13 16:29 +0000 [r939034] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.h, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h: Implement + DateTime reading, port all the remaining unit tests. + +2009-03-13 15:47 +0000 [r939027-939028] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/link.cpp, + trunk/kdesupport/akonadi/server/src/handler/link.h: Port LINK. + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Another + fix in sequence reading (update correctly the m_position by + getting rid of the extra index variable) + +2009-03-13 14:58 +0000 [r939006] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/expunge.h, + trunk/kdesupport/akonadi/server/src/handler/delete.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.h, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp, + trunk/kdesupport/akonadi/server/src/handler/expunge.cpp: Port + Delete and Expunge. + +2009-03-13 13:40 +0000 [r938981] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/copy.h, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp: Port the + Copy handler. + +2009-03-12 15:13 +0000 [r938638] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Port fetch + to imapstreamparser + +2009-03-12 14:31 +0000 [r938628-938629] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/src/storage/querybuilder.cpp: + Port the Store handler to the imapstreamparser + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h: Add a + method to push back some data to the parser, fix reading of + sequence sets. + +2009-03-11 14:01 +0000 [r938176] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/libs/xdgbasedirs.cpp: Include the KDE + prefix in our search paths. Useful if Akonadi and KDE are + installed into different prefixes and XDG_DATA_DIRS is not set up + correctly. So, mostly developer convenience. + +2009-03-11 09:44 +0000 [r938091] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Clear + the data buffer after a literal part was read to save memory. + +2009-03-10 17:21 +0000 [r937879] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp: Merge + Kevin's changes for the imap stream parser. + +2009-03-09 15:53 +0000 [r937343] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbinitializer.cpp: + Add query error to the error message. + +2009-03-08 23:10 +0000 [r937083] Alex Merry kde@randomguy3.me.uk + + * trunk/kdesupport/akonadi/Mainpage.dox: Lay out the documentation + a bit better. There are still a couple of references to missing + pages. Question: why does the project version get injected into + the KDE Support API docs main pages, but not into the kdelibs API + docs main pages? + +2009-03-08 16:22 +0000 [r936905] Christian Ehrlicher Ch.Ehrlicher@gmx.de + + * trunk/kdesupport/akonadi/server/tests/unittest/CMakeLists.txt: + the private/public hack does not work on windows -> disabled + imapstreamparsertest there + +2009-03-05 20:40 +0000 [r935628] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/server/CMakeLists.txt: enforce build + dirs to be searched before other dirs, as entities.h collides + with an entities.h from /opt/local, here on OS X + +2009-03-05 14:44 +0000 [r935544] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/CMakeLists.txt: support gcov compiling + +2009-03-03 11:34 +0000 [r934526] Dario Freddi drf@kde.org + + * trunk/kdesupport/akonadi/README: Fix typo + +2009-03-01 12:23 +0000 [r933592] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretrievalthread.cpp: + Rework thread termination/deletion code. We now have no more open + database connection when shutting down the db server, which + contrary to my hopes has apparently no impact on shutdown speed. + +2009-02-28 23:14 +0000 [r933455] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretrievalmanager.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/storage/itemretrievalmanager.h, + trunk/kdesupport/akonadi/server/src/intervalcheck.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Move the + collection sync triggering code to ItemRetrievalManager as well, + to share the resource interface cache and benefit from the + thread-safety fixes there. + +2009-02-28 22:51 +0000 [r933443] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.h: remove + the old item retrieval code + +2009-02-28 22:46 +0000 [r933442] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Use the + new item retrieval code here as well, but keep ignoring errors + for now. As we fall back to whatever we already have in the cache + in this case, this actually is the desired behaviour in many + cases, but I guess we need a command parameter to let the + application specify if errors should be forwarded or ignored. + +2009-02-28 22:40 +0000 [r933440] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.cpp: our unit + tests need way too long to run... + +2009-02-28 22:01 +0000 [r933428] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/itemretriever.cpp, + trunk/kdesupport/akonadi/server/src/akonadi.h, + trunk/kdesupport/akonadi/server/src/storage/itemretrievalmanager.cpp + (added), + trunk/kdesupport/akonadi/server/src/storage/itemretrievalthread.h + (added), + trunk/kdesupport/akonadi/server/src/storage/itemretriever.h, + trunk/kdesupport/akonadi/server/src/storage/itemretrievalmanager.h + (added), trunk/kdesupport/akonadi/server/src/exception.h, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/akonadi.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretrievalthread.cpp + (added): New item retrieval code. Much better error handling + (compared to none before) and also fixes a bunch of corner cases + that were not handled correctly so far. Hopefully also fixes + thread-safety problems we had with the old code, but this will + require some more testing. The fetch handler still needs to be + changed to use this though. + +2009-02-28 21:56 +0000 [r933425] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.cpp: Only + retrieve all child items when we absolutely have to. + +2009-02-28 15:16 +0000 [r933283] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/modify.cpp: Retrieve + all items from their resources before moving a collection. + +2009-02-28 14:02 +0000 [r933259] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h: Reshuffling + of the parsing code to be able to access the requested changes + before starting the database transaction. This is needed since we + can only request not yet cached items from their resources before + beginning a transaction (we wouldn't see the changes otherwise). + And we need to do that for inter-resource moves. + +2009-02-28 10:52 +0000 [r933199] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/itemretriever.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretriever.h, + trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp: Retrieve + all items from the source resource before we copy them. + +2009-02-28 09:36 +0000 [r933175] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp, + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp: + Various small improvements, suggested by Kevin. + +2009-02-26 22:30 +0000 [r932572] Allen Winter winter@kde.org + + * trunk/kdesupport/akonadi/server/src/imapstreamparser.h: fix + EXPORT so linking works on Windows + +2009-02-26 12:08 +0000 [r932340] Jesper Thomschütz jesperht@yahoo.com + + * trunk/kdesupport/soprano/server/sparql/sparqlmodel.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp, + trunk/kdesupport/phonon/gstreamer/x11renderer.cpp: Removing some + "unused variables" warnings by employing Q_UNUSED + +2009-02-26 09:58 +0000 [r932076] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.cpp + (added), trunk/kdesupport/akonadi/server/src/akonadiconnection.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/handler/uid.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/imapstreamparsertest.h + (added), trunk/kdesupport/akonadi/server/src/imapstreamparser.cpp + (added), trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/handler/uid.h, + trunk/kdesupport/akonadi/server/src/imapstreamparser.h (added), + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/CMakeLists.txt: + Add a streaming IMAP parser, as described on + http://reviewboard.kde.org/r/191/ . Updated the UID handler to + support and use it. Update the akonadiconnection to use the + streaming parser if the handler support is. Reviewed by Volker. + +2009-02-25 10:50 +0000 [r931461] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp: Fix startup when + the MySQL config files didn't change. + +2009-02-24 18:32 +0000 [r931064] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/handler/modify.h, + trunk/kdesupport/akonadi/server/src/handler/link.h, + trunk/kdesupport/akonadi/server/src/handler/delete.h, + trunk/kdesupport/akonadi/server/src/handler/create.h, + trunk/kdesupport/akonadi/server/src/handler/append.h, + trunk/kdesupport/akonadi/server/src/handler/rename.h, + trunk/kdesupport/akonadi/server/src/handler/colcopy.h, + trunk/kdesupport/akonadi/server/src/handler/login.h, + trunk/kdesupport/akonadi/server/src/handler/logout.h, + trunk/kdesupport/akonadi/server/src/handler/copy.h, + trunk/kdesupport/akonadi/server/src/handler/expunge.h, + trunk/kdesupport/akonadi/server/src/handler/list.h, + trunk/kdesupport/akonadi/server/src/handler/noop.h, + trunk/kdesupport/akonadi/server/src/handler/select.h, + trunk/kdesupport/akonadi/server/src/handler/capability.h, + trunk/kdesupport/akonadi/server/src/handler/subscribe.h, + trunk/kdesupport/akonadi/server/src/handler/akappend.h, + trunk/kdesupport/akonadi/server/src/handler/status.h, + trunk/kdesupport/akonadi/server/src/handler/transaction.h, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.h: + Add Q_OBJECT to subclasses, helps e.g when debugging. + +2009-02-24 18:29 +0000 [r931063] Rex Dieter rdieter@math.unl.edu + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp: update user + mysql.conf only if global/local one's are newer, and in doing so, + clear mysql ib_logfile's + +2009-02-22 16:37 +0000 [r930069] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretriever.cpp + (added), trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemretriever.h + (added), trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/storage/entity.h, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp: Extract + item retrieval code from the fetch handler and use it to retrieve + all missing item parts before we copy them. Changing the fetch + handler to use the new retrieval code as well will need some more + work though, as it's quite interleaved with everything else there + for performance reasons. + +2009-02-22 13:04 +0000 [r929953] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/handler/link.cpp, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.h + (added), trunk/kdesupport/akonadi/libs/imapset_p.h, + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp, + trunk/kdesupport/akonadi/server/src/storage/itemqueryhelper.cpp + (added): Factor out the item query helper code, we will need that + outside of the command handlers as well. + +2009-02-21 21:08 +0000 [r929675] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/exception.h: fix include + guard + +2009-02-21 20:58 +0000 [r929670] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/shared/akcrash.cpp, + trunk/kdesupport/akonadi/server/shared/akcrash.h, + trunk/kdesupport/akonadi/server/src/handler.h, + trunk/kdesupport/akonadi/server/src/exception.h (added), + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Move the + handler exception and its handling out of the FETCH handler, so + we can use it for all other handlers as well. + +2009-02-21 15:53 +0000 [r929552] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/src/cachecleaner.cpp: check if + collection has a resource associated with it + +2009-02-18 11:55 +0000 [r927758] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp, + trunk/kdesupport/akonadi/server/src/cachecleaner.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.h: Add a + revision number for each payload file and increment it when it + changes and delete the old one after. + +2009-02-17 12:20 +0000 [r927412] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp: Fix item + copying in case the item data is in an external file. Increment + protocol version number. + +2009-02-15 16:00 +0000 [r926523] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp: Update + resource identifiers on inter-resource moves. + +2009-02-15 12:23 +0000 [r926432] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp: + Prevent moving into a child collection of the source collection. + +2009-02-12 20:30 +0000 [r925239] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/fetch.h, + trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Prepare + the server to send the payload file name instead of the real data + if requested. The default case is to send the data over socket, + just like before. + +2009-02-10 11:12 +0000 [r924168] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/KDE/kdepimlibs/akonadi/tests/itemappendtest.cpp, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/KDE/kdepimlibs/akonadi/tests/itemappendtest.h: + Automatically register new mimetypes. + +2009-02-09 22:05 +0000 [r924027] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp: Fix + the case when the external storage is switched back to internal + one. + +2009-02-09 21:06 +0000 [r923995] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbupdate.xml, + trunk/kdesupport/akonadi/server/src/storage/akonadidb.xml, + trunk/kdesupport/akonadi/server/src/storage/dbconfig.cpp, + trunk/kdesupport/akonadi/server/src/storage/dbconfig.h: Use + qint64 for PartTable::datasize, set the threshold to a nicer + value. + +2009-02-09 17:57 +0000 [r923906-923907] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp, + trunk/kdesupport/akonadi/server/src/cachecleaner.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.h, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Use + DbConfig to decide when it is needed to use external payload. + + * trunk/kdesupport/akonadi/server/src/storage/dbconfig.cpp, + trunk/kdesupport/akonadi/server/src/storage/dbconfig.h: Add + options to enable external storage payload (disabled by default) + and set a size threshold. Files above this threshold will be + stored in an external file if the external storage is enabled. + +2009-02-07 07:55 +0000 [r922471] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp, + trunk/kdesupport/akonadi/server/src/handler/store.h, + trunk/kdesupport/akonadi/server/src/storage/parthelper.h: cleanup + part deletion code + +2009-02-07 01:02 +0000 [r922428] Patrick Spendrin ps_ml@gmx.de + + * trunk/kdesupport/akonadi/server/akonadictl/main.cpp: use the + native function on mingw too + +2009-02-06 09:04 +0000 [r922019] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/store.cpp, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/parthelper.cpp + (added), trunk/kdesupport/akonadi/server/src/akonadiconnection.h, + trunk/kdesupport/akonadi/server/src/cachecleaner.cpp, + trunk/kdesupport/akonadi/server/src/storage/akonadidb.xml, + trunk/kdesupport/akonadi/server/src/storage/parthelper.h (added), + trunk/kdesupport/akonadi/server/src/storage/entities-header.xsl, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/CMakeLists.txt, + trunk/kdesupport/akonadi/server/src/global.h, + trunk/kdesupport/akonadi/server/src/akonadi.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp: Commit the + preliminary version of "store payload in files" functionality. + +2009-02-05 22:35 +0000 [r921908] Romain Pokrzywka romain@kdab.net + + * trunk/kdesupport/akonadi/server/akonadictl/main.cpp: added + #include + +2009-02-05 22:19 +0000 [r921903] Romain Pokrzywka romain@kdab.net + + * trunk/kdesupport/akonadi/server/akonadictl/main.cpp: fixed the + build on platforms without unistd.h (ie. windows) + +2009-02-01 22:41 +0000 [r919962] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp: Fix crash on + quit due to still accepting new connections while we are already + shutting down. + +2009-02-01 22:10 +0000 [r919954] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/make-unittest-mysql.sh + (removed), + trunk/kdesupport/akonadi/server/src/storage/create-unittest-values.sql + (removed), + trunk/kdesupport/akonadi/server/src/storage/make-unittest-mysql-embedded.sh + (removed), + trunk/kdesupport/akonadi/server/src/storage/make-unittest-sqlite.sh + (removed): obsolete, unittests use the testrunner now + +2009-02-01 10:52 +0000 [r919551] Volker Krause vkrause@kde.org + + * trunk/KDE/kdepimlibs/akonadi/resourcebase.cpp, + trunk/KDE/kdepimlibs/akonadi/resourcescheduler.cpp, + trunk/KDE/kdepimlibs/akonadi/resourcebase.h, + trunk/kdesupport/akonadi/interfaces/org.freedesktop.Akonadi.Resource.xml, + trunk/KDE/kdepimlibs/akonadi/resourcescheduler.h: Emit a signal + when a full sync has been completed. Not really interesting for + normal applications but crucial for the testrunner and the + benchmarks. + +2009-02-01 10:19 +0000 [r919542] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/akonadi.cpp: Make sure the + query object is destroyed before we close the database + connection. Fixes a bunch of invalid reads found by valgrind. + +2009-01-30 18:51 +0000 [r918781] Frank Osterfeld frank.osterfeld@kdemail.net + + * trunk/kdesupport/akonadi/server/control/main.cpp, + trunk/kdesupport/akonadi: fix error message + +2009-01-26 18:12 +0000 [r917032] Andras Mantia amantia@kde.org + + * trunk/kdesupport/akonadi/server/src/handler.h: Add some API docs + and removed an unimplemented method declaration. + +2009-01-25 20:55 +0000 [r916728] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp: clear + the remote id on cross-resource copies + +2009-01-25 17:30 +0000 [r916629] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/akonadictl/main.cpp: Oops ... let + me try again + +2009-01-25 17:19 +0000 [r916624] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/akonadictl/main.cpp: add restart + option in akonadictl + +2009-01-24 15:26 +0000 [r916099] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/src/cachecleaner.cpp, + trunk/kdesupport/akonadi/server/src/cachecleaner.h: create an + "smart/lazy" algorithm for CacheClean wake ups + +2009-01-24 15:01 +0000 [r916095] Volker Krause vkrause@kde.org + + * trunk/KDE/kdepim/akonadiconsole/agentwidget.h, + trunk/KDE/kdepim/akonadiconsole/mainwidget.cpp, + trunk/KDE/kdepimlibs/akonadi/agentinstance.cpp, + trunk/KDE/kdepim/akonadiconsole/CMakeLists.txt, + trunk/KDE/kdepim/akonadiconsole/agentwidget.ui (added), + trunk/KDE/kdepimlibs/akonadi/agentinstancewidget.cpp, + trunk/KDE/kdepimlibs/akonadi/agentinstance.h, + trunk/kdesupport/akonadi/interfaces/org.freedesktop.Akonadi.AgentManager.xml, + trunk/kdesupport/akonadi/server/control/agentmanager.cpp, + trunk/KDE/kdepim/akonadiconsole/agentwidget.cpp, + trunk/kdesupport/akonadi/server/control/agentmanager.h: Add + support for manually restarting an agent instance and make that + available in akonadiconsole. This is extremely useful when + developing agents or resources. While I was at it, rework the + agent view in akonadiconsole to show more infos, add a context + menu, etc. + +2009-01-24 12:42 +0000 [r916024] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbupdate.xml: + Database update code for the location -> collection rename. + +2009-01-23 20:38 +0000 [r915808] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/storage/dbupdater.cpp: Make + sure errors during database initialization or upgrade end up in + the error report. + +2009-01-23 20:21 +0000 [r915806] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/src/storage/dbupdate.xml, + trunk/kdesupport/akonadi/server/src/storage/akonadidb.xml: - + revert the table renaming in the pre-rename update code - + increment db schema version + +2009-01-22 00:02 +0000 [r914892] Igor Trindade Oliveira igor_trindade@yahoo.com.br + + * trunk/kdesupport/akonadi/server/src/handlerhelper.cpp, + trunk/kdesupport/akonadi/server/src/nepomukmanager.h, + trunk/kdesupport/akonadi/server/src/resourcemanager.cpp, + trunk/kdesupport/akonadi/server/src/handler/rename.cpp, + trunk/kdesupport/akonadi/server/src/abstractsearchmanager.cpp, + trunk/kdesupport/akonadi/server/src/handler/append.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.h, + trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.cpp, + trunk/kdesupport/akonadi/server/src/handler/list.cpp, + trunk/kdesupport/akonadi/server/src/handler/colcopy.h, + trunk/kdesupport/akonadi/server/src/cachecleaner.cpp, + trunk/kdesupport/akonadi/server/src/storage/akonadidb.xml, + trunk/kdesupport/akonadi/server/src/storage/datastore.h, + trunk/kdesupport/akonadi/server/src/handler/modify.cpp, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.h, + trunk/kdesupport/akonadi/server/src/handler/aklist.h, + trunk/kdesupport/akonadi/server/src/nepomukmanager.cpp, + trunk/kdesupport/akonadi/server/src/storage/dbupdate.xml, + trunk/kdesupport/akonadi/server/src/handler/link.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/teststoragebackend.h, + trunk/kdesupport/akonadi/server/src/xesammanager.h, + trunk/kdesupport/akonadi/server/src/handler.cpp, + trunk/kdesupport/akonadi/server/src/handler/delete.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.cpp, + trunk/kdesupport/akonadi/server/src/handlerhelper.h, + trunk/kdesupport/akonadi/server/src/intervalcheck.cpp, + trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp, + trunk/kdesupport/akonadi/server/src/handler/expunge.cpp, + trunk/kdesupport/akonadi/server/src/abstractsearchmanager.h, + trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, + trunk/kdesupport/akonadi/server/src/akonadiconnection.h, + trunk/kdesupport/akonadi/server/src/handler/select.cpp, + trunk/kdesupport/akonadi/server/src/handler/copy.h, + trunk/kdesupport/akonadi/server/src/handler/list.h, + trunk/kdesupport/akonadi/server/src/handler/subscribe.cpp, + trunk/kdesupport/akonadi/server/src/storage/create-unittest-values.sql, + trunk/kdesupport/akonadi/server/src/handler/status.cpp, + trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, + trunk/kdesupport/akonadi/server/src/storage/notificationcollector.cpp, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.cpp, + trunk/kdesupport/akonadi/server/src/handler/aklist.cpp, + trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, + trunk/kdesupport/akonadi/server/tests/unittest/teststoragebackend.cpp, + trunk/kdesupport/akonadi/server/src/xesammanager.cpp: janitor + job: change Location name to Collection + +2009-01-09 23:33 +0000 [r908591] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/shared/akdebug.cpp: Don't put + essential code into assert(), doesn't seem to be executed in + release builds. + +2009-01-05 15:00 +0000 [r906112] David Faure faure@kde.org + + * trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/cmake/modules/FindBoost.cmake (removed): + Require cmake-2.6.0 and its much nicer FindBoost.cmake CCMAIL: + adam@kde.org, vkrause@kde.org + +2009-01-05 14:44 +0000 [r906099] David Faure faure@kde.org + + * trunk/kdesupport/akonadi/cmake/modules/FindBoost.cmake: Before, + it said: -- Boost Version required: .. Found: 1.34.1 Now it says: + -- Boost Version found: 1.34.1. Component PROGRAM_OPTIONS not + found. (given akonadi's use: find_package(Boost REQUIRED + COMPONENTS program_options)) I see that CMake's FindBoost is much + better about this already, it has _Boost_MISSING_COMPONENTS and + Boost_ERROR_REASON. Should this copy be removed? I think the + initial reason isn't right anymore, if we depend on cmake-2.6.2. + CCMAIL: adam@kde.org + +2009-01-04 22:07 +0000 [r905714] Patrick Spendrin ps_ml@gmx.de + + * trunk/kdesupport/akonadi/cmake/modules/FindBoost.cmake: add 1.37 + release + +2009-01-04 15:57 +0000 [r905553] Volker Krause vkrause@kde.org + + * trunk/kdesupport/akonadi/server/control/agentmanager.cpp: Don't + try to restart an agent that has been deleted. + +2009-01-04 15:45 +0000 [r905550] Alexander Neundorf neundorf@kde.org + + * trunk/kdesupport/akonadi/cmake/modules/FindAutomoc4.cmake, + trunk/kdesupport/phonon/cmake/FindAutomoc4.cmake: -update + FindAutomoc4.cmake to include documentation, same as in + kdelibs/cmake/modules/FindAutomoc4.cmake Alex + +2009-01-03 16:19 +0000 [r905037] Tom Albers toma@kde.org + + * trunk/kdesupport/akonadi/CMakeLists.txt: Bump version for trunk + to 1.1.80. Open for features again. + +2009-01-03 16:10 +0000 [r905016] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/ChangeLog, trunk/kdesupport/akonadi/NEWS, @@ -141,7 +1790,7 @@ * trunk/kdesupport/akonadi/server/src/storage/dbconfig.cpp: add FreeBSD mysqld default path -2008-12-16 19:06 +0000 [r897748] Tom Albers tomalbers@kde.nl +2008-12-16 19:06 +0000 [r897748] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/ChangeLog, trunk/kdesupport/akonadi/NEWS, @@ -175,8 +1824,8 @@ 2008-12-02 10:14 +0000 [r891498] Alexander Neundorf neundorf@kde.org - * trunk/kdesupport/akonadi/cmake/CMakeLists.txt (removed), - trunk/kdesupport/akonadi/CMakeLists.txt, + * trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/cmake/CMakeLists.txt (removed), trunk/kdesupport/akonadi/cmake/modules/FindAutomoc4.cmake, trunk/kdesupport/akonadi/server/CMakeLists.txt: -proper RPATH handling (see comments in CMakeLists.txt) -remove LIB_DESTINATION @@ -234,7 +1883,7 @@ trunk/kdesupport/akonadi/server/src/storage/dbconfig.cpp: Try harder to find the mysqld executable during runtime. -2008-11-19 19:31 +0000 [r886652] Tom Albers tomalbers@kde.nl +2008-11-19 19:31 +0000 [r886652] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/ChangeLog, trunk/kdesupport/akonadi/NEWS: Prep for release. @@ -270,7 +1919,7 @@ also removes the restriction that agents were not allowed to access the agent manager themselves to avoid deadlocks. -2008-11-04 21:16 +0000 [r880161] Tom Albers tomalbers@kde.nl +2008-11-04 21:16 +0000 [r880161] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, trunk/kdesupport/akonadi/server/src/handler/append.cpp: Fix the @@ -281,7 +1930,7 @@ * trunk/kdesupport/akonadi/server/src/storage/mysql-global.conf: Increase the limit to something more realistical. -2008-11-01 10:09 +0000 [r878448] Tom Albers tomalbers@kde.nl +2008-11-01 10:09 +0000 [r878448] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, trunk/kdesupport/akonadi/server/src/handler/append.cpp: Don't @@ -291,7 +1940,7 @@ messages are marked as recent, although they also contain the \Seen flag. -2008-11-01 09:26 +0000 [r878427] Tom Albers tomalbers@kde.nl +2008-11-01 09:26 +0000 [r878427] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/server/src/handler/append.h, trunk/kdesupport/akonadi/server/src/handler/store.cpp, @@ -310,7 +1959,7 @@ * trunk/kdesupport/akonadi/server/src/storage/notificationcollector.h: build fix CCMAIL vkrause@kde.org -2008-10-04 23:05 +0000 [r867919] Tom Albers tomalbers@kde.nl +2008-10-04 23:05 +0000 [r867919] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/server/src/storage/mysql-global.conf: 1M is to restrictive, messages can and will be bigger. @@ -554,8 +2203,8 @@ trunk/KDE/kdepimlibs/akonadi/agentinstance.h, trunk/KDE/kdepimlibs/akonadi/agentbase.cpp, trunk/KDE/kdepim/akonadi/migration/kres/kresmigrator.cpp (added), - trunk/KDE/kdepim/akonadi/migration/kres/kabcmigrator.h (added), trunk/kdesupport/akonadi/interfaces/org.freedesktop.Akonadi.Agent.Control.xml, + trunk/KDE/kdepim/akonadi/migration/kres/kabcmigrator.h (added), trunk/KDE/kdepim/akonadi/migration (added), trunk/KDE/kdepimlibs/akonadi/agentbase.h, trunk/KDE/kdepim/akonadi/migration/kres/kresmigrator.h (added), @@ -590,7 +2239,7 @@ trunk/kdesupport/akonadi/server/src/handler/fetch.cpp: Return the modification time when an item is appended/stored/fetcehd. -2008-07-22 20:52 +0000 [r836720] Tom Albers tomalbers@kde.nl +2008-07-22 20:52 +0000 [r836720] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/CMakeLists.txt: Move version from 0.82.0 to 1.0.80. This is now akonadi unstable branch. Akonadi stable is @@ -641,7 +2290,7 @@ CMAKE_INSTALL_PREFIX, since this is already part of PATH_SUFFIXES Alex -2008-06-18 16:00 +0000 [r821830] Tom Albers tomalbers@kde.nl +2008-06-18 16:00 +0000 [r821830] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/ChangeLog, trunk/kdesupport/akonadi/NEWS, @@ -719,12 +2368,12 @@ trunk/kdesupport/akonadi/CMakeLists.txt: fix install_name RPATH stuff on OSX -2008-05-19 10:26 +0000 [r809711] Jaroslaw Staniek js@iidea.pl +2008-05-19 10:26 +0000 [r809711] Jarosław Staniek staniek@kde.org * trunk/kdesupport/akonadi/CMakeLists.txt: make libs install to bin on Windows, so far this fixes akonadiprivate.dll destination -2008-05-18 22:03 +0000 [r809435] Tom Albers tomalbers@kde.nl +2008-05-18 22:03 +0000 [r809435] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/ChangeLog (added), trunk/kdesupport/akonadi/NEWS (added), @@ -794,20 +2443,20 @@ * trunk/kdesupport/akonadi/server/control/org.freedesktop.Akonadi.Control.service.cmake (added), - trunk/kdesupport/akonadi/server/interfaces/org.kde.Akonadi.SearchQueryIterator.xml - (removed), trunk/kdesupport/akonadi/server/interfaces/org.kde.Akonadi.Search.xml (removed), + trunk/kdesupport/akonadi/server/interfaces/org.kde.Akonadi.SearchQueryIterator.xml + (removed), trunk/kdesupport/akonadi/server/interfaces/org.kde.Akonadi.Server.xml (removed), trunk/kdesupport/akonadi/interfaces/org.kde.Akonadi.Agent.Control.xml (removed), trunk/kdesupport/akonadi/server/src/nepomukmanager.h, trunk/kdesupport/akonadi/interfaces/org.kde.Akonadi.Tracer.xml (removed), trunk/kdesupport/akonadi/libs/protocol_p.h, + trunk/kdesupport/akonadi/server/control/agentmanager.cpp, trunk/kdesupport/akonadi/interfaces/org.kde.Akonadi.NotificationManager.xml (removed), trunk/kdesupport/akonadi/server/src/resourcemanager.cpp, - trunk/kdesupport/akonadi/server/control/agentmanager.cpp, trunk/kdesupport/akonadi/server/src/main.cpp, trunk/kdesupport/akonadi/server/src/notificationmanager.h, trunk/kdesupport/akonadi/interfaces/org.kde.Akonadi.Resource.xml @@ -880,7 +2529,7 @@ Extend requestItemDelivery() by mimeType to provide a sane API on ResourceBase side. -2008-04-29 23:04 +0000 [r802602] Tom Albers tomalbers@kde.nl +2008-04-29 23:04 +0000 [r802602] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/cmake/modules/COPYING-CMAKE-SCRIPTS (added): Referenced by some of the files in here. @@ -949,8 +2598,9 @@ (removed), trunk/kdesupport/akonadi/server/akonadictl/CMakeLists.txt, trunk/kdesupport/akonadi/server/src/tests/CMakeLists.txt, + trunk/kdesupport/akonadi/CMakeLists.txt, trunk/kdesupport/akonadi/cmake/modules/kde4automoc.files.in - (removed), trunk/kdesupport/akonadi/CMakeLists.txt, + (removed), trunk/kdesupport/akonadi/cmake/modules/KDE4Macros.cmake (removed), trunk/kdesupport/akonadi/server/CMakeLists.txt, trunk/kdesupport/akonadi/server/control/CMakeLists.txt, @@ -980,8 +2630,8 @@ * trunk/kdesupport/akonadi/server/src/nepomukmanager.cpp, trunk/kdesupport/akonadi/server/queryserver/query.cpp, trunk/kdesupport/akonadi/server/src/handlerhelper.cpp, - trunk/kdesupport/akonadi/server/src/handler/create.cpp, trunk/kdesupport/akonadi/server/src/storage/querybuilder.cpp, + trunk/kdesupport/akonadi/server/src/handler/create.cpp, trunk/kdesupport/akonadi/server/src/notificationmanager.cpp, trunk/kdesupport/akonadi/server/control/agentmanager.cpp, trunk/kdesupport/akonadi/server/src/handler/append.cpp, @@ -1138,7 +2788,7 @@ not available on msvc, do not use it - rather check with cmake for availability - msvc compiles now -2008-04-23 21:20 +0000 [r800304] Tom Albers tomalbers@kde.nl +2008-04-23 21:20 +0000 [r800304] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/lgpl-license (added), trunk/kdesupport/akonadi/server/CMakeLists.txt, @@ -1152,7 +2802,7 @@ * trunk/kdesupport/akonadi/cmake/automoc/CMakeLists.txt: Don't install our borrowed automoc. -2008-04-23 20:39 +0000 [r800285] Tom Albers tomalbers@kde.nl +2008-04-23 20:39 +0000 [r800285] Tom Albers toma@kde.org * trunk/kdesupport/akonadi/server/CMakeLists.txt, trunk/kdesupport/akonadi/libs/CMakeLists.txt: We can no longer @@ -1175,8 +2825,8 @@ 2008-04-23 19:23 +0000 [r800264] Will Stephenson wstephenson@kde.org * trunk/kdesupport/akonadi/libs/akonadiprotocolinternals_export.h, - trunk/kdesupport/akonadi/kdemacros.h.cmake (removed), trunk/kdesupport/akonadi/CMakeLists.txt, + trunk/kdesupport/akonadi/kdemacros.h.cmake (removed), trunk/kdesupport/akonadi/server/src/akonadiprivate_export.h: Last few kdemacros.h dependencies; thanks Allen @@ -1259,8 +2909,8 @@ 2008-04-23 17:25 +0000 [r800213] Will Stephenson wstephenson@kde.org - * trunk/kdesupport/akonadi/libs/notificationmessage_p.h (added), - trunk/kdesupport/akonadi/server/interfaces (added), + * trunk/kdesupport/akonadi/server/interfaces (added), + trunk/kdesupport/akonadi/libs/notificationmessage_p.h (added), trunk/KDE/kdepim/akonadi/config-akonadi.h.cmake (removed), trunk/KDE/kdepim/akonadi/server/akonadictl (removed), trunk/KDE/kdepim/akonadi/server/Mainpage.dox (removed), @@ -1282,8 +2932,8 @@ trunk/KDE/kdepim/akonadi/server/queryserver (removed), trunk/kdesupport/akonadi/server/akonadictl (added), trunk/kdesupport/akonadi/server/Mainpage.dox (added), - trunk/KDE/kdepim/akonadi/server/templates (removed), trunk/kdesupport/akonadi/cmake (added), + trunk/KDE/kdepim/akonadi/server/templates (removed), trunk/kdesupport/akonadi/server/src/handler/modify.cpp, trunk/kdesupport/akonadi/interfaces (added), trunk/kdesupport/akonadi/server/akonadictl/main.cpp, @@ -1294,10 +2944,10 @@ (removed), trunk/kdesupport/akonadi/server/src/handler/delete.cpp, trunk/KDE/kdepimlibs/akonadi/imapset_p.h (removed), - trunk/KDE/kdepim/akonadi/server/control (removed), trunk/kdesupport/akonadi/server/queryserver (added), - trunk/kdesupport/akonadi/server/control/CMakeLists.txt, + trunk/KDE/kdepim/akonadi/server/control (removed), trunk/kdesupport/akonadi/server/src/handler/colcopy.cpp, + trunk/kdesupport/akonadi/server/control/CMakeLists.txt, trunk/kdesupport/akonadi/server/templates (added), trunk/kdesupport/akonadi/libs/notificationmessage.cpp (added), trunk/kdesupport/akonadi/server/src/storage/datastore.cpp, @@ -1324,8 +2974,8 @@ (added), trunk/kdesupport/akonadi (added), trunk/kdesupport/akonadi/server/src/handler/rename.cpp, trunk/KDE/kdepimlibs/akonadi/notificationmessage.cpp (removed), - trunk/KDE/kdepimlibs/akonadi/CMakeLists.txt, trunk/kdesupport/akonadi/server/src/handler/login.cpp, + trunk/KDE/kdepimlibs/akonadi/CMakeLists.txt, trunk/kdesupport/akonadi/server/src/akonadiconnection.cpp, trunk/KDE/kdepim/akonadi/akonadi-mime.xml (removed), trunk/kdesupport/akonadi/server/src/handler/list.cpp, @@ -1339,10 +2989,10 @@ (removed), trunk/KDE/kdepimlibs/akonadi/interfaces/org.kde.Akonadi.NotificationManager.xml (removed), trunk/kdesupport/akonadi/server/src/handler/store.cpp, - trunk/kdesupport/akonadi/server/src/handler/fetch.h, trunk/KDE/kdepim/akonadi/CMakeLists.txt, - trunk/KDE/kdepimlibs/akonadi/xdgbasedirs_p.h (removed), + trunk/kdesupport/akonadi/server/src/handler/fetch.h, trunk/kdesupport/akonadi/akonadi-mime.xml (added), + trunk/KDE/kdepimlibs/akonadi/xdgbasedirs_p.h (removed), trunk/kdesupport/akonadi/server/src/handler.cpp, trunk/kdesupport/akonadi/server/akonadictl/CMakeLists.txt, trunk/kdesupport/akonadi/server/src/handler/create.cpp, @@ -1356,10 +3006,10 @@ trunk/KDE/kdepim/akonadi/server/src (removed), trunk/kdesupport/akonadi/server/src/handler/select.cpp, trunk/kdesupport/akonadi/server/src/handler/subscribe.cpp, - trunk/kdesupport/akonadi/server/src/handler/status.cpp, trunk/kdesupport/akonadi/server/src/handler/akappend.cpp, - trunk/kdesupport/akonadi/server/src/handler/searchpersistent.cpp, + trunk/kdesupport/akonadi/server/src/handler/status.cpp, trunk/kdesupport/akonadi/server/control/agentinfo.cpp, + trunk/kdesupport/akonadi/server/src/handler/searchpersistent.cpp, trunk/KDE/kdepim/akonadi/server/tests (removed), trunk/kdesupport/akonadi/server/src/handler/fetch.cpp, trunk/KDE/kdepimlibs/akonadi/interfaces/org.kde.Akonadi.Agent.Status.xml diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/BasicFindPackageVersion.cmake.in /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/BasicFindPackageVersion.cmake.in --- akonadi-1.1.1/cmake/modules/BasicFindPackageVersion.cmake.in 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/cmake/modules/BasicFindPackageVersion.cmake.in 2009-07-28 17:51:58.000000000 +0100 @@ -0,0 +1,30 @@ +# This is a very basic file for the new style find_package() search mode, +# i.e. Config-mode. It is used by MACRO_WRITE_BASIC_CMAKE_VERSION_FILE() from +# MacroWriteBasicCMakeVersionFile.cmake. +# In this mode find_package() searches for a Config.cmake +# file and an associated Version.cmake file, which it loads to check +# the version number. +# This file can be used with configure_file() to generate such a file for a project +# with very basic logic. +# It sets PACKAGE_VERSION_EXACT if the current version string and the requested +# version string are exactly the same and it sets PACKAGE_VERSION_COMPATIBLE +# if the current version is >= requested version. +# If this is not good enough for your project, you need to write your own +# improved Version.cmake file. +# This file requires the following three variables to be set: +# PROJECT_VERSION_MAJOR +# PROJECT_VERSION_MINOR +# PROJECT_VERSION_PATCH + + +set(PACKAGE_VERSION @PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@) + +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}") +endif("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" ) + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/FindAutomoc4.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/FindAutomoc4.cmake --- akonadi-1.1.1/cmake/modules/FindAutomoc4.cmake 2009-01-21 18:29:02.000000000 +0000 +++ akonadi-1.2.0/cmake/modules/FindAutomoc4.cmake 2009-07-28 17:51:58.000000000 +0100 @@ -3,20 +3,42 @@ # # AUTOMOC4_FOUND - automoc4 has been found # AUTOMOC4_EXECUTABLE - the automoc4 tool +# AUTOMOC4_VERSION - the full version of automoc4 +# AUTOMOC4_VERSION_MAJOR, AUTOMOC4_VERSION_MINOR, AUTOMOC4_VERSION_PATCH - AUTOMOC4_VERSION +# broken into its components # # It also adds the following macros # AUTOMOC4( ) # Use this to run automoc4 on all files contained in the list . # -# AUTOMOC4_MOC_HEADERS( header1.h header2.h) +# AUTOMOC4_MOC_HEADERS( header1.h header2.h ...) # Use this to add more header files to be processed with automoc4. +# +# AUTOMOC4_ADD_EXECUTABLE( src1 src2 ...) +# This macro does the same as ADD_EXECUTABLE, but additionally +# adds automoc4 handling for all source files. +# +# AUTOMOC4_ADD_LIBRARY( src1 src2 ...) +# This macro does the same as ADD_LIBRARY, but additionally +# adds automoc4 handling for all source files. +# Internal helper macro, may change or be removed anytime: +# _ADD_AUTOMOC4_TARGET( ) +# +# Since version 0.9.88: +# The following two macros are only to be used for KDE4 projects +# and do something which makes sure automoc4 works for KDE. Don't +# use them anywhere else. +# _AUTOMOC4_KDE4_PRE_TARGET_HANDLING( ) +# _AUTOMOC4_KDE4_POST_TARGET_HANDLING() -# Copyright (c) 2008, Alexander Neundorf, + +# Copyright (c) 2008-2009, Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. + # check if we are inside KDESupport if("${KDESupport_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") # when building this project as part of kdesupport diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/FindLibraryWithDebug.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/FindLibraryWithDebug.cmake --- akonadi-1.1.1/cmake/modules/FindLibraryWithDebug.cmake 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/cmake/modules/FindLibraryWithDebug.cmake 2009-07-28 17:51:58.000000000 +0100 @@ -0,0 +1,113 @@ +# +# FIND_LIBRARY_WITH_DEBUG +# -> enhanced FIND_LIBRARY to allow the search for an +# optional debug library with a WIN32_DEBUG_POSTFIX similar +# to CMAKE_DEBUG_POSTFIX when creating a shared lib +# it has to be the second and third argument + +# Copyright (c) 2007, Christian Ehrlicher, +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +MACRO(FIND_LIBRARY_WITH_DEBUG var_name win32_dbg_postfix_name dgb_postfix libname) + + IF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + + # no WIN32_DEBUG_POSTFIX -> simply pass all arguments to FIND_LIBRARY + FIND_LIBRARY(${var_name} + ${win32_dbg_postfix_name} + ${dgb_postfix} + ${libname} + ${ARGN} + ) + + ELSE(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + + IF(NOT WIN32) + # on non-win32 we don't need to take care about WIN32_DEBUG_POSTFIX + + FIND_LIBRARY(${var_name} ${libname} ${ARGN}) + + ELSE(NOT WIN32) + + # 1. get all possible libnames + SET(args ${ARGN}) + SET(newargs "") + SET(libnames_release "") + SET(libnames_debug "") + + LIST(LENGTH args listCount) + + IF("${libname}" STREQUAL "NAMES") + SET(append_rest 0) + LIST(APPEND args " ") + + FOREACH(i RANGE ${listCount}) + LIST(GET args ${i} val) + + IF(append_rest) + LIST(APPEND newargs ${val}) + ELSE(append_rest) + IF("${val}" STREQUAL "PATHS") + LIST(APPEND newargs ${val}) + SET(append_rest 1) + ELSE("${val}" STREQUAL "PATHS") + LIST(APPEND libnames_release "${val}") + LIST(APPEND libnames_debug "${val}${dgb_postfix}") + ENDIF("${val}" STREQUAL "PATHS") + ENDIF(append_rest) + + ENDFOREACH(i) + + ELSE("${libname}" STREQUAL "NAMES") + + # just one name + LIST(APPEND libnames_release "${libname}") + LIST(APPEND libnames_debug "${libname}${dgb_postfix}") + + SET(newargs ${args}) + + ENDIF("${libname}" STREQUAL "NAMES") + + # search the release lib + FIND_LIBRARY(${var_name}_RELEASE + NAMES ${libnames_release} + ${newargs} + ) + + # search the debug lib + FIND_LIBRARY(${var_name}_DEBUG + NAMES ${libnames_debug} + ${newargs} + ) + + IF(${var_name}_RELEASE AND ${var_name}_DEBUG) + + # both libs found + SET(${var_name} optimized ${${var_name}_RELEASE} + debug ${${var_name}_DEBUG}) + + ELSE(${var_name}_RELEASE AND ${var_name}_DEBUG) + + IF(${var_name}_RELEASE) + + # only release found + SET(${var_name} ${${var_name}_RELEASE}) + + ELSE(${var_name}_RELEASE) + + # only debug (or nothing) found + SET(${var_name} ${${var_name}_DEBUG}) + + ENDIF(${var_name}_RELEASE) + + ENDIF(${var_name}_RELEASE AND ${var_name}_DEBUG) + + MARK_AS_ADVANCED(${var_name}_RELEASE) + MARK_AS_ADVANCED(${var_name}_DEBUG) + + ENDIF(NOT WIN32) + + ENDIF(NOT "${win32_dbg_postfix_name}" STREQUAL "WIN32_DEBUG_POSTFIX") + +ENDMACRO(FIND_LIBRARY_WITH_DEBUG) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/FindQt4.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/FindQt4.cmake --- akonadi-1.1.1/cmake/modules/FindQt4.cmake 2009-01-21 18:29:02.000000000 +0000 +++ akonadi-1.2.0/cmake/modules/FindQt4.cmake 2009-07-28 17:51:58.000000000 +0100 @@ -623,6 +623,7 @@ PATHS ${QT_INCLUDE_DIR}/QtDBus ${QT_HEADERS_DIR}/QtDBus + ${QT_LIBRARY_DIR}/QtDBus.framework/Headers NO_DEFAULT_PATH ) # Set QT_QTASSISTANTCLIENT_INCLUDE_DIR diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/FindSoprano.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/FindSoprano.cmake --- akonadi-1.1.1/cmake/modules/FindSoprano.cmake 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/cmake/modules/FindSoprano.cmake 2009-07-28 17:51:58.000000000 +0100 @@ -0,0 +1,220 @@ +# +# Find an installation of Soprano +# +# Sets the following variables: +# Soprano_FOUND - true is Soprano has been found +# SOPRANO_INCLUDE_DIR - The include directory +# SOPRANO_LIBRARIES - The Soprano core library to link to (libsoprano) +# SOPRANO_INDEX_LIBRARIES - The Soprano index library (libsopranoindex) +# SOPRANO_CLIENT_LIBRARIES - The Soprano client library (libsopranoclient) +# SOPRANO_SERVER_LIBRARIES - The Soprano server library (libsopranoserver) +# SOPRANO_VERSION - The Soprano version (string value) +# +# SOPRANO_PLUGIN_NQUADPARSER_FOUND - true if the nquadparser plugin is found +# SOPRANO_PLUGIN_NQUADSERIALIZER_FOUND - true if the nquadserializer plugin is found +# SOPRANO_PLUGIN_RAPTORPARSER_FOUND - true if the raptorparser plugin is found +# SOPRANO_PLUGIN_RAPTORSERIALIZER_FOUND - true if the raptorserializer plugin is found +# SOPRANO_PLUGIN_REDLANDBACKEND_FOUND - true if the redlandbackend plugin is found +# SOPRANO_PLUGIN_SESAME2BACKEND_FOUND - true if the sesame2backend plugin is found +# SOPRANO_PLUGIN_VIRTUOSOBACKEND_FOUND - true if the virtuosobackend plugin is found +# +# Options: +# Set SOPRANO_MIN_VERSION to set the minimum required Soprano version (default: 1.99) +# + +# Copyright (c) 2008, Sebastian Trueg, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +#if(SOPRANO_INCLUDE_DIR AND SOPRANO_LIBRARIES AND SOPRANO_INDEX_LIBRARIES AND SOPRANO_SERVER_LIBRARIES) + + # read from cache +# set(Soprano_FOUND TRUE) +# set(SopranoServer_FOUND TRUE) +# set(SopranoClient_FOUND TRUE) +# set(SopranoIndex_FOUND TRUE) + +#else(SOPRANO_INCLUDE_DIR AND SOPRANO_LIBRARIES AND SOPRANO_INDEX_LIBRARIES AND SOPRANO_SERVER_LIBRARIES) + include(FindLibraryWithDebug) + + # have packageconfig set variables to find Soprano: + # package config dirs are used as secondary search paths after install_dir + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(Soprano_PKGCONF soprano) + endif() + + find_path(SOPRANO_INCLUDE_DIR + NAMES + soprano/soprano.h + HINTS + ${INCLUDE_INSTALL_DIR} + ${Soprano_PKGCONF_INCLUDE_DIRS} + ) + + find_library_with_debug(SOPRANO_INDEX_LIBRARIES + WIN32_DEBUG_POSTFIX d + NAMES + sopranoindex + HINTS + ${LIB_INSTALL_DIR} + ${Soprano_PKGCONF_LIBRARY_DIRS} + ) + + find_library_with_debug(SOPRANO_CLIENT_LIBRARIES + WIN32_DEBUG_POSTFIX d + NAMES + sopranoclient + HINTS + ${LIB_INSTALL_DIR} + ${Soprano_PKGCONF_LIBRARY_DIRS} + ) + + find_library_with_debug(SOPRANO_LIBRARIES + WIN32_DEBUG_POSTFIX d + NAMES soprano + HINTS + ${LIB_INSTALL_DIR} + ${Soprano_PKGCONF_LIBRARY_DIRS} + ) + + find_library_with_debug(SOPRANO_SERVER_LIBRARIES + WIN32_DEBUG_POSTFIX d + NAMES + sopranoserver + HINTS + ${LIB_INSTALL_DIR} + ${Soprano_PKGCONF_LIBRARY_DIRS} + ) + + # check for all the libs as required to make sure that we do not try to compile with an old version + + if(SOPRANO_INCLUDE_DIR AND SOPRANO_LIBRARIES) + set(Soprano_FOUND TRUE) + endif(SOPRANO_INCLUDE_DIR AND SOPRANO_LIBRARIES) + + if(Soprano_FOUND AND SOPRANO_INDEX_LIBRARIES) + set(SopranoIndex_FOUND TRUE) + endif(Soprano_FOUND AND SOPRANO_INDEX_LIBRARIES) + + if(Soprano_FOUND AND SOPRANO_CLIENT_LIBRARIES) + set(SopranoClient_FOUND TRUE) + endif(Soprano_FOUND AND SOPRANO_CLIENT_LIBRARIES) + + if(Soprano_FOUND AND SOPRANO_SERVER_LIBRARIES) + set(SopranoServer_FOUND TRUE) + endif(Soprano_FOUND AND SOPRANO_SERVER_LIBRARIES) + + # check Soprano version + + # We set a default for the minimum required version to be backwards compatible + if(NOT SOPRANO_MIN_VERSION) + set(SOPRANO_MIN_VERSION "1.99") + endif(NOT SOPRANO_MIN_VERSION) + + if(Soprano_FOUND) + file(READ ${SOPRANO_INCLUDE_DIR}/soprano/version.h SOPRANO_VERSION_CONTENT) + string(REGEX MATCH "SOPRANO_VERSION_STRING \".*\"\n" SOPRANO_VERSION_MATCH ${SOPRANO_VERSION_CONTENT}) + if(SOPRANO_VERSION_MATCH) + string(REGEX REPLACE "SOPRANO_VERSION_STRING \"(.*)\"\n" "\\1" SOPRANO_VERSION ${SOPRANO_VERSION_MATCH}) + if(SOPRANO_VERSION STRLESS "${SOPRANO_MIN_VERSION}") + set(Soprano_FOUND FALSE) + if(Soprano_FIND_REQUIRED) + message(FATAL_ERROR "Soprano version ${SOPRANO_VERSION} is too old. Please install ${SOPRANO_MIN_VERSION} or newer") + else(Soprano_FIND_REQUIRED) + message(STATUS "Soprano version ${SOPRANO_VERSION} is too old. Please install ${SOPRANO_MIN_VERSION} or newer") + endif(Soprano_FIND_REQUIRED) + endif(SOPRANO_VERSION STRLESS "${SOPRANO_MIN_VERSION}") + endif(SOPRANO_VERSION_MATCH) + endif(Soprano_FOUND) + + + #look for parser plugins + if(Soprano_FOUND) + find_path(SOPRANO_PLUGIN_DIR + NAMES + soprano/plugins + PATHS + ${SOPRANO_INCLUDE_DIR}/../share + ${SHARE_INSTALL_PREFIX} + /usr/share + /usr/local/share + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) + set(SOPRANO_PLUGIN_DIR "${SOPRANO_PLUGIN_DIR}/soprano/plugins") + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/nquadparser.desktop) + set(SOPRANO_PLUGIN_NQUADPARSER_FOUND TRUE) + set(_plugins "${_plugins} nquadparser") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/nquadparser.desktop) + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/nquadserializer.desktop) + set(SOPRANO_PLUGIN_NQUADSERIALIZER_FOUND TRUE) + set(_plugins "${_plugins} nquadserializer") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/nquadserializer.desktop) + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/raptorparser.desktop) + set(SOPRANO_PLUGIN_RAPTORPARSER_FOUND TRUE) + set(_plugins "${_plugins} raptorparser") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/raptorparser.desktop) + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/raptorserializer.desktop) + set(SOPRANO_PLUGIN_RAPTORSERIALIZER_FOUND TRUE) + set(_plugins "${_plugins} raptorserializer") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/raptorserializer.desktop) + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/redlandbackend.desktop) + set(SOPRANO_PLUGIN_REDLANDBACKEND_FOUND TRUE) + set(_plugins "${_plugins} redlandbackend") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/redlandbackend.desktop) + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/sesame2backend.desktop) + set(SOPRANO_PLUGIN_SESAME2BACKEND_FOUND TRUE) + set(_plugins "${_plugins} sesame2backend") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/sesame2backend.desktop) + + if(EXISTS ${SOPRANO_PLUGIN_DIR}/virtuosobackend.desktop) + set(SOPRANO_PLUGIN_VIRTUOSOBACKEND_FOUND TRUE) + set(_plugins "${_plugins} virtuosobackend") + endif(EXISTS ${SOPRANO_PLUGIN_DIR}/virtuosobackend.desktop) + + endif(Soprano_FOUND) + + if(Soprano_FOUND) + if(NOT Soprano_FIND_QUIETLY) + message(STATUS "Found Soprano version ${SOPRANO_VERSION}: ${SOPRANO_LIBRARIES}") + message(STATUS "Found Soprano includes: ${SOPRANO_INCLUDE_DIR}") + message(STATUS "Found Soprano Index: ${SOPRANO_INDEX_LIBRARIES}") + message(STATUS "Found Soprano Client: ${SOPRANO_CLIENT_LIBRARIES}") + message(STATUS "Found Soprano Plugin Dir: ${SOPRANO_PLUGIN_DIR}") + message(STATUS "Found Soprano Plugins:${_plugins}") + endif(NOT Soprano_FIND_QUIETLY) + else(Soprano_FOUND) + if(Soprano_FIND_REQUIRED) + if(NOT SOPRANO_INCLUDE_DIR) + message(FATAL_ERROR "Could not find Soprano includes.") + endif(NOT SOPRANO_INCLUDE_DIR) + if(NOT SOPRANO_LIBRARIES) + message(FATAL_ERROR "Could not find Soprano library.") + endif(NOT SOPRANO_LIBRARIES) + else(Soprano_FIND_REQUIRED) + if(NOT SOPRANO_INCLUDE_DIR) + message(STATUS "Could not find Soprano includes.") + endif(NOT SOPRANO_INCLUDE_DIR) + if(NOT SOPRANO_LIBRARIES) + message(STATUS "Could not find Soprano library.") + endif(NOT SOPRANO_LIBRARIES) + endif(Soprano_FIND_REQUIRED) + endif(Soprano_FOUND) + +mark_as_advanced(SOPRANO_CLIENT_LIBRARIES + SOPRANO_INDEX_LIBRARIES + SOPRANO_LIBRARIES + SOPRANO_SERVER_LIBRARIES + SOPRANO_INCLUDE_DIR + SOPRANO_PLUGIN_DIR) + +#endif(SOPRANO_INCLUDE_DIR AND SOPRANO_LIBRARIES AND SOPRANO_INDEX_LIBRARIES AND SOPRANO_SERVER_LIBRARIES) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/InstallSettings.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/InstallSettings.cmake --- akonadi-1.1.1/cmake/modules/InstallSettings.cmake 2009-01-21 18:29:02.000000000 +0000 +++ akonadi-1.2.0/cmake/modules/InstallSettings.cmake 2009-07-28 17:51:58.000000000 +0100 @@ -3,134 +3,88 @@ # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. -set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) +set (LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)") if (WIN32) -# use relative install prefix to avoid hardcoded install paths in cmake_install.cmake files + # use relative install prefix to avoid hardcoded install paths in cmake_install.cmake files - set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" ) # The subdirectory relative to the install prefix where libraries will be installed (default is ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}) + set (EXEC_INSTALL_PREFIX "") # Base directory for executables and libraries - set(EXEC_INSTALL_PREFIX "" ) # Base directory for executables and libraries - set(SHARE_INSTALL_PREFIX "share" ) # Base directory for files which go to share/ - set(BIN_INSTALL_DIR "bin" ) # The install dir for executables (default ${EXEC_INSTALL_PREFIX}/bin) - set(SBIN_INSTALL_DIR "sbin" ) # The install dir for system executables (default ${EXEC_INSTALL_PREFIX}/sbin) - - set(LIBEXEC_INSTALL_DIR "${BIN_INSTALL_DIR}" ) # The subdirectory relative to the install prefix where libraries will be installed (default is ${BIN_INSTALL_DIR}) - set(INCLUDE_INSTALL_DIR "include" ) # The subdirectory to the header prefix - - set(PLUGIN_INSTALL_DIR "lib${LIB_SUFFIX}/kde4" ) # "The subdirectory relative to the install prefix where plugins will be installed (default is ${LIB_INSTALL_DIR}/kde4) - set(CONFIG_INSTALL_DIR "share/config" ) # The config file install dir - set(DATA_INSTALL_DIR "share/apps" ) # The parent directory where applications can install their data - set(HTML_INSTALL_DIR "share/doc/HTML" ) # The HTML install dir for documentation - set(ICON_INSTALL_DIR "share/icons" ) # The icon install dir (default ${SHARE_INSTALL_PREFIX}/share/icons/) - set(KCFG_INSTALL_DIR "share/config.kcfg" ) # The install dir for kconfig files - set(LOCALE_INSTALL_DIR "share/locale" ) # The install dir for translations - set(MIME_INSTALL_DIR "share/mimelnk" ) # The install dir for the mimetype desktop files - set(SERVICES_INSTALL_DIR "share/kde4/services" ) # The install dir for service (desktop, protocol, ...) files - set(SERVICETYPES_INSTALL_DIR "share/kde4/servicetypes" ) # The install dir for servicestypes desktop files - set(SOUND_INSTALL_DIR "share/sounds" ) # The install dir for sound files - set(TEMPLATES_INSTALL_DIR "share/templates" ) # The install dir for templates (Create new file...) - set(WALLPAPER_INSTALL_DIR "share/wallpapers" ) # The install dir for wallpapers - set(DEMO_INSTALL_DIR "share/demos" ) # The install dir for demos - set(KCONF_UPDATE_INSTALL_DIR "share/apps/kconf_update" ) # The kconf_update install dir - set(AUTOSTART_INSTALL_DIR "share/autostart" ) # The install dir for autostart files - - set(XDG_APPS_INSTALL_DIR "share/applications/kde4" ) # The XDG apps dir - set(XDG_DIRECTORY_INSTALL_DIR "share/desktop-directories" ) # The XDG directory - set(XDG_MIME_INSTALL_DIR "share/mime/packages" ) # The install dir for the xdg mimetypes - - set(SYSCONF_INSTALL_DIR "etc" ) # The kde sysconfig install dir (default /etc) - set(MAN_INSTALL_DIR "share/man" ) # The kde man install dir (default ${SHARE_INSTALL_PREFIX}/man/) - set(INFO_INSTALL_DIR "share/info" ) # The kde info install dir (default ${SHARE_INSTALL_PREFIX}/info)") - set(DBUS_INTERFACES_INSTALL_DIR "share/dbus-1/interfaces" ) # The kde dbus interfaces install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/interfaces)") - set(DBUS_SERVICES_INSTALL_DIR "share/dbus-1/services" ) # The kde dbus services install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/services)") + ## ${CMAKE_INSTALL_PREFIX}/* + set (BIN_INSTALL_DIR "bin") # The install dir for executables (default ${EXEC_INSTALL_PREFIX}/bin) + set (INCLUDE_INSTALL_DIR "include") # The subdirectory to the header prefix + set (LIB_INSTALL_DIR "lib${LIB_SUFFIX}") # The subdirectory relative to the install prefix where libraries will be installed (default is ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}) + set (SHARE_INSTALL_PREFIX "share") # Base directory for files which go to share/ + + ## ${CMAKE_INSTALL_PREFIX}/share/* + set (CONFIG_INSTALL_DIR "share/config") # The config file install dir + set (DBUS_INTERFACES_INSTALL_DIR "share/dbus-1/interfaces") # The DBus interfaces install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/interfaces)") + set (DBUS_SERVICES_INSTALL_DIR "share/dbus-1/services") # The DBus services install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/services)") + set (XDG_MIME_INSTALL_DIR "share/mime/packages") # The install dir for the xdg mimetypes else (WIN32) - - # this macro implements some very special logic how to deal with the cache - # by default the various install locations inherit their value from theit "parent" variable - # so if you set CMAKE_INSTALL_PREFIX, then EXEC_INSTALL_PREFIX, PLUGIN_INSTALL_DIR will - # calculate their value by appending subdirs to CMAKE_INSTALL_PREFIX - # this would work completely without using the cache. - # but if somebody wants e.g. a different EXEC_INSTALL_PREFIX this value has to go into - # the cache, otherwise it will be forgotten on the next cmake run. - # Once a variable is in the cache, it doesn't depend on its "parent" variables - # anymore and you can only change it by editing it directly. - # this macro helps in this regard, because as long as you don't set one of the - # variables explicitely to some location, it will always calculate its value from its - # parents. So modifying CMAKE_INSTALL_PREFIX later on will have the desired effect. - # But once you decide to set e.g. EXEC_INSTALL_PREFIX to some special location - # this will go into the cache and it will no longer depend on CMAKE_INSTALL_PREFIX. - macro(_SET_FANCY _var _value _comment) - set(predefinedvalue "${_value}") - - if (NOT DEFINED ${_var}) - set(${_var} ${predefinedvalue}) - else (NOT DEFINED ${_var}) - set(${_var} "${${_var}}" CACHE PATH "${_comment}") - endif (NOT DEFINED ${_var}) - endmacro(_SET_FANCY) - - - _set_fancy(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Base directory for executables and libraries") - _set_fancy(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share" "Base directory for files which go to share/") - _set_fancy(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" "The install dir for executables (default ${EXEC_INSTALL_PREFIX}/bin)") - _set_fancy(SBIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/sbin" "The install dir for system executables (default ${EXEC_INSTALL_PREFIX}/sbin)") - _set_fancy(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" "The subdirectory relative to the install prefix where libraries will be installed (default is ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX})") - _set_fancy(LIBEXEC_INSTALL_DIR "${LIB_INSTALL_DIR}/kde4/libexec" "The subdirectory relative to the install prefix where libraries will be installed (default is ${LIB_INSTALL_DIR}/kde4/libexec)") - _set_fancy(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" "The subdirectory to the header prefix") - - _set_fancy(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/kde4" "The subdirectory relative to the install prefix where plugins will be installed (default is ${LIB_INSTALL_DIR}/kde4)") - _set_fancy(CONFIG_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/config" "The config file install dir") - _set_fancy(DATA_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/apps" "The parent directory where applications can install their data") - _set_fancy(HTML_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/doc/HTML" "The HTML install dir for documentation") - _set_fancy(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" "The icon install dir (default ${SHARE_INSTALL_PREFIX}/share/icons/)") - _set_fancy(KCFG_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/config.kcfg" "The install dir for kconfig files") - _set_fancy(LOCALE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/locale" "The install dir for translations") - _set_fancy(MIME_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/mimelnk" "The install dir for the mimetype desktop files") - _set_fancy(SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/kde4/services" "The install dir for service (desktop, protocol, ...) files") - _set_fancy(SERVICETYPES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/kde4/servicetypes" "The install dir for servicestypes desktop files") - _set_fancy(SOUND_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/sounds" "The install dir for sound files") - _set_fancy(TEMPLATES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/templates" "The install dir for templates (Create new file...)") - _set_fancy(WALLPAPER_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/wallpapers" "The install dir for wallpapers") - _set_fancy(DEMO_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/demos" "The install dir for demos") - _set_fancy(KCONF_UPDATE_INSTALL_DIR "${DATA_INSTALL_DIR}/kconf_update" "The kconf_update install dir") - _set_fancy(AUTOSTART_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/autostart" "The install dir for autostart files") - - _set_fancy(XDG_APPS_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/applications/kde4" "The XDG apps dir") - _set_fancy(XDG_DIRECTORY_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/desktop-directories" "The XDG directory") - _set_fancy(XDG_MIME_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/mime/packages" "The install dir for the xdg mimetypes") - - _set_fancy(SYSCONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/etc" "The kde sysconfig install dir (default ${CMAKE_INSTALL_PREFIX}/etc)") - _set_fancy(MAN_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/man" "The kde man install dir (default ${SHARE_INSTALL_PREFIX}/man/)") - _set_fancy(INFO_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/info" "The kde info install dir (default ${SHARE_INSTALL_PREFIX}/info)") - _set_fancy(DBUS_INTERFACES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/interfaces" "The kde dbus interfaces install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/interfaces)") - _set_fancy(DBUS_SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" "The kde dbus services install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/services)") + # this macro implements some very special logic how to deal with the cache + # by default the various install locations inherit their value from theit "parent" variable + # so if you set CMAKE_INSTALL_PREFIX, then EXEC_INSTALL_PREFIX, PLUGIN_INSTALL_DIR will + # calculate their value by appending subdirs to CMAKE_INSTALL_PREFIX + # this would work completely without using the cache. + # but if somebody wants e.g. a different EXEC_INSTALL_PREFIX this value has to go into + # the cache, otherwise it will be forgotten on the next cmake run. + # Once a variable is in the cache, it doesn't depend on its "parent" variables + # anymore and you can only change it by editing it directly. + # this macro helps in this regard, because as long as you don't set one of the + # variables explicitely to some location, it will always calculate its value from its + # parents. So modifying CMAKE_INSTALL_PREFIX later on will have the desired effect. + # But once you decide to set e.g. EXEC_INSTALL_PREFIX to some special location + # this will go into the cache and it will no longer depend on CMAKE_INSTALL_PREFIX. + macro(_SET_FANCY _var _value _comment) + SET (predefinedvalue "${_value}") + + if (NOT DEFINED ${_var}) + SET (${_var} ${predefinedvalue}) + else (NOT DEFINED ${_var}) + SET (${_var} "${${_var}}" CACHE PATH "${_comment}") + endif (NOT DEFINED ${_var}) + endmacro(_SET_FANCY) + + + _set_fancy(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Base directory for executables and libraries") + + ## ${CMAKE_INSTALL_PREFIX}/* + _set_fancy(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" "The install dir for executables (default ${EXEC_INSTALL_PREFIX}/bin)") + _set_fancy(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" "The subdirectory to the header prefix") + _set_fancy(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" "The subdirectory relative to the install prefix where libraries will be installed (default is ${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX})") + _set_fancy(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share" "Base directory for files which go to share/") + + + ## ${CMAKE_INSTALL_PREFIX}/share/* + _set_fancy(CONFIG_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/config" "The config file install dir") + _set_fancy(DBUS_INTERFACES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/interfaces" "The DBus interfaces install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/interfaces)") + _set_fancy(DBUS_SERVICES_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" "The DBus services install dir (default ${SHARE_INSTALL_PREFIX}/dbus-1/services)") + _set_fancy(XDG_MIME_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/mime/packages" "The install dir for the xdg mimetypes") endif (WIN32) # The INSTALL_TARGETS_DEFAULT_ARGS variable should be used when libraries are installed. # The arguments are also ok for regular executables, i.e. executables which don't go -# into sbin/ or libexec/, but for installing executables the basic syntax +# into sbin/ or libexec/, but for installing executables the basic syntax # INSTALL(TARGETS kate DESTINATION "${BIN_INSTALL_DIR}") # is enough, so using this variable there doesn't help a lot. # The variable must not be used for installing plugins. # Usage is like this: -# install(TARGETS kdecore kdeui ${INSTALL_TARGETS_DEFAULT_ARGS} ) +# install(TARGETS kdecore kdeui ${INSTALL_TARGETS_DEFAULT_ARGS}) # # This will install libraries correctly under UNIX, OSX and Windows (i.e. dll's go # into bin/. # Later on it will be possible to extend this for installing OSX frameworks -# The COMPONENT Devel argument has the effect that static libraries belong to the +# The COMPONENT Devel argument has the effect that static libraries belong to the # "Devel" install component. If we use this also for all install() commands # for header files, it will be possible to install -# -everything: make install OR cmake -P cmake_install.cmake -# -only the development files: cmake -DCOMPONENT=Devel -P cmake_install.cmake -# -everything except the development files: cmake -DCOMPONENT=Unspecified -P cmake_install.cmake +# -everything: make install OR cmake -P cmake_install.cmake +# -only the development files: cmake -DCOMPONENT=Devel -P cmake_install.cmake +# -everything except the development files: cmake -DCOMPONENT=Unspecified -P cmake_install.cmake # This can then also be used for packaging with cpack. -set(INSTALL_TARGETS_DEFAULT_ARGS RUNTIME DESTINATION "${BIN_INSTALL_DIR}" - LIBRARY DESTINATION "${LIB_INSTALL_DIR}" - ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" COMPONENT Devel ) - - +SET (INSTALL_TARGETS_DEFAULT_ARGS RUNTIME DESTINATION "${BIN_INSTALL_DIR}" +LIBRARY DESTINATION "${LIB_INSTALL_DIR}" +ARCHIVE DESTINATION "${LIB_INSTALL_DIR}" COMPONENT Devel) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/cmake/modules/MacroWriteBasicCMakeVersionFile.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/cmake/modules/MacroWriteBasicCMakeVersionFile.cmake --- akonadi-1.1.1/cmake/modules/MacroWriteBasicCMakeVersionFile.cmake 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/cmake/modules/MacroWriteBasicCMakeVersionFile.cmake 2009-07-28 17:51:58.000000000 +0100 @@ -0,0 +1,22 @@ +# MACRO_WRITE_BASIC_CMAKE_VERSION_FILE( _filename _major _minor _patch) +# Writes a file for use as ConfigVersion.cmake file to <_filename>. +# See the documentation of FIND_PACKAGE() for details on this. +# _filename is the output filename, it should be in the build tree. +# _major is the major version number of the project to be installed +# _minor is the minor version number of the project to be installed +# _patch is the patch version number of the project to be installed +# + +# Copyright (c) 2008, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +get_filename_component(_currentListFileDir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(MACRO_WRITE_BASIC_CMAKE_VERSION_FILE _filename _major _minor _patch) + set(PROJECT_VERSION_MAJOR ${_major}) + set(PROJECT_VERSION_MINOR ${_minor}) + set(PROJECT_VERSION_PATCH ${_patch}) + configure_file(${_currentListFileDir}/BasicFindPackageVersion.cmake.in "${_filename}" @ONLY) +endfunction(MACRO_WRITE_BASIC_CMAKE_VERSION_FILE _major _minor _patch) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/CMakeLists.txt --- akonadi-1.1.1/CMakeLists.txt 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/CMakeLists.txt 2009-07-28 17:52:13.000000000 +0100 @@ -1,150 +1,203 @@ project(Akonadi) -cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR) - -# enable unit tests -enable_testing() +cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") +set(CMAKE_MODULE_PATH "${Akonadi_SOURCE_DIR}/cmake/modules") + + +############### Build Options ############### + +option(AKONADI_BUILD_TESTS "Build the Akonadi unit tests." TRUE) +if (AKONADI_BUILD_TESTS) + enable_testing() +endif (AKONADI_BUILD_TESTS) + +if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} VERSION_GREATER 2.6.2) + option(USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR "Prefer to install the Config.cmake files to lib/cmake/ instead of lib//cmake" TRUE) +endif(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} VERSION_GREATER 2.6.2) + -######## find packages #################### +############### CMake Macros ############### include(MacroLogFeature) +include(InstallSettings) +include(CheckIncludeFiles) +include(MacroWriteBasicCMakeVersionFile) + +############### The Akonadi version (used in AkonadiConfig.cmake) ############### + +set(AKONADI_VERSION_MAJOR "1") +set(AKONADI_VERSION_MINOR "2") +set(AKONADI_VERSION_PATCH "0") +set(AKONADI_VERSION "${AKONADI_VERSION_MAJOR}.${AKONADI_VERSION_MINOR}.${AKONADI_VERSION_PATCH}") + +# If Subversion is installed, and a '.svn' directory is found, +# we append the SVN revision to AKONADI_VERSION_STRING. +set(AKONADI_VERSION_STRING "${AKONADI_VERSION}") +if (EXISTS "${Akonadi_SOURCE_DIR}/.svn") + if (NOT Subversion_FOUND) + find_package( Subversion) + endif (NOT Subversion_FOUND) + if (Subversion_FOUND) + Subversion_WC_INFO( ${PROJECT_SOURCE_DIR} Akonadi) + set(AKONADI_VERSION_STRING "${AKONADI_VERSION_STRING} (revision ${Akonadi_WC_LAST_CHANGED_REV})") + endif (Subversion_FOUND) +endif (EXISTS "${Akonadi_SOURCE_DIR}/.svn") + -set(QT_MIN_VERSION 4.4.0) +############### Find what we need ############### + +#### Qt4 #### +set(QT_MIN_VERSION 4.5.0) find_package(Qt4 REQUIRED) -# properly set up compile flags (QT_DEBUG/QT_NO_DEBUG, ...) -include(${QT_USE_FILE}) + +# We need QtDBus and QtSql +set(QT_USE_QTDBUS TRUE) +set(QT_USE_QTSQL TRUE) if (NOT QT_QTDBUS_FOUND) message(FATAL_ERROR "Akonadi requires Qt4 with QtDBus module in order to built.") endif(NOT QT_QTDBUS_FOUND) +if (NOT QT_QTSQL_FOUND) + message(FATAL_ERROR "Akonadi requires Qt4 with QtSql module in order to built.") +endif(NOT QT_QTSQL_FOUND) +# properly set up compile flags (QT_DEBUG/QT_NO_DEBUG, ...) +include(${QT_USE_FILE}) +#### Soprano #### + +# There are different cases : +# 1/ We're running CMake in kdesupport. The Soprano dir exists and its compilation is enabled. +# 2/ We're running CMake in kdesupport but Soprano won't be built. we look for installed headers. +# 3/ We're just building akonadi. We look for the Soprano headers. + +if("${KDESupport_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") # We ran CMake in kdesupport + if(EXISTS ${KDESupport_SOURCE_DIR}/soprano AND BUILD_soprano) # and we plan to build Soprano + message(STATUS "Soprano is needed to build Akonadi. We will use Soprano from kdesupport.") + set (SOPRANO_INCLUDE_DIR ${KDESupport_SOURCE_DIR}/soprano) + set (SOPRANO_LIBRARIES soprano) + set (Soprano_FOUND TRUE) + endif(EXISTS ${KDESupport_SOURCE_DIR}/soprano AND BUILD_soprano) +endif("${KDESupport_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") + +if(NOT Soprano_FOUND) + find_package(Soprano) +endif(NOT Soprano_FOUND) + +macro_log_feature(Soprano_FOUND "Soprano" "Semantic Desktop Storing" "http://soprano.sourceforge.net" TRUE "" "Soprano is required to build Akonadi") + +#### Automoc #### find_package(Automoc4 REQUIRED) +#### SMI #### set(SHARED_MIME_INFO_MINIMUM_VERSION "0.20") find_package(SharedMimeInfo REQUIRED) +#### XSLTProc #### find_program(XSLTPROC_EXECUTABLE xsltproc) if(NOT XSLTPROC_EXECUTABLE) message(FATAL_ERROR "\nThe command line XSLT processor program 'xsltproc' could not be found.\nPlease install xsltproc.\n") endif(NOT XSLTPROC_EXECUTABLE) +#### Boost #### if(MSVC) # otherwise we get an undefined reference to # boost::program_options::options_description::m_default_line_length set(Boost_USE_STATIC_LIBS ON) + set (_ENABLE_EXCEPTIONS -EHsc) endif(MSVC) -find_package(Boost REQUIRED COMPONENTS program_options) -if(NOT Boost_FOUND) - message(FATAL_ERROR "Akonadi requires the Boost C++ libraries.") -endif(NOT Boost_FOUND) -include_directories(${Boost_INCLUDE_DIR}) + +find_package(Boost COMPONENTS program_options) +macro_log_feature(Boost_FOUND "Boost" "Boost C++ Libraries" "http://www.boost.org" TRUE "" "Akonadi requires the Boost C++ libraries.") + # should be handled by FindBoost.cmake -> cmake bug #8335 if(NOT Boost_USE_STATIC_LIBS) add_definitions(-DBOOST_DYN_LINK) endif(NOT Boost_USE_STATIC_LIBS) -# this one actually sets only install locations -include(InstallSettings) +############### Compilers flags ############### + +if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") + set (_ENABLE_EXCEPTIONS -fexceptions) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-check-new -fno-common") + if(CMAKE_COMPILE_GCOV) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + endif(CMAKE_COMPILE_GCOV) +endif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") -################ configure checks and similar things ############################## -set(AKONADI_LIB_MAJOR_VERSION "1") -set(AKONADI_LIB_MINOR_VERSION "1") -set(AKONADI_LIB_PATCH_VERSION "1") -set(AKONADI_LIB_VERSION_STRING "${AKONADI_LIB_MAJOR_VERSION}.${AKONADI_LIB_MINOR_VERSION}.${AKONADI_LIB_PATCH_VERSION}") - -set( AKONADI_VERSION_STRING "${AKONADI_LIB_VERSION_STRING}" ) -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.svn") - if ( NOT Subversion_FOUND ) - find_package( Subversion ) - endif ( NOT Subversion_FOUND ) - if ( Subversion_FOUND ) - Subversion_WC_INFO( ${PROJECT_SOURCE_DIR} Akonadi ) - set( AKONADI_VERSION_STRING "${AKONADI_LIB_VERSION_STRING} (revision ${Akonadi_WC_LAST_CHANGED_REV})" ) - endif ( Subversion_FOUND ) -endif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.svn") +############### Configure checks ############### + +check_include_files(execinfo.h HAVE_EXECINFO_H) +check_include_files(unistd.h HAVE_UNISTD_H) # set the output paths set (EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) if (WIN32) - set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH} ) + set(LIBRARY_OUTPUT_PATH ${EXECUTABLE_OUTPUT_PATH}) else (WIN32) - set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib ) + set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) endif (WIN32) - # Set up RPATH handling, so the libs are found if they are installed to a non-standard location. # By default cmake builds the targets with full RPATH to everything in the build directory, # but then removes the RPATH when installing. # These two options below make it set the RPATH of the installed targets to all # RPATH directories outside the current CMAKE_BINARY_DIR and also the library # install directory. Alex -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -set(CMAKE_INSTALL_RPATH "${LIB_INSTALL_DIR}" ) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_INSTALL_RPATH "${LIB_INSTALL_DIR}") if(APPLE) set(CMAKE_INSTALL_NAME_DIR ${LIB_INSTALL_DIR}) endif(APPLE) -configure_file(akonadi-prefix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/akonadi-prefix.h) +############### Macros ############### -include(CheckIncludeFiles) -check_include_files(execinfo.h HAVE_EXECINFO_H) -check_include_files(unistd.h HAVE_UNISTD_H) -configure_file(config-akonadi.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-akonadi.h) +macro(MAKE_INSTALL_PATH_ABSOLUTE out in) + if (IS_ABSOLUTE "${in}") + set(${out} "${in}") + else (IS_ABSOLUTE "${in}") + set(${out} "\${AKONADI_INSTALL_DIR}/${in}") + endif (IS_ABSOLUTE "${in}") +endmacro(MAKE_INSTALL_PATH_ABSOLUTE out in) -# maybe the handling for the different compilers could be split out into a separate file, Alex -if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") - set (_ENABLE_EXCEPTIONS -fexceptions) - set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long -std=iso9899:1990 -Wundef -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common") - set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnon-virtual-dtor -Wno-long-long -ansi -Wundef -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wformat-security -fno-check-new -fno-common") -else (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") - if (MSVC) - set (_ENABLE_EXCEPTIONS -EHsc) - endif (MSVC) -endif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") +############### Prepare AkonadiConfig.cmake ############### + +# all the following variables are put into AkonadiConfig.cmake, so +# they are usable by projects using Akonadi. +make_install_path_absolute(AKONADI_BIN_DIR ${BIN_INSTALL_DIR}) +make_install_path_absolute(AKONADI_INCLUDE_DIR ${INCLUDE_INSTALL_DIR}) +make_install_path_absolute(AKONADI_LIB_DIR ${LIB_INSTALL_DIR}) +make_install_path_absolute(AKONADI_CONFIG_DIR ${CONFIG_INSTALL_DIR}) +make_install_path_absolute(AKONADI_DBUS_INTERFACES_DIR ${DBUS_INTERFACES_INSTALL_DIR}) +make_install_path_absolute(AKONADI_DBUS_SERVICES_DIR ${DBUS_SERVICES_INSTALL_DIR}) +make_install_path_absolute(AKONADI_XDG_MIME_DIR ${XDG_MIME_INSTALL_DIR}) + +# Used in configure_file() and install(EXPORT) +#set(AKONADI_TARGET_PREFIX Akonadi__) + + +############### Generate files ############### +configure_file(akonadi-prefix.h.cmake ${Akonadi_BINARY_DIR}/akonadi-prefix.h) +configure_file(config-akonadi.h.cmake ${Akonadi_BINARY_DIR}/config-akonadi.h) +configure_file(AkonadiConfig.cmake.in "${Akonadi_BINARY_DIR}/AkonadiConfig.cmake" @ONLY) -################# build targets ######################## +if (NOT WIN32) + configure_file(${Akonadi_SOURCE_DIR}/akonadi.pc.cmake ${Akonadi_BINARY_DIR}/akonadi.pc @ONLY) +endif (NOT WIN32) -# Use the project specific source and binary directories, they are always the same -# both when building akonadi as part of kdesupport or separate CMAKE_(SOURCE|BINARY)_DIR -# are the root directory of the cmake build tree, i.e. kdesupport/ if akonadi is built -# as part o kdesupport and kdesupport/akonadi/ if it is build separate. -# (CMAKE_CURRENT_(SOURCE|BINARY)_DIR are also always the same). Alex -include_directories(${Akonadi_SOURCE_DIR} ${Akonadi_BINARY_DIR} ${QT_INCLUDES}) - -# if something breaks because the block below is commented out, -# please send me an email, Alex - -# # we need the absolute directories where stuff will be installed too -# # but since the variables which contain the destinations can be relative -# # or absolute paths, we need this macro to make them all absoulte, Alex -# macro(MAKE_INSTALL_PATH_ABSOLUTE out in) -# if (UNIX) -# if ("${in}" MATCHES "^/.*") -# set(${out} "${in}") -# else ("${in}" MATCHES "^/.*") -# set(${out} "\${KDE4_INSTALL_DIR}/${in}") -# endif ("${in}" MATCHES "^/.*") -# else (UNIX) -# if ("${in}" MATCHES "^[a-zA-Z]:.*") -# set(${out} "${in}") -# else ("${in}" MATCHES "^[a-zA-Z]:.*") -# set(${out} "\${KDE4_INSTALL_DIR}/${in}") -# endif ("${in}" MATCHES "^[a-zA-Z]:.*") -# endif (UNIX) -# endmacro(MAKE_INSTALL_PATH_ABSOLUTE out in) -# -# make_install_path_absolute(AKONADI_DBUS_INTERFACES_DIR ${DBUS_INTERFACES_INSTALL_DIR}) -# make_install_path_absolute(AKONADI_DBUS_SERVICES_DIR ${DBUS_SERVICES_INSTALL_DIR}) -# -# set(AKONADI_DBUS_INTERFACES_DIR \"${AKONADI_DBUS_INTERFACES_DIR}\") -# set(AKONADI_DBUS_SERVICES_DIR \"${AKONADI_DBUS_SERVICES_DIR}\") +# this file is used by to check if the installed version can be used. +macro_write_basic_cmake_version_file(${Akonadi_BINARY_DIR}/AkonadiConfigVersion.cmake + ${AKONADI_VERSION_MAJOR} ${AKONADI_VERSION_MINOR} ${AKONADI_VERSION_PATCH}) + +############### build targets ############### + +include_directories(${Akonadi_SOURCE_DIR} ${Akonadi_BINARY_DIR} ${QT_INCLUDES} ${Boost_INCLUDE_DIR}) add_subdirectory(interfaces) add_subdirectory(libs) @@ -153,15 +206,24 @@ add_subdirectory(server) endif (XSLTPROC_EXECUTABLE) -############# install stuff ###################### + +############### install stuff ############### + +if(USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) + set(_AkonadiConfig_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/Akonadi) +else(USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) + set(_AkonadiConfig_INSTALL_DIR ${LIB_INSTALL_DIR}/Akonadi/cmake) +endif(USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) + +install(FILES ${Akonadi_BINARY_DIR}/AkonadiConfigVersion.cmake + ${Akonadi_BINARY_DIR}/AkonadiConfig.cmake + DESTINATION ${_AkonadiConfig_INSTALL_DIR} ) install(FILES akonadi-mime.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR}) -if(NOT WIN32) # pkgconfig file - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/akonadi.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/akonadi.pc @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/akonadi.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig ) +if(NOT WIN32) + install(FILES ${Akonadi_BINARY_DIR}/akonadi.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) endif(NOT WIN32) - macro_display_feature_log() diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/config-akonadi.h.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/config-akonadi.h.cmake --- akonadi-1.1.1/config-akonadi.h.cmake 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/config-akonadi.h.cmake 2009-07-28 17:52:13.000000000 +0100 @@ -1,7 +1,7 @@ #cmakedefine HAVE_EXECINFO_H 1 #cmakedefine HAVE_UNISTD_H 1 -#define AKONADI_MAJOR_VERSION @AKONADI_LIB_MAJOR_VERSION@ -#define AKONADI_MINOR_VERSION @AKONADI_LIB_MINOR_VERSION@ -#define AKONADI_PATCH_VERSION @AKONADI_LIB_PATCH_VERSION@ +#define AKONADI_VERSION_MAJOR @AKONADI_VERSION_MAJOR@ +#define AKONADI_VERSION_MINOR @AKONADI_VERSION_MINOR@ +#define AKONADI_VERSION_PATCH @AKONADI_VERSION_PATCH@ #define AKONADI_VERSION_STRING "@AKONADI_VERSION_STRING@" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/akonadi-dbg.lintian /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/akonadi-dbg.lintian --- akonadi-1.1.1/debian/akonadi-dbg.lintian 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/akonadi-dbg.lintian 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -akonadi-dbg: dbg-package-missing-depends akonadi diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/akonadi-server.installgen /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/akonadi-server.installgen --- akonadi-1.1.1/debian/akonadi-server.installgen 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/akonadi-server.installgen 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +0,0 @@ -# Install dbus XMLs to libakonadi-dev first -depends libakonadi-dev - -dst:usr/bin -dst:etc/ -dst:usr/share diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/changelog /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/changelog --- akonadi-1.1.1/debian/changelog 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/changelog 2009-08-23 19:48:09.000000000 +0100 @@ -1,9 +1,130 @@ +akonadi (1.2.0-0ubuntu1~jaunty1build1) jaunty; urgency=low + + * Jaunty backport + + -- Mieszko Ślusarczyk (spitfire) Sat, 22 Aug 2009 17:40:36 +0200 + +akonadi (1.2.0-0ubuntu1~ppa1) karmic; urgency=low + + * New upstream release: + - bump akonadi version in debian/rules + - bump pkg-kde-tools to 0.4.6 + - bump libsoprano-dev to 2.3.0 + - bump cdbs to 0.4.56 + + -- Alessandro Ghersi Wed, 29 Jul 2009 19:52:18 +0200 + +akonadi (1.1.95-0ubuntu1) karmic; urgency=low + + * New upstream release + - bump akonadi version in debian/rules + - bump libboost-program-options1.35-dev to libboost-program-options1.38-dev + + -- Alessandro Ghersi Wed, 24 Jun 2009 05:14:31 +0200 + +akonadi (1.1.90-0ubuntu1) karmic; urgency=low + + * New upstream release: + - bump akonadi versino in debian/rules + - update libakonadi-dev.install + - refresh 02_hardcode_debian_mysqld_path.diff + + -- Steve Stalcup Thu, 04 Jun 2009 09:47:55 -0400 + +akonadi (1.1.85-0ubuntu1) karmic; urgency=low + + * New upstream release + + -- Jonathan Riddell Wed, 13 May 2009 09:45:47 +0000 + +akonadi (1.1.2-1ubuntu1) karmic; urgency=low + + * Merge from Debian unstable, remaining changes: (LP: #372700) + + Add mysql-server-5.0 as build dependency + + Add mysql-server-5.0 as dependency for akonadi-server + + Hardlink mysqld to mysqld-akonadi (rules) + + Add apparmor profile for mysqld-akonadi (usr.sbin.mysqld-akonadi) + + Add pre and postinst scripts for akonadi-server + + -- Jonathan Thomas Wed, 06 May 2009 09:09:00 -0400 + +akonadi (1.1.2-1) unstable; urgency=low + + * New upstream release. + + +++ Changes by Modestas Vainius: + + * Point Debian Vcs URLs to pkg-kde/trunk (new location). + + +++ Changes by Fathi Boudra: + + * Bump Standards-Version to 3.8.1. No changes needed. + * Update akonadi-dbg package section: debug. + + -- Debian Qt/KDE Maintainers Fri, 01 May 2009 14:00:31 +0200 + +akonadi (1.1.1-2ubuntu2) karmic; urgency=low + + * Add missing comma to build-depends, fixes FTBFS + + -- Jonathan Thomas Tue, 28 Apr 2009 13:21:15 -0400 + +akonadi (1.1.1-2ubuntu1) karmic; urgency=low + + * Merge from Debian unstable, remaining changes: (LP: #367109) + + Add mysql-server-5.0 as build dependency + + Add mysql-server-5.0 as dependency for akonadi-server + + Hardlink mysqld to mysqld-akonadi (rules) + + Add apparmor profile for mysqld-akonadi (usr.sbin.mysqld-akonadi) + + Add pre and postinst scripts for akonadi-server + * Update KUBUNTU-DEBAIN-DIFFERENCES + + -- Jonathan Thomas Sat, 25 Apr 2009 22:25:00 -0400 + +akonadi (1.1.1-2) unstable; urgency=low + + +++ Changes by Modestas Vainius: + + * Switch from internal debian/cdbs/kde.mk to pkg-kde-tools: + - build depend on pkg-kde-tools 0.4; + - remove debian/cdbs directory; + - replace debian/cdbs/kde.mk with + /usr/share/pkg-kde-tools/qt-kde-team/1/debian-qt-kde.mk in debian/rules. + * Remove THIS_SHOULD_GO_TO_UNSTABLE from debian/rules. + + -- Debian Qt/KDE Maintainers Wed, 18 Feb 2009 23:19:08 +0100 + akonadi (1.1.1-0ubuntu3) jaunty; urgency=low * Change build-deps to boost 1.35, LP: #297152 -- Jonathan Riddell Thu, 29 Jan 2009 12:13:53 +0000 +akonadi (1.1.1-1) experimental; urgency=low + + * New upstream release. + + +++ Changes by Modestas Vainius: + + * Bump Standards-Version to 3.8.0: add README.source. + * Build depend on libboost-program-options-dev. + * Bump cmake build depend to 2.6.2. + * Switch to new installgen format. + * Update install files. + * Bump shlibs. + * Make akonadi-server depend on libqt4-sql-mysql. + * Add 02_hardcode_debian_mysqld_path.diff patch to hardcode MySQLd + path instread of depending on mysql-server at build time. + * Bump debian/compat and debhelper build dependency to v7 (to get more + sophisticated debian/tmp handling). + + +++ Changes by Ana Beatriz Guerrero Lopez: + + * Remove no longer needed lintian override. + * Updating packaging copyright years. + + -- Debian Qt/KDE Maintainers Sat, 24 Jan 2009 13:37:05 +0100 + akonadi (1.1.1-0ubuntu2) jaunty; urgency=low * akonadi-server depends on mysql-server-core-5.0 not diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/compat /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/compat --- akonadi-1.1.1/debian/compat 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/compat 2009-08-23 19:48:09.000000000 +0100 @@ -1 +1 @@ -5 +7 diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/control /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/control --- akonadi-1.1.1/debian/control 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/control 2009-08-23 19:48:09.000000000 +0100 @@ -5,14 +5,14 @@ XSBC-Original-Maintainer: Debian Qt/KDE Maintainers Uploaders: Sune Vuorela , Modestas Vainius , Ana Beatriz Guerrero Lopez -Build-Depends: cdbs (>= 0.4.51), debhelper (>= 5), cmake (>= 2.6.0~), automoc, quilt, - libqt4-dev (>> 4.4.0~), shared-mime-info, libmysqlclient15-dev, libboost1.35-dev - libxslt-dev, libsoprano-dev (>= 2.1.64), xsltproc, libdbus-1-dev, - mysql-server-core-5.0, pkg-kde-tools, libboost-program-options1.35-dev -Standards-Version: 3.8.0 -Vcs-Browser: http://svn.debian.org/wsvn/pkg-kde/branches/kde4/packages/akonadi -Vcs-Svn: svn://svn.debian.org/pkg-kde/branches/kde4/packages/akonadi +Build-Depends: cdbs (>= 0.4.52), debhelper (>= 7), cmake (>= 2.6.2), automoc, quilt, pkg-kde-tools (>= 0.4), + libqt4-dev (>> 4.4.0~), shared-mime-info, libmysqlclient15-dev, + libxslt-dev, libsoprano-dev (>= 2.3.0), xsltproc, libdbus-1-dev, libboost-program-options1.35-dev, + mysql-server-core-5.0 +Standards-Version: 3.8.1 Homepage: http://pim.kde.org/akonadi +Vcs-Browser: http://svn.debian.org/wsvn/pkg-kde/trunk/packages/akonadi +Vcs-Svn: svn://svn.debian.org/pkg-kde/trunk/packages/akonadi Package: libakonadiprivate1 Section: libs @@ -29,7 +29,7 @@ service. Package: akonadi-dbg -Section: libdevel +Section: debug Architecture: any Suggests: akonadi-server (= ${binary:Version}) Description: debugging symbols for the Akonadi PIM storage service diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/copyright /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/copyright --- akonadi-1.1.1/debian/copyright 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/copyright 2009-08-23 19:48:09.000000000 +0100 @@ -15,10 +15,12 @@ Copyright: © 2007-2008 Kevin Krammer Copyright: © 2000 Timo Hummel Copyright: © 2000 Tom Braun +Copyright: © 1997 Matthias Kalle Dalheimer +Copyright: © 2002 Holger Freyther Copyright: © 2002 Kitware, Inc., Insight Consortium. All Rights Reserved. Copyright: © 2006 Allen Winter -Copyright: © 2007 Pino Toscano -Copyright: © 2007 Christian Schaarschmidt +Copyright: © 2007 Pino Toscano +Copyright: © 2007 Christian Schaarschmidt Upstream authors: @@ -93,5 +95,5 @@ On Debian systems, the complete text of the GNU Library General Public License can be found in `/usr/share/common-licenses/LGPL-2-1'. -The Debian packaging is © 2008, Debian Qt/KDE Maintainers and +The Debian packaging is © 2008-2009, Debian Qt/KDE Maintainers and is licensed under the same license as the software, LGPL-2-1, see above. diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/installgen /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/installgen --- akonadi-1.1.1/debian/installgen 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/debian/installgen 2009-08-23 19:48:09.000000000 +0100 @@ -0,0 +1,13 @@ +[libakonadi-dev] +dst:usr/include +dst:usr/lib/.*\.so$ +dst:usr/share/dbus.*/interfaces/.*\.xml$ +dst:.*\.pc$ + +[akonadi-server] +dst:usr/bin +dst:etc/ +dst:usr/share + +[libakonadiprivate1] +dst:usr/lib/lib[^/]+\.so\.1($|\.) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/KUBUNTU-DEBIAN-DIFFERENCES /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/KUBUNTU-DEBIAN-DIFFERENCES --- akonadi-1.1.1/debian/KUBUNTU-DEBIAN-DIFFERENCES 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/KUBUNTU-DEBIAN-DIFFERENCES 2009-08-23 19:48:09.000000000 +0100 @@ -1,7 +1,5 @@ * Add mysql-server-5.0 as build dependency * Add mysql-server-5.0 as dependency for akonadi-server -* Add kubuntu_01_mysqldakonadi.diff ensuring that akonadiserver uses - mysqld-akonadi instead of mysqld itself (to bypass apparmor) * Hardlink mysqld to mysqld-akonadi (rules) * Add apparmor profile for mysqld-akonadi (usr.sbin.mysqld-akonadi) * Add pre and postinst scripts for akonadi-server diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/libakonadi-dev.install /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/libakonadi-dev.install --- akonadi-1.1.1/debian/libakonadi-dev.install 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/libakonadi-dev.install 2009-08-23 19:48:09.000000000 +0100 @@ -20,3 +20,5 @@ usr/share/dbus-1/interfaces/org.freedesktop.Akonadi.Server.xml usr/share/dbus-1/interfaces/org.freedesktop.Akonadi.Tracer.xml usr/share/dbus-1/interfaces/org.freedesktop.Akonadi.TracerNotification.xml +usr/lib/Akonadi/cmake/AkonadiConfig.cmake +usr/lib/Akonadi/cmake/AkonadiConfigVersion.cmake diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/libakonadi-dev.installgen /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/libakonadi-dev.installgen --- akonadi-1.1.1/debian/libakonadi-dev.installgen 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/libakonadi-dev.installgen 1970-01-01 01:00:00.000000000 +0100 @@ -1,4 +0,0 @@ -dst:usr/include -dst:usr/lib/.*\.so$ -dst:usr/share/dbus.*/interfaces/.*\.xml$ -dst:.*\.pc$ diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/libakonadiprivate1.installgen /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/libakonadiprivate1.installgen --- akonadi-1.1.1/debian/libakonadiprivate1.installgen 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/libakonadiprivate1.installgen 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -dst:usr/lib/.*\.so\. diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/libakonadiprivate1.lintian /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/libakonadiprivate1.lintian --- akonadi-1.1.1/debian/libakonadiprivate1.lintian 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/libakonadiprivate1.lintian 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -libakonadiprivate1: package-name-doesnt-match-sonames libakonadiprivate1 libakonadiprotocolinternals1 diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/patches/02_hardcode_debian_mysqld_path.diff /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/patches/02_hardcode_debian_mysqld_path.diff --- akonadi-1.1.1/debian/patches/02_hardcode_debian_mysqld_path.diff 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/debian/patches/02_hardcode_debian_mysqld_path.diff 2009-08-23 19:48:09.000000000 +0100 @@ -0,0 +1,10 @@ +diff -Nurp akonadi-1.1.90/server/CMakeLists.txt akonadi-1.1.90/server/CMakeLists.txt +--- akonadi-1.1.90/server/CMakeLists.txt 2009-06-03 13:38:03.000000000 -0400 ++++ akonadi-1.1.90/server/CMakeLists.txt 2009-06-04 09:53:05.000000000 -0400 +@@ -1,5 +1,5 @@ + +-find_program( MYSQLD_EXECUTABLE mysqld /usr/sbin /usr/local/sbin /usr/libexec /usr/local/libexec /opt/mysql/libexec ) ++set( MYSQLD_EXECUTABLE "/usr/sbin/mysqld" CACHE STRING "MySQL Server path" ) + if( MYSQLD_EXECUTABLE) + message( STATUS "MySQL Server found." ) + else ( MYSQLD_EXECUTABLE ) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/patches/series /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/patches/series --- akonadi-1.1.1/debian/patches/series 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/patches/series 2009-08-23 19:48:09.000000000 +0100 @@ -1 +1,2 @@ 01_x11_not_required.diff +02_hardcode_debian_mysqld_path.diff diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/README.source /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/README.source --- akonadi-1.1.1/debian/README.source 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/debian/README.source 2009-08-23 19:48:09.000000000 +0100 @@ -0,0 +1,6 @@ +This package uses quilt for upstream source code patch management. Please read +/usr/share/doc/quilt/README.source for more information how to apply, unapply, +add, modify or remove patches. + +Please note that /usr/share/doc/quilt/README.source is only available in quilt +version 0.46-4.1 or later. diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/debian/rules /tmp/x5zlUfQlMV/akonadi-1.2.0/debian/rules --- akonadi-1.1.1/debian/rules 2009-08-23 19:48:09.000000000 +0100 +++ akonadi-1.2.0/debian/rules 2009-08-23 19:48:09.000000000 +0100 @@ -1,13 +1,13 @@ #!/usr/bin/make -f DEB_CONFIG_INSTALL_DIR = /etc -DEB_DH_MAKESHLIBS_ARGS_libakonadiprivate0 := -V'libakonadiprivate1 (>= 1.1.1)' +DEB_DH_MAKESHLIBS_ARGS_libakonadiprivate1 := -V'libakonadiprivate1 (>= 1.2.0)' DEB_CMAKE_EXTRA_FLAGS += -DMYSQLD_EXECUTABLE=/usr/sbin/mysqld DEB_CMAKE_EXTRA_FLAGS += -DMYSQLD_EXECUTABLE=/usr/sbin/mysqld-akonadi -include /usr/share/cdbs/1/class/kde4.mk +include /usr/share/pkg-kde-tools/qt-kde-team/1/debian-qt-kde.mk binary-install/akonadi-server:: install -p -D debian/usr.sbin.mysqld-akonadi \ diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/interfaces/org.freedesktop.Akonadi.AgentManager.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/interfaces/org.freedesktop.Akonadi.AgentManager.xml --- akonadi-1.1.1/interfaces/org.freedesktop.Akonadi.AgentManager.xml 2009-01-21 18:29:02.000000000 +0000 +++ akonadi-1.2.0/interfaces/org.freedesktop.Akonadi.AgentManager.xml 2009-07-28 17:51:58.000000000 +0100 @@ -123,5 +123,8 @@ + + + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/interfaces/org.freedesktop.Akonadi.Resource.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/interfaces/org.freedesktop.Akonadi.Resource.xml --- akonadi-1.1.1/interfaces/org.freedesktop.Akonadi.Resource.xml 2009-01-21 18:29:02.000000000 +0000 +++ akonadi-1.2.0/interfaces/org.freedesktop.Akonadi.Resource.xml 2009-07-28 17:51:58.000000000 +0100 @@ -4,6 +4,7 @@ + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/CMakeLists.txt --- akonadi-1.1.1/libs/CMakeLists.txt 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/libs/CMakeLists.txt 2009-07-28 17:51:59.000000000 +0100 @@ -1,11 +1,5 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII ${_ENABLE_EXCEPTIONS}" ) -# according to akonadi/CMakeLists.txt still cmake 2.4.5 is required, -# which doesn't have this command, so only do it if it exists. Alex -if(COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) -endif(COMMAND cmake_policy) - # libakonadiprotocolinternals set( akonadiprotocolinternals_srcs imapparser.cpp @@ -18,8 +12,8 @@ target_link_libraries( akonadiprotocolinternals ${QT_QTCORE_LIBRARY} ${QT_QTDBUS_LIBRARY}) set_target_properties( akonadiprotocolinternals PROPERTIES - VERSION ${AKONADI_LIB_VERSION_STRING} - SOVERSION ${AKONADI_LIB_MAJOR_VERSION} + VERSION ${AKONADI_VERSION} + SOVERSION ${AKONADI_VERSION_MAJOR} DEFINE_SYMBOL MAKE_AKONADIPROTOCOLINTERNALS_LIB ) @@ -35,4 +29,5 @@ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/akonadi/private ) +add_subdirectory( tests ) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/imapparser.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/imapparser.cpp --- akonadi-1.1.1/libs/imapparser.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/libs/imapparser.cpp 2009-07-28 17:51:59.000000000 +0100 @@ -180,16 +180,32 @@ if ( data[begin] == '"' ) { ++begin; for ( int i = begin; i < data.length(); ++i ) { - if ( data[i] == '\\' ) { - ++i; + const char ch = data.at( i ); + if ( foundSlash ) { + foundSlash = false; + if ( ch == 'r' ) + result += '\r'; + else if ( ch == 'n' ) + result += '\n'; + else if ( ch == '\\' ) + result += '\\'; + else if ( ch == '\"' ) + result += '\"'; + else { + //TODO: this is actually an error + result += ch; + } + continue; + } + if ( ch == '\\' ) { foundSlash = true; continue; } - if ( data[i] == '"' ) { - result = data.mid( begin, i - begin ); + if ( ch == '"' ) { end = i + 1; // skip the '"' break; } + result += ch; } } @@ -212,15 +228,16 @@ // transform unquoted NIL if ( result == "NIL" ) result.clear(); - } - // strip quotes - if ( foundSlash ) { - while ( result.contains( "\\\"" ) ) - result.replace( "\\\"", "\"" ); - while ( result.contains( "\\\\" ) ) - result.replace( "\\\\", "\\" ); + // strip quotes + if ( foundSlash ) { + while ( result.contains( "\\\"" ) ) + result.replace( "\\\"", "\"" ); + while ( result.contains( "\\\\" ) ) + result.replace( "\\\\", "\\" ); + } } + return end; } @@ -309,9 +326,20 @@ QByteArray result( "\"" ); result.reserve( data.length() + 2 ); for ( int i = 0; i < data.length(); ++i ) { - if ( data.at( i ) == '"' || data.at( i ) == '\\' ) + const char ch = data.at( i ); + if ( ch == '\n' ) { + result += "\\n"; + continue; + } + + if ( ch == '\r' ) { + result += "\\r"; + continue; + } + + if ( ch == '"' || ch == '\\' ) result += '\\'; - result += data.at( i ); + result += ch; } result += '"'; return result; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/imapset_p.h /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/imapset_p.h --- akonadi-1.1.1/libs/imapset_p.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/libs/imapset_p.h 2009-07-28 17:51:59.000000000 +0100 @@ -163,7 +163,7 @@ /** Adds the given list of positive integer numbers to the set. The list is sorted and splitted into as large as possible intervals. - No interval merging is perofrmed. + No interval merging is performed. @param values List of positive integer numbers in arbitrary order */ void add( const QList &values ); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/notificationmessage.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/notificationmessage.cpp --- akonadi-1.1.1/libs/notificationmessage.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/libs/notificationmessage.cpp 2009-07-28 17:51:59.000000000 +0100 @@ -22,6 +22,7 @@ #include #include #include +#include "imapparser_p.h" using namespace Akonadi; @@ -50,7 +51,7 @@ parts = other.parts; } - bool compareWithoutOp( const Private &other ) const + bool compareWithoutOpAndParts( const Private &other ) const { return sessionId == other.sessionId && type == other.type @@ -59,7 +60,12 @@ && resource == other.resource && parentCollection == other.parentCollection && parentDestCollection == other.parentDestCollection - && mimeType == other.mimeType + && mimeType == other.mimeType; + } + + bool compareWithoutOp( const Private &other ) const + { + return compareWithoutOpAndParts( other ) && parts == other.parts; } @@ -237,7 +243,9 @@ rv += QLatin1String( "added" ); break; case Modify: - rv += QLatin1String( "modified" ); + rv += QLatin1String( "modified parts (" ); + rv += QString::fromLatin1( ImapParser::join( itemParts().toList(), ", " ) ); + rv += QLatin1String( ")" ); break; case Move: rv += QLatin1String( "moved" ); @@ -270,6 +278,12 @@ it = list.erase( it ); } else ++it; + } else if ( msg.d->compareWithoutOpAndParts( *((*it).d) ) ) { + if ( msg.operation() == Modify && (*it).operation() == Modify ) { + (*it).setItemParts( (*it).itemParts() + msg.itemParts() ); + return; + } else + ++it; } else ++it; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/protocol_p.h /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/protocol_p.h --- akonadi-1.1.1/libs/protocol_p.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/libs/protocol_p.h 2009-07-28 17:51:59.000000000 +0100 @@ -33,12 +33,35 @@ #define AKONADI_DBUS_CONTROL_SERVICE "org.freedesktop.Akonadi.Control" // Commands +#define AKONADI_CMD_COLLECTIONCOPY "COLCOPY" +#define AKONADI_CMD_COLLECTIONCREATE "CREATE" +#define AKONADI_CMD_COLLECTIONDELETE "DELETE" +#define AKONADI_CMD_COLLECTIONMODIFY "MODIFY" +#define AKONADI_CMD_COLLECTIONMOVE "COLMOVE" + +#define AKONADI_CMD_ITEMCOPY "COPY" +#define AKONADI_CMD_ITEMCREATE "X-AKAPPEND" +#define AKONADI_CMD_ITEMDELETE "REMOVE" #define AKONADI_CMD_ITEMFETCH "FETCH" +#define AKONADI_CMD_ITEMMODIFY "STORE" +#define AKONADI_CMD_ITEMMOVE "MOVE" + #define AKONADI_CMD_UID "UID" +#define AKONADI_CMD_RESOURCESELECT "RESSELECT" +#define AKONADI_CMD_RID "RID" // Command parameters #define AKONADI_PARAM_FULLPAYLOAD "FULLPAYLOAD" #define AKONADI_PARAM_ALLATTRIBUTES "ALLATTR" #define AKONADI_PARAM_CACHEONLY "CACHEONLY" +#define AKONADI_PARAM_EXTERNALPAYLOAD "EXTERNALPAYLOAD" +#define AKONADI_PARAM_REVISION "REV" +#define AKONADI_PARAM_SIZE "SIZE" +#define AKONADI_PARAM_FLAGS "FLAGS" +#define AKONADI_PARAM_REMOTEID "REMOTEID" +#define AKONADI_PARAM_UNDIRTY "DIRTY" +#define AKONADI_PARAM_MIMETYPE "MIMETYPE" +#define AKONADI_PARAM_CACHEPOLICY "CACHEPOLICY" +#define AKONADI_PARAM_NAME "NAME" #endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/tests/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/tests/CMakeLists.txt --- akonadi-1.1.1/libs/tests/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/libs/tests/CMakeLists.txt 2009-07-28 17:51:59.000000000 +0100 @@ -0,0 +1,12 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR} ) + +macro(add_unit_test _source) + set(_test ${_source}) + get_filename_component(_name ${_source} NAME_WE) + automoc4_add_executable(${_name} ${_source}) + add_test(akonadi-${_name} ${_name}) + target_link_libraries(${_name} akonadiprotocolinternals ${QT_QTGUI_LIBRARY} ${QT_QTTEST_LIBRARY}) +endmacro(add_unit_test) + +add_unit_test( notificationmessagetest.cpp ) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/tests/notificationmessagetest.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/tests/notificationmessagetest.cpp --- akonadi-1.1.1/libs/tests/notificationmessagetest.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/libs/tests/notificationmessagetest.cpp 2009-07-28 17:51:59.000000000 +0100 @@ -0,0 +1,122 @@ +/* + Copyright (c) 2007 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "notificationmessagetest.h" +#include "notificationmessagetest.moc" + +#include + +#include + +QTEST_APPLESS_MAIN( NotificationMessageTest ) + +using namespace Akonadi; + +Q_DECLARE_METATYPE( NotificationMessage::Type ) + +void NotificationMessageTest::testCompress() +{ + NotificationMessage::List list; + NotificationMessage msg; + msg.setType( NotificationMessage::Item ); + msg.setOperation( NotificationMessage::Add ); + + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + + msg.setOperation( NotificationMessage::Modify ); + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + QCOMPARE( list.first().operation(), NotificationMessage::Add ); + + msg.setOperation( NotificationMessage::Remove ); + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 2 ); // should be 2 for collections, 0 for items? +} + +void NotificationMessageTest::testCompress2() +{ + NotificationMessage::List list; + NotificationMessage msg; + msg.setType( NotificationMessage::Item ); + msg.setOperation( NotificationMessage::Modify ); + + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + + msg.setOperation( NotificationMessage::Remove ); + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + QCOMPARE( list.first().operation(), NotificationMessage::Remove ); +} + +void NotificationMessageTest::testCompress3() +{ + NotificationMessage::List list; + NotificationMessage msg; + msg.setType( NotificationMessage::Item ); + msg.setOperation( NotificationMessage::Modify ); + + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); +} + +void NotificationMessageTest::testNoCompress() +{ + NotificationMessage::List list; + NotificationMessage msg; + msg.setType( NotificationMessage::Item ); + msg.setOperation( NotificationMessage::Modify ); + + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + + msg.setType( NotificationMessage::Collection ); + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 2 ); +} + +void NotificationMessageTest::testPartModificationMerge_data() +{ + QTest::addColumn( "type" ); + QTest::newRow( "item" ) << NotificationMessage::Item; + QTest::newRow( "collection" ) << NotificationMessage::Collection; +} + +void NotificationMessageTest::testPartModificationMerge() +{ + QFETCH( NotificationMessage::Type, type ); + + NotificationMessage::List list; + NotificationMessage msg; + msg.setType( type ); + msg.setOperation( NotificationMessage::Modify ); + msg.setItemParts( QSet() << "PART1" ); + + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + + msg.setItemParts( QSet() << "PART2" ); + NotificationMessage::appendAndCompress( list, msg ); + QCOMPARE( list.count(), 1 ); + QCOMPARE( list.first().itemParts(), (QSet() << "PART1" << "PART2") ); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/tests/notificationmessagetest.h /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/tests/notificationmessagetest.h --- akonadi-1.1.1/libs/tests/notificationmessagetest.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/libs/tests/notificationmessagetest.h 2009-07-28 17:51:59.000000000 +0100 @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_NOTIFICATIONMESSAGETEST_H +#define AKONADI_NOTIFICATIONMESSAGETEST_H + +#include + +class NotificationMessageTest : public QObject +{ + Q_OBJECT + private slots: + void testCompress(); + void testCompress2(); + void testCompress3(); + void testNoCompress(); + void testPartModificationMerge_data(); + void testPartModificationMerge(); +}; + + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/libs/xdgbasedirs.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/libs/xdgbasedirs.cpp --- akonadi-1.1.1/libs/xdgbasedirs.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/libs/xdgbasedirs.cpp 2009-07-28 17:51:59.000000000 +0100 @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -42,6 +43,15 @@ return pathList; } +static QStringList splitPathList( const QString &pathList ) +{ +#if defined(Q_OS_WIN) //krazy:exclude=cpp + return pathList.split( QLatin1Char( ';' ) ); +#else + return pathList.split( QLatin1Char( ':' ) ); +#endif +} + namespace Akonadi { class XdgBaseDirsPrivate @@ -111,6 +121,20 @@ dataDirs << prefixDataDir; } + // fallback for users with KDE in a different prefix and not correctly set up XDG_DATA_DIRS, hi David ;-) + QProcess proc; + // ### should probably rather be --path xdg-something + const QStringList args = QStringList() << QLatin1String( "--prefix" ); + proc.start( QLatin1String( "kde4-config" ), args ); + if ( proc.waitForStarted() && proc.waitForFinished() && proc.exitCode() == 0 ) { + proc.setReadChannel( QProcess::StandardOutput ); + foreach ( const QString &basePath, splitPathList( QString::fromLocal8Bit( proc.readLine().trimmed() ) ) ) { + const QString path = basePath + QDir::separator() + QLatin1String( "share" ); + if ( !dataDirs.contains( path ) ) + dataDirs << path; + } + } + instance()->mDataDirs = dataDirs; } return instance()->mDataDirs; @@ -326,9 +350,5 @@ xdgDirList = QString::fromLocal8Bit( env ); } -#if defined(Q_OS_WIN) //krazy:exclude=cpp - return xdgDirList.split( QLatin1Char( ';' ) ); -#else - return xdgDirList.split( QLatin1Char( ':' ) ); -#endif + return splitPathList( xdgDirList ); } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/Mainpage.dox /tmp/x5zlUfQlMV/akonadi-1.2.0/Mainpage.dox --- akonadi-1.1.1/Mainpage.dox 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/Mainpage.dox 2009-07-28 17:52:13.000000000 +0100 @@ -1,32 +1,46 @@ /** \mainpage Akonadi storage and control server -\section akonadi_server_overview Overview - -\li An introduction to server components can be found in \ref akonadi_design -\li \ref akonadi_server_definitions -\li \ref akonadi_server_database -\li Alphabetical Class List -\li Class Hierarchy -\li \ref akonadi_server_srclayout +

+Overview | +\ref akonadi_server_definitions | +\ref akonadi_server_srclayout +

+ +Akonadi aims to be an extensible cross-desktop storage service for PIM data +and meta data providing concurrent read, write, and query access. +It provides unique desktop-wide object identification and retrieval. + +This is the API documentation for the Akonadi server. If you are using Akonadi +from within KDE, you almost certainly want the +KDE client library documentation. +This API reference is more useful to people implementing client libraries or +working on the Akonadi server itself. +For more information, see the Akonadi website. \page akonadi_server_definitions Type Definitions +

+\ref index "Overview" | +\ref Type Definitions | +\ref akonadi_server_srclayout +

+ To let all components play together nicely, we have to use some common encoding definitions. \li Collection names
Collection names and paths are Unicode strings (QString) to allow custom names by the user. -\li DataReference
+\li Data references
The persistent identifier is an unsigned integer and the external URL is a Unicode string (QString). \li Transferred data over IMAP
The data transferred over IMAP are byte arrays (QByteArray). If Unicode strings are transferred over IMAP, UTF-8 encoding is applied. -\li Error/Status messages
+\li Error and status messages
Error and status messages are visible to the user, so they have to be Unicode strings (QString). @@ -35,16 +49,22 @@ \page akonadi_server_srclayout Source Code Layout +

+\ref index "Overview" | +\ref akonadi_server_definitions | +\ref Source Code Layout +

+ The code of the storage and control components is located in the \c server sub-directory. The different parts are layed out as follows:
  • \e control
    - Contains the source code of the \ref akonadi_design_control "Control" component. + Contains the source code of the \ref akonadi_design_control "control" component.
  • \e interfaces
    Contains the D-Bus interface descriptions of the Akonadi components
  • \e src
    - Contains the source code of the \ref akonadi_design_storage "Storage" component. + Contains the source code of the \ref akonadi_design_storage "storage" component.
  • \e src/handler
    Contains the source code for the handlers of the single IMAP commands. See command handlers module @@ -60,5 +80,8 @@
*/ -// DOXYGEN_REFERENCES = akonadi // DOXYGEN_EXCLUDE = sqlplugin server/control server/akonadictl server/tests +// DOXYGEN_PROJECTNAME=Akonadi +// DOXYGEN_PROJECTVERSION=1.1.80 + +// vim:ts=4:sw=4:expandtab:filetype=doxygen diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/NEWS /tmp/x5zlUfQlMV/akonadi-1.2.0/NEWS --- akonadi-1.1.1/NEWS 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/NEWS 2009-07-28 17:52:13.000000000 +0100 @@ -1,3 +1,59 @@ +1.2.0 28-June-2009 +--------------------------------------------- +- Fix attribute joining in collection list results. +- Buildsystem fixes for Mac OS. +- Do not show a console window for akonadi_control on Windows. + +1.1.95 23-June-2009 +--------------------------------------------- +- Fix item size handling. +- Add support for retrieving collection statistics as part + of the AKLIST/AKLSUB commands. +- Add support for collection size statistics. +- Build fixes for Windows. +- Support RID-based operations for CREATE, MODIFY and DELETE. +- Avoid emitting unecessary change notifications when + modifying items or collections. +- Add COLMOVE command. +- Reduce number of database writes when modifying a collection. +- Fix parsing of attributes containing CR or LF characters. + +1.1.90 03-June-2009 +--------------------------------------------- +- Return the storage location for items in FETCH responses +- Fix remode identifier encoding problems +- Fix infinite loop when parsing RID lists +- Fix parsing errors on stray newlines +- Support RID-based operations for STORE and MOVE +- Fix race on resource creation +- Provide modified item parts in change notifications +- Build system fixes + +1.1.85 05-May-2009 +--------------------------------------------- +- Improved CMake scripts so it is possible to detect + the Akonadi version in projects that depend on it. +- Simplified the check for existance of tables. +- Add a dedicated item deletion command, to get rid of + the old STORE/EXPUNGE which was extremely inefficient. +- Some fixes to support sqlite in the future. +- Soprano is required now. +- Qt 4.5.0 is required now. +- Support for collection retrieval by remote identifier. +- Support for item retrieval based on the remote identifier. +- Less useless debug output. +- Fixed leak on socket error. +- Various smaller bug fixes, see ChangeLog for a list. +- Support for writing large payloads to a file. +- New Item retrieval code. +- Added a streaming IMAP parser, and ported code the use it. +- Add support for manually restarting an agent instance. + +1.1.2 30-Apr-2009 +--------------------------------------------- +- Avoid DBUS lockups, reported at: https://bugs.kde.org/182198 +- Update user mysql.conf only if global/local one's are newer + 1.1.1 21-Jan-2009 --------------------------------------------- - Fix code that was not executed in a release build. diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/README /tmp/x5zlUfQlMV/akonadi-1.2.0/README --- akonadi-1.1.1/README 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/README 2009-07-28 17:52:13.000000000 +0100 @@ -14,7 +14,7 @@ that is needed to build the client libraries and the application which want to make use of Akonadi. -Struture +Structure ---------- * cmake/ diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/akonadictl/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/server/akonadictl/CMakeLists.txt --- akonadi-1.1.1/server/akonadictl/CMakeLists.txt 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/akonadictl/CMakeLists.txt 2009-07-28 17:52:13.000000000 +0100 @@ -10,7 +10,7 @@ ) qt4_add_dbus_interfaces( akonadictl_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.ControlManager.xml + ${Akonadi_SOURCE_DIR}/server/interfaces/org.freedesktop.Akonadi.ControlManager.xml ) automoc4_add_executable(akonadictl ${akonadictl_SRCS}) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/akonadictl/main.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/akonadictl/main.cpp --- akonadi-1.1.1/server/akonadictl/main.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/akonadictl/main.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -24,12 +24,21 @@ #include #include +#include + #include "akapplication.h" #include "protocol_p.h" #include "controlmanagerinterface.h" #include "akonadistarter.h" +#if defined(HAVE_UNISTD_H) && !defined(Q_WS_WIN) +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif + static bool startServer() { if ( QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_DBUS_CONTROL_SERVICE ) @@ -68,6 +77,7 @@ "Commands:\n" " start : Starts the Akonadi server with all its processes\n" " stop : Stops the Akonadi server and all its processes cleanly\n" + " restart : Restart Akonadi server with all its processes\n" " status : Shows a status overview of the Akonadi server" ); @@ -77,6 +87,7 @@ optionsList.append( QLatin1String( "start" ) ); optionsList.append( QLatin1String( "stop" ) ); optionsList.append( QLatin1String( "status" ) ); + optionsList.append( QLatin1String( "restart" ) ); const QStringList arguments = app.arguments(); if ( arguments.count() != 2 ) { @@ -96,6 +107,20 @@ } else if ( arguments[ 1 ] == QLatin1String( "status" ) ) { if ( !statusServer() ) return 5; + } else if ( arguments[ 1 ] == QLatin1String( "restart") ) { + if ( !stopServer() ) + return 4; + else { + do { +#if defined(HAVE_UNISTD_H) && !defined(Q_WS_WIN) + usleep(100000); +#else + Sleep(100000); +#endif + } while( QDBusConnection::sessionBus().interface()->isServiceRegistered( AKONADI_DBUS_CONTROL_SERVICE ) ); + if ( !startServer() ) + return 3; + } } return 0; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/server/CMakeLists.txt --- akonadi-1.1.1/server/CMakeLists.txt 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/CMakeLists.txt 2009-07-28 17:52:13.000000000 +0100 @@ -1,30 +1,22 @@ -# disable MySQL/Embedded plugin for now since it is unused and causes linker problems on Debian based 64bit systems -# macro_optional_find_package(MySQL) -# macro_log_feature(MYSQL_EMBEDDED_FOUND "mysql-embedded" "MySQL/Embedded Library" "http://www.mysql.org" FALSE "" "Needed for the MySQL/Embedded Akonadi backend. Sqlite will be used instead.") -# -# macro_optional_find_package(OpenSSL) -# macro_log_feature(OPENSSL_FOUND "OpenSSL" "A toolkit implementing the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols" "http://openssl.org" FALSE "" "Required for building the Akonadi SQL plugin with MySQL embedded.") - - -# according to akonadi/CMakeLists.txt still cmake 2.4.5 is required, -# which doesn't have this command, so only do it if it exists. Alex -if(COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) -endif(COMMAND cmake_policy) - -find_program( MYSQLD_EXECUTABLE mysqld /usr/sbin /usr/local/sbin /usr/libexec /opt/mysql/libexec ) -macro_log_feature( MYSQLD_EXECUTABLE "MySQL Server" "Database server" "http://www.mysql.com" FALSE "" - "Akonadi server requires the mysqld binary" ) -include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) +find_program( MYSQLD_EXECUTABLE mysqld /usr/sbin /usr/local/sbin /usr/libexec /usr/local/libexec /opt/mysql/libexec ) +if( MYSQLD_EXECUTABLE) + message( STATUS "MySQL Server found." ) +else ( MYSQLD_EXECUTABLE ) + message( STATUS "MySQL Server wasn't found. it is required to run Akonadi" ) +endif( MYSQLD_EXECUTABLE ) + include_directories( ${Boost_INCLUDE_DIR} ) -include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libs ) -include_directories( ${CMAKE_CURRENT_BINARY_DIR}/../libs ) +include_directories( ${Akonadi_SOURCE_DIR}/libs ) +include_directories( ${Akonadi_BINARY_DIR}/libs ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src/handler ) +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/src/search ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/tests ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/shared ) +include_directories( ${SOPRANO_INCLUDE_DIR} ) +include_directories( BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ) +include_directories( BEFORE ${CMAKE_CURRENT_BINARY_DIR} ) set( AKONADI_PROTOCOLINTERNALS_LIBS ${akonadiprotocolinternals_LIB_DEPENDS} akonadiprotocolinternals) @@ -37,7 +29,6 @@ add_subdirectory( akonadictl ) add_subdirectory( control ) add_subdirectory( src ) -#add_subdirectory( queryserver ) add_subdirectory( tests ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_ENABLE_EXCEPTIONS} -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII" ) @@ -77,6 +68,7 @@ src/handler/append.cpp src/handler/copy.cpp src/handler/colcopy.cpp + src/handler/colmove.cpp src/handler/create.cpp src/handler/capability.cpp src/handler/delete.cpp @@ -87,8 +79,12 @@ src/handler/login.cpp src/handler/logout.cpp src/handler/modify.cpp + src/handler/move.cpp src/handler/noop.cpp + src/handler/remove.cpp src/handler/rename.cpp + src/handler/resourceselect.cpp + src/handler/scope.cpp src/handler/searchhelper.cpp src/handler/searchpersistent.cpp src/handler/select.cpp @@ -96,16 +92,30 @@ src/handler/status.cpp src/handler/store.cpp src/handler/transaction.cpp - src/handler/uid.cpp + + src/search/dbusoperators.cpp + src/search/query.cpp + src/search/queryserviceclient.cpp + src/search/result.cpp + src/search/term.cpp + + src/storage/collectionqueryhelper.cpp src/storage/entity.cpp ${CMAKE_CURRENT_BINARY_DIR}/entities.cpp src/storage/datastore.cpp src/storage/dbconfig.cpp src/storage/dbinitializer.cpp - src/storage/dbupdater.cpp src/storage/notificationcollector.cpp + src/storage/dbupdater.cpp + src/storage/itemqueryhelper.cpp + src/storage/itemretriever.cpp + src/storage/itemretrievalmanager.cpp + src/storage/itemretrievalthread.cpp + src/storage/notificationcollector.cpp src/storage/query.cpp src/storage/querybuilder.cpp + src/storage/queryhelper.cpp src/storage/transaction.cpp + src/storage/parthelper.cpp src/tracer.cpp src/dbustracer.cpp src/filetracer.cpp @@ -116,18 +126,20 @@ src/xesammanager.cpp src/nepomukmanager.cpp src/debuginterface.cpp + src/imapstreamparser.cpp ) qt4_generate_dbus_interface( src/debuginterface.h org.freedesktop.Akonadi.DebugInterface.xml ) qt4_add_dbus_adaptor( libakonadiprivate_SRCS interfaces/org.freedesktop.Akonadi.TracerNotification.xml dbustracer.h Akonadi::DBusTracer ) -qt4_add_dbus_adaptor( libakonadiprivate_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.Tracer.xml tracer.h Akonadi::Tracer +qt4_add_dbus_adaptor( libakonadiprivate_SRCS ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.Tracer.xml tracer.h Akonadi::Tracer ) -qt4_add_dbus_adaptor( libakonadiprivate_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.NotificationManager.xml notificationmanager.h Akonadi::NotificationManager ) +qt4_add_dbus_adaptor( libakonadiprivate_SRCS ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.NotificationManager.xml notificationmanager.h Akonadi::NotificationManager ) qt4_add_dbus_adaptor( libakonadiprivate_SRCS interfaces/org.freedesktop.Akonadi.Server.xml akonadi.h Akonadi::AkonadiServer ) qt4_add_dbus_adaptor( libakonadiprivate_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.DebugInterface.xml debuginterface.h DebugInterface ) +qt4_add_dbus_adaptor( libakonadiprivate_SRCS ${Akonadi_SOURCE_DIR}/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml resourcemanager.h Akonadi::ResourceManager ) -qt4_add_dbus_interfaces( libakonadiprivate_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.AgentManager.xml ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.Resource.xml ) +qt4_add_dbus_interfaces( libakonadiprivate_SRCS ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.AgentManager.xml ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.Resource.xml ) qt4_add_dbus_interface( libakonadiprivate_SRCS interfaces/org.freedesktop.Akonadi.Search.xml searchinterface ) qt4_add_dbus_interface( libakonadiprivate_SRCS interfaces/org.freedesktop.Akonadi.SearchQuery.xml searchqueryinterface ) qt4_add_dbus_interface( libakonadiprivate_SRCS interfaces/org.freedesktop.Akonadi.SearchQueryIterator.xml searchqueryiteratorinterface ) @@ -138,10 +150,17 @@ qt4_add_dbus_interface( libakonadiprivate_SRCS ${xesam_xml} xesaminterface ) +set_source_files_properties(src/search/org.kde.nepomuk.QueryService.xml PROPERTIES INCLUDE "querymetatype.h") +set_source_files_properties(src/search/org.kde.nepomuk.Query.xml PROPERTIES INCLUDE "result.h") + +qt4_add_dbus_interface(libakonadiprivate_SRCS src/search/org.kde.nepomuk.QueryService.xml queryserviceinterface) +qt4_add_dbus_interface(libakonadiprivate_SRCS src/search/org.kde.nepomuk.Query.xml queryinterface) + qt4_add_resources( libakonadiprivate_SRCS src/storage/akonadidb.qrc ) automoc4_add_library( akonadiprivate SHARED ${libakonadiprivate_SRCS} ) + target_link_libraries( akonadiprivate ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY} @@ -150,11 +169,12 @@ ${QT_QTXML_LIBRARY} ${AKONADI_PROTOCOLINTERNALS_LIBS} ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${SOPRANO_LIBRARIES} ) set_target_properties( akonadiprivate PROPERTIES - VERSION ${AKONADI_LIB_VERSION_STRING} - SOVERSION ${AKONADI_LIB_MAJOR_VERSION} + VERSION ${AKONADI_VERSION} + SOVERSION ${AKONADI_VERSION_MAJOR} DEFINE_SYMBOL MAKE_AKONADIPRIVATE_LIB ) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/control/agentinstance.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/control/agentinstance.cpp --- akonadi-1.1.1/server/control/agentinstance.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/control/agentinstance.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -218,10 +218,8 @@ void AgentInstance::errorHandler(const QDBusError & error) { - mManager->tracer()->error( QLatin1String( "AgentInstance::errorHandler" ), - QString( "D-Bus communication error '%1': '%2'" ) - .arg( error.name(), error.message() ) ); - + //avoid using the server tracer, can result in D-BUS lockups + qDebug() << QString( "D-Bus communication error '%1': '%2'" ).arg( error.name(), error.message() ) ; // TODO try again after some time, esp. on timeout errors } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/control/agentmanager.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/control/agentmanager.cpp --- akonadi-1.1.1/server/control/agentmanager.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/control/agentmanager.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -25,6 +25,7 @@ #include "serverinterface.h" #include "../../libs/xdgbasedirs_p.h" #include "akdebug.h" +#include "resource_manager.h" #include #include @@ -175,6 +176,9 @@ return QString(); mAgentInstances.insert( instance->identifier(), instance ); + org::freedesktop::Akonadi::ResourceManager * resmanager = new org::freedesktop::Akonadi::ResourceManager( QLatin1String("org.freedesktop.Akonadi"), QLatin1String("/ResourceManager"), QDBusConnection::sessionBus(), this ); + resmanager->addResourceInstance(instance->identifier()); + save(); return instance->identifier(); } @@ -198,6 +202,9 @@ mAgentInstances.remove( identifier ); save(); + + org::freedesktop::Akonadi::ResourceManager * resmanager = new org::freedesktop::Akonadi::ResourceManager( QLatin1String("org.freedesktop.Akonadi"), QLatin1String("/ResourceManager"), QDBusConnection::sessionBus(), this ); + resmanager->removeResourceInstance(instance->identifier()); emit agentInstanceRemoved( identifier ); } @@ -308,6 +315,13 @@ mAgentInstances.value( identifier )->resourceInterface()->synchronizeCollection( collection ); } +void AgentManager::restartAgentInstance(const QString& identifier) +{ + if ( !checkInstance( identifier ) ) + return; + mAgentInstances.value( identifier )->restartWhenIdle(); +} + void AgentManager::updatePluginInfos() { QHash oldInfos = mAgents; @@ -545,4 +559,5 @@ } } + #include "agentmanager.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/control/agentmanager.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/control/agentmanager.h --- akonadi-1.1.1/server/control/agentmanager.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/control/agentmanager.h 2009-07-28 17:52:03.000000000 +0100 @@ -204,6 +204,13 @@ */ void setAgentInstanceOnline( const QString &identifier, bool state ); + + /** + Restarts the agent instance @p identifier. This is supposed to be used as a + development aid and not something to use during normal operations. + */ + void restartAgentInstance( const QString &identifier ); + Q_SIGNALS: /** * This signal is emitted whenever a new agent type was installed on the system. diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/control/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/server/control/CMakeLists.txt --- akonadi-1.1.1/server/control/CMakeLists.txt 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/control/CMakeLists.txt 2009-07-28 17:52:03.000000000 +0100 @@ -23,19 +23,23 @@ processcontrol.cpp ) -qt4_add_dbus_adaptor( control_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../../interfaces/org.freedesktop.Akonadi.AgentManager.xml agentmanager.h AgentManager ) -qt4_add_dbus_adaptor( control_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.ControlManager.xml controlmanager.h ControlManager ) +qt4_add_dbus_adaptor( control_SRCS ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.AgentManager.xml agentmanager.h AgentManager ) +qt4_add_dbus_adaptor( control_SRCS ${Akonadi_SOURCE_DIR}/server/interfaces/org.freedesktop.Akonadi.ControlManager.xml controlmanager.h ControlManager ) qt4_add_dbus_interfaces( control_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/../../interfaces/org.freedesktop.Akonadi.Tracer.xml - ${CMAKE_CURRENT_SOURCE_DIR}/../../interfaces/org.freedesktop.Akonadi.Agent.Control.xml - ${CMAKE_CURRENT_SOURCE_DIR}/../../interfaces/org.freedesktop.Akonadi.Agent.Status.xml - ${CMAKE_CURRENT_SOURCE_DIR}/../../interfaces/org.freedesktop.Akonadi.Resource.xml - ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.Server.xml + ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.Tracer.xml + ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.Agent.Control.xml + ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.Agent.Status.xml + ${Akonadi_SOURCE_DIR}/interfaces/org.freedesktop.Akonadi.Resource.xml + ${Akonadi_SOURCE_DIR}/server/interfaces/org.freedesktop.Akonadi.Server.xml ) - +qt4_add_dbus_interface( control_SRCS ${Akonadi_SOURCE_DIR}/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml resource_manager) automoc4_add_executable(akonadi_control ${control_SRCS}) set_target_properties(akonadi_control PROPERTIES OUTPUT_NAME akonadi_control) +if (WIN32) + set_target_properties(akonadi_control PROPERTIES WIN32_EXECUTABLE TRUE) + target_link_libraries(akonadi_control ${QT_QTMAIN_LIBRARY}) +endif (WIN32) target_link_libraries(akonadi_control ${QT_QTCORE_LIBRARY} ${QT_QTDBUS_LIBRARY} ${AKONADI_PROTOCOLINTERNALS_LIBS} ${Boost_PROGRAM_OPTIONS_LIBRARY}) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/control/main.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/control/main.cpp --- akonadi-1.1.1/server/control/main.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/control/main.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -53,7 +53,7 @@ app.parseCommandLine(); if ( !QDBusConnection::sessionBus().registerService( AKONADI_DBUS_CONTROL_SERVICE ) ) - akFatal() << "Unable to register service: %s" << QDBusConnection::sessionBus().lastError().message(); + akFatal() << "Unable to register service: " << QDBusConnection::sessionBus().lastError().message(); new ControlManager; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/control/processcontrol.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/control/processcontrol.cpp --- akonadi-1.1.1/server/control/processcontrol.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/control/processcontrol.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -124,24 +124,38 @@ } } +namespace { + static QString getEnv( const char* name, const QString& defaultValue=QString() ) { + const QString v = QString::fromLocal8Bit( qgetenv( name ) ); + return !v.isEmpty() ? v : defaultValue; + } +} + void ProcessControl::start() { #ifdef Q_OS_UNIX - QString agentValgrind = QString::fromLocal8Bit( qgetenv("AKONADI_VALGRIND") ); + QString agentValgrind = getEnv( "AKONADI_VALGRIND" ); if ( !agentValgrind.isEmpty() && mApplication.contains( agentValgrind ) ) { + + mArguments.prepend( mApplication ); + mApplication = QString::fromLocal8Bit( "valgrind" ); + + const QString valgrindSkin = getEnv( "AKONADI_VALGRIND_SKIN", QString::fromLocal8Bit( "memcheck" ) ); + mArguments.prepend( QLatin1String( "--tool=" ) + valgrindSkin ); + + const QString valgrindOptions = getEnv( "AKONADI_VALGRIND_OPTIONS" ); + if ( !valgrindOptions.isEmpty() ) + mArguments = valgrindOptions.split( ' ' ) << mArguments; + qDebug(); qDebug() << "============================================================"; qDebug() << "ProcessControl: Valgrinding process" << mApplication; + if ( !valgrindSkin.isEmpty() ) + qDebug() << "ProcessControl: Valgrind skin:" << valgrindSkin; + if ( !valgrindOptions.isEmpty() ) + qDebug() << "ProcessControl: Additional Valgrind options:" << valgrindOptions; qDebug() << "============================================================"; qDebug(); - QString valgrindSkin = QString::fromLocal8Bit( qgetenv( "AKONADI_VALGRIND_SKIN" ) ); - - mArguments.prepend( mApplication ); - mApplication = QString::fromLocal8Bit( "valgrind" ); - if ( !valgrindSkin.isEmpty() ) - mArguments.prepend( QLatin1String( "--tool=" ) + valgrindSkin ); - else - mArguments.prepend (QLatin1String( "--tool=memcheck") ); } #endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml --- akonadi-1.1.1/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/interfaces/org.freedesktop.Akonadi.ResourceManager.xml 2009-07-28 17:52:04.000000000 +0100 @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/CMakeLists.txt --- akonadi-1.1.1/server/queryserver/CMakeLists.txt 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/CMakeLists.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1,44 +0,0 @@ -include_directories( - ${SOPRANO_INCLUDE_DIR} -) - -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_ENABLE_EXCEPTIONS}" ) - -########### next target ############### - -set( queryserver_SRCS - main.cpp - query.cpp - queryiterator.cpp - search.cpp -) - -qt4_add_dbus_adaptor( queryserver_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.Search.xml search.h Search ) -qt4_add_dbus_adaptor( queryserver_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.SearchQuery.xml query.h Query ) -qt4_add_dbus_adaptor( queryserver_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces/org.freedesktop.Akonadi.SearchQueryIterator.xml queryiterator.h QueryIterator ) - -add_executable( akonadi_queryserver ${queryserver_SRCS} ) -set_target_properties( akonadi_queryserver PROPERTIES OUTPUT_NAME akonadi_queryserver ) - -target_link_libraries( akonadi_queryserver ${QT_QTCORE_LIBRARY} ${QT_QTDBUS_LIBRARY} ${NEPOMUK_LIBRARY} ) - -install( TARGETS akonadi_queryserver DESTINATION ${BIN_INSTALL_DIR} ) - -configure_file( org.freedesktop.Akonadi.Search.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.Search.service ) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.Search.service DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dbus-1/services ) - -########### next target ############### - -qt4_add_dbus_interface( queryservertestdbus_SRCS ../interfaces/org.freedesktop.Akonadi.Search.xml searchinterface ) -qt4_add_dbus_interface( queryservertestdbus_SRCS ../interfaces/org.freedesktop.Akonadi.SearchQuery.xml searchqueryinterface ) -qt4_add_dbus_interface( queryservertestdbus_SRCS ../interfaces/org.freedesktop.Akonadi.SearchQueryIterator.xml searchqueryiteratorinterface ) - -set( queryservertest_SRCS - queryservertest.cpp - ${queryservertestdbus_SRCS} -) - -add_executable( akonadi_queryservertest ${queryservertest_SRCS} ) -set_target_properties( akonadi_queryservertest PROPERTIES OUTPUT_NAME akonadi_queryservertest ) - -target_link_libraries( akonadi_queryservertest ${QT_QTCORE_LIBRARY} ${QT_QTDBUS_LIBRARY} ${NEPOMUK_LIBRARY} ${SOPRANO_LIBRARY} ) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/main.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/main.cpp --- akonadi-1.1.1/server/queryserver/main.cpp 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/main.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,38 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include -#include - -#include "search.h" - -int main( int argc, char **argv ) -{ - QCoreApplication app( argc, argv ); - - if ( !QDBusConnection::sessionBus().registerService( "org.freedesktop.Akonadi.Search" ) ) { - qDebug( "Unable to register service: %s", qPrintable( QDBusConnection::sessionBus().lastError().message() ) ); - return 1; - } - - new Search(); - - return app.exec(); -} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/org.kde.Akonadi.Search.service.cmake /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/org.kde.Akonadi.Search.service.cmake --- akonadi-1.1.1/server/queryserver/org.kde.Akonadi.Search.service.cmake 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/org.kde.Akonadi.Search.service.cmake 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=org.freedesktop.Akonadi.Search -Exec=${BIN_INSTALL_DIR}/akonadi_queryserver diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/query.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/query.cpp --- akonadi-1.1.1/server/queryserver/query.cpp 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/query.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,178 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include - -#include - -#include "searchqueryadaptor.h" -#include "queryiterator.h" - -#include "query.h" - -class Query::Private -{ - public: - Private( Query *parent, const QString &queryString, Soprano::Model *model, const QString &id ) - : mParent( parent ), mId( id ), mIteratorIdCounter( 0 ), - mQueryString( queryString ), mModel( model ), mRunning( false ) - { - } - - QString createUniqueIteratorId() - { - return QString( "%1/Iterator%2" ).arg( mId ).arg( ++mIteratorIdCounter ); - } - - void _k_statementsAdded(); - void _k_statementsRemoved(); - - Query *mParent; - QString mId; - unsigned int mIteratorIdCounter; - QString mQueryString; - QDateTime mQueryTime; - Soprano::Model *mModel; - QSet mIterators; - QSet mAllUris; - bool mRunning; -}; - -void Query::Private::_k_statementsAdded() -{ - QList uris; - Soprano::QueryResultIterator it = mModel->executeQuery( mQueryString, Soprano::Query::QueryLanguageSparql ); - - /** - * Check for uris which exist in the model but not - * in our cache -> they are new - */ - while ( it.next() ) { - const QString uri = it.binding( 0 ).uri().toString(); - - if ( !mAllUris.contains( uri ) ) { - mAllUris.insert( uri ); - uris.append( uri ); - } - } - - mQueryTime = QDateTime::currentDateTime(); - - emit mParent->hitsChanged( uris ); -} - -void Query::Private::_k_statementsRemoved() -{ - QSet availableUris; - - Soprano::QueryResultIterator it = mModel->executeQuery( mQueryString, Soprano::Query::QueryLanguageSparql ); - while ( it.next() ) { - const QString uri = it.binding( 0 ).uri().toString(); - availableUris.insert( uri ); - } - - /** - * Check for uris which exist in our cache but not in - * in the model -> they were deleted. - */ - QSet tmp = mAllUris; - tmp.subtract( availableUris ); // tmp contains all deleted uris - - /** - * Remove all deleted uris from our cache. - */ - mAllUris.subtract( tmp ); - - /** - * Create result map. - */ - QList uris; - Q_FOREACH( const QString &uri, tmp ) - uris.append( uri ); - - mQueryTime = QDateTime::currentDateTime(); - - emit mParent->hitsRemoved( uris ); -} - -Query::Query( const QString &queryString, Soprano::Model *model, const QString &id, QObject *parent ) - : QObject( parent ), d( new Private( this, queryString, model, id ) ) -{ - new SearchQueryAdaptor( this ); - - QDBusConnection::sessionBus().registerObject( id, this ); -} - -Query::~Query() -{ - delete d; -} - -void Query::start() -{ - if ( d->mRunning ) - return; - - d->mRunning = true; - - /** - * Stores all available items, so we can find out which uris have been added/deleted. - * - * TODO: change it when Nepomuk will support search by creation time. - */ - Soprano::QueryResultIterator it = d->mModel->executeQuery( d->mQueryString, Soprano::Query::QueryLanguageSparql ); - while ( it.next() ) - d->mAllUris.insert( it.binding( 0 ).uri().toString() ); - - it.close(); - - connect( d->mModel, SIGNAL( statementsAdded() ), this, SLOT( _k_statementsAdded() ) ); - connect( d->mModel, SIGNAL( statementsRemoved() ), this, SLOT( _k_statementsRemoved() ) ); -} - -void Query::stop() -{ - if ( !d->mRunning ) - return; - - d->mRunning = false; - - disconnect( d->mModel, SIGNAL( statementsAdded() ), this, SLOT( _k_statementsAdded() ) ); - disconnect( d->mModel, SIGNAL( statementsRemoved() ), this, SLOT( _k_statementsRemoved() ) ); -} - -void Query::close() -{ - deleteLater(); -} - -QString Query::allHits() -{ - const QString id = d->createUniqueIteratorId(); - - Soprano::QueryResultIterator it = d->mModel->executeQuery( d->mQueryString, Soprano::Query::QueryLanguageSparql ); - - QueryIterator *iterator = new QueryIterator( it, id, this ); - d->mIterators.insert( iterator ); - - return id; -} - -#include "query.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/query.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/query.h --- akonadi-1.1.1/server/queryserver/query.h 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/query.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,89 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#ifndef QUERY_H -#define QUERY_H - -#include - -#include - -class Query : public QObject -{ - Q_OBJECT - - public: - /** - * Creates a new search query. - * - * @param queryString The sparql query string. - * @param model The soprano model. - * @param id The dbus path id. - * @param parent The parent object. - */ - Query( const QString &queryString, Soprano::Model *model, const QString &id, QObject *parent = 0 ); - - /** - * Destroys the search query. - */ - ~Query(); - - public Q_SLOTS: - /** - * Starts the query. - */ - void start(); - - /** - * Stops the query. - * - * You can restart it by calling start(). - */ - void stop(); - - /** - * Closes the query and destroys the dbus object. - */ - void close(); - - /** - * Returns the dbus path to an iterator for all hits. - */ - QString allHits(); - - Q_SIGNALS: - /** - * This signal is emitted whenever hits has changed. - */ - void hitsChanged( const QList &hits ); - - /** - * This signal is emitted whenever hits has been removed. - */ - void hitsRemoved( const QList &hits ); - - private: - class Private; - Private* const d; - - Q_PRIVATE_SLOT( d, void _k_statementsAdded() ) - Q_PRIVATE_SLOT( d, void _k_statementsRemoved() ) -}; - -#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/queryiterator.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/queryiterator.cpp --- akonadi-1.1.1/server/queryserver/queryiterator.cpp 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/queryiterator.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,72 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include -#include - -#include "queryiterator.h" - -#include "searchqueryiteratoradaptor.h" - -class QueryIterator::Private -{ - public: - Private( const Soprano::QueryResultIterator &it ) - : mIterator( it ) - { - } - - Soprano::QueryResultIterator mIterator; -}; - -QueryIterator::QueryIterator( const Soprano::QueryResultIterator &it, const QString &id, QObject *parent ) - : QObject( parent ), d( new Private( it ) ) -{ - new SearchQueryIteratorAdaptor( this ); - - QDBusConnection::sessionBus().registerObject( id, this ); -} - -QueryIterator::~QueryIterator() -{ - delete d; -} - -bool QueryIterator::next() -{ - return d->mIterator.next(); -} - -QString QueryIterator::currentUri() -{ - return d->mIterator.binding( 0 ).uri().toString(); -} - -double QueryIterator::currentScore() -{ - return 1; -} - -void QueryIterator::close() -{ - deleteLater(); -} - -#include "queryiterator.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/queryiterator.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/queryiterator.h --- akonadi-1.1.1/server/queryserver/queryiterator.h 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/queryiterator.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,69 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#ifndef QUERYITERATOR_H -#define QUERYITERATOR_H - -#include -#include - -#include - -class QueryIterator : public QObject -{ - Q_OBJECT - - public: - /** - * Creates a new search query iterator with the given @p id and @p parent. - */ - QueryIterator( const Soprano::QueryResultIterator &it, const QString &id, QObject *parent = 0 ); - - /** - * Destroys the search query iterator. - */ - ~QueryIterator(); - - public Q_SLOTS: - /** - * Returns whether there is a next hit. - */ - bool next(); - - /** - * Returns the uri of the current hit. - */ - QString currentUri(); - - /** - * Returns the score of the current hit. - */ - double currentScore(); - - /** - * Closes the query iterator and destroys the dbus object. - */ - void close(); - - private: - class Private; - Private* const d; -}; - -#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/queryservertest.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/queryservertest.cpp --- akonadi-1.1.1/server/queryserver/queryservertest.cpp 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/queryservertest.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,106 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include -#include - -#include "searchqueryiteratorinterface.h" - -#include "queryservertest.h" - -TestObject::TestObject( const QString &query, QObject *parent ) - : QObject( parent ) -{ - mSearch = new org::freedesktop::Akonadi::Search( "org.freedesktop.Akonadi.Search", "/Search", QDBusConnection::sessionBus(), this ); - - const QString queryPath = mSearch->createQuery( query ); - - mQuery = new org::freedesktop::Akonadi::SearchQuery( "org.freedesktop.Akonadi.Search", queryPath, QDBusConnection::sessionBus(), this ); - - const QString iteratorPath = mQuery->allHits(); - - org::freedesktop::Akonadi::SearchQueryIterator *iterator = - new org::freedesktop::Akonadi::SearchQueryIterator( "org.freedesktop.Akonadi.Search", iteratorPath, - QDBusConnection::sessionBus(), this ); - while ( iterator->next() ) { - QString data = iterator->currentUri(); - qDebug(" %s", qPrintable( data ) ); - } - - iterator->close(); - - connect( mQuery, SIGNAL( hitsChanged( const QMap& ) ), - this, SLOT( hitsChanged( const QMap& ) ) ); - connect( mQuery, SIGNAL( hitsChanged( const QMap& ) ), - this, SLOT( hitsRemoved( const QMap& ) ) ); - - mQuery->start(); -} - -TestObject::~TestObject() -{ - mQuery->stop(); - mQuery->close(); -} - -void TestObject::hitsChanged( const QMap &hits ) -{ - qDebug( "New hits:" ); - QMapIterator it( hits ); - while ( it.hasNext() ) { - it.next(); - qDebug( " %s: %f", qPrintable( it.key() ), it.value() ); - } -} - -void TestObject::hitsRemoved( const QMap &hits ) -{ - qDebug( "Removed hits:" ); - QMapIterator it( hits ); - while ( it.hasNext() ) { - it.next(); - qDebug( " %s: %f", qPrintable( it.key() ), it.value() ); - } -} - -int main( int argc, char **argv ) -{ - QCoreApplication app( argc, argv ); - -/* - if ( argc != 2 ) { - qDebug( "usage: queryservertest query" ); - return 1; - } - - new TestObject( QString::fromLatin1( argv[ 1 ] ) ); -*/ - QString query( "prefix nco: select ?result where { ?result a nco:PersonContact . ?result nco:nameGiven ?n . FILTER REGEX(STR(?n), 'Tobias') . }" ); - TestObject *test = new TestObject( query ); - - - int retval = app.exec(); - - delete test; - - return retval; -} - -#include "queryservertest.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/queryservertest.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/queryservertest.h --- akonadi-1.1.1/server/queryserver/queryservertest.h 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/queryservertest.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,45 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#ifndef AKONADI_QUERYSERVERTEST_H -#define AKONADI_QUERYSERVERTEST_H - -#include - -#include "searchinterface.h" -#include "searchqueryinterface.h" - -class TestObject : public QObject -{ - Q_OBJECT - - public: - explicit TestObject( const QString &query, QObject *parent = 0 ); - ~TestObject(); - - private Q_SLOTS: - void hitsChanged( const QMap &hits ); - void hitsRemoved( const QMap &hits ); - - private: - org::freedesktop::Akonadi::Search *mSearch; - org::freedesktop::Akonadi::SearchQuery *mQuery; -}; - -#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/search.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/search.cpp --- akonadi-1.1.1/server/queryserver/search.cpp 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/search.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,76 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include - -#include -#include - -#include "query.h" -#include "searchadaptor.h" - -#include "search.h" - -class Search::Private -{ - public: - Private() - : mQueryIdCounter( 0 ) - { - } - - QString createUniqueQueryId() - { - return QString( "/Search/Query%1" ).arg( ++mQueryIdCounter ); - } - - unsigned int mQueryIdCounter; - QSet mQueries; - Soprano::Model *mModel; - Soprano::Client::DBusClient *mSopranoClient; -}; - -Search::Search( QObject *parent ) - : QObject( parent ), d( new Private ) -{ - new SearchAdaptor( this ); - - QDBusConnection::sessionBus().registerObject( "/Search", this ); - - d->mSopranoClient = new Soprano::Client::DBusClient( "org.kde.NepomukServer", this ); - d->mModel = d->mSopranoClient->createModel( "main" ); -} - -Search::~Search() -{ - delete d; -} - -QString Search::createQuery( const QString &queryString ) -{ - const QString id = d->createUniqueQueryId(); - - Query *query = new Query( queryString, d->mModel, id, this ); - d->mQueries.insert( query ); - - return id; -} - -#include "search.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/queryserver/search.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/queryserver/search.h --- akonadi-1.1.1/server/queryserver/search.h 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/queryserver/search.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,52 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#ifndef SEARCH_H -#define SEARCH_H - -#include - -class Search : public QObject -{ - Q_OBJECT - - public: - /** - * Creates a new search object with the given @p parent. - */ - Search( QObject *parent = 0 ); - - /** - * Destroys the search object. - */ - ~Search(); - - public Q_SLOTS: - /** - * Creates a new query object with the given @p queryString - * and returns the dbus path to the object. - */ - QString createQuery( const QString &queryString ); - - private: - class Private; - Private* const d; -}; - -#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/shared/akcrash.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/shared/akcrash.cpp --- akonadi-1.1.1/server/shared/akcrash.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/shared/akcrash.cpp 2009-07-28 17:52:04.000000000 +0100 @@ -37,7 +37,7 @@ # include #endif -QString kBacktrace() +QString akBacktrace() { QString s; @@ -86,9 +86,9 @@ if ( recursionCount <= 2 ) { if ( sig != SIGTERM && sig != SIGINT ) { if ( recursionCount == 1 ) - akError() << kBacktrace(); + akError() << akBacktrace(); else // fall back to something more simple in case the other one crashed itself - fprintf( stderr, "%s", kBacktrace().toLatin1().data() ); + fprintf( stderr, "%s", akBacktrace().toLatin1().data() ); if ( s_emergencyMethod ) s_emergencyMethod( sig ); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/shared/akcrash.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/shared/akcrash.h --- akonadi-1.1.1/server/shared/akcrash.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/shared/akcrash.h 2009-07-28 17:52:04.000000000 +0100 @@ -22,6 +22,9 @@ #ifndef AKCRASH_H #define AKCRASH_H +#include + +QString akBacktrace(); class AkonadiCrash { diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/abstractsearchmanager.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/abstractsearchmanager.cpp --- akonadi-1.1.1/server/src/abstractsearchmanager.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/abstractsearchmanager.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -40,7 +40,7 @@ mInstance = this; } -bool DummySearchManager::addSearch( const Location& ) +bool DummySearchManager::addSearch( const Collection& ) { return true; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/abstractsearchmanager.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/abstractsearchmanager.h --- akonadi-1.1.1/server/src/abstractsearchmanager.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/abstractsearchmanager.h 2009-07-28 17:52:13.000000000 +0100 @@ -24,7 +24,7 @@ namespace Akonadi { -class Location; +class Collection; /** * AbstractSearchManager is an abstract interface for search managers. @@ -46,16 +46,16 @@ static AbstractSearchManager* instance(); /** - * Adds the given @p location to the search. + * Adds the given @p collection to the search. * - * @returns true if the location was added successfully, false otherwise. + * @returns true if the collection was added successfully, false otherwise. */ - virtual bool addSearch( const Location &location ) = 0; + virtual bool addSearch( const Collection &collection ) = 0; /** - * Removes the location with the given @p id from the search. + * Removes the collection with the given @p id from the search. * - * @returns true if the location was removed successfully, false otherwise. + * @returns true if the collection was removed successfully, false otherwise. */ virtual bool removeSearch( qint64 id ) = 0; @@ -71,7 +71,7 @@ { public: DummySearchManager(); - bool addSearch( const Location &location ); + bool addSearch( const Collection &collection ); bool removeSearch( qint64 id ); }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/akonadiconnection.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/akonadiconnection.cpp --- akonadi-1.1.1/server/src/akonadiconnection.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/akonadiconnection.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -27,7 +27,10 @@ #include "response.h" #include "tracer.h" -#include "../libs/imapparser_p.h" +#include "libs/imapparser_p.h" +#include "imapstreamparser.h" +#include "shared/akdebug.h" +#include "shared/akcrash.h" #include @@ -41,6 +44,8 @@ , m_connectionState( NonAuthenticated ) , m_backend( 0 ) , m_selectedConnection( 0 ) + , m_parser( 0 ) + , m_streamParser( 0 ) { m_identifier.sprintf( "%p", static_cast( this ) ); Tracer::self()->beginConnection( m_identifier, QString() ); @@ -70,6 +75,8 @@ qWarning() << "AkonadiConnection(" << m_identifier << ")::run: failed to set socket descriptor: " << m_socket->error() << "(" << m_socket->errorString() << ")"; + delete m_socket; + m_socket = 0; return; } @@ -88,11 +95,14 @@ connect( m_socket, SIGNAL( disconnected() ), this, SLOT( slotDisconnected() ), Qt::DirectConnection ); - writeOut( "* OK Akonadi Almost IMAP Server [PROTOCOL 6]"); + writeOut( "* OK Akonadi Almost IMAP Server [PROTOCOL 15]"); + m_streamParser = new ImapStreamParser( m_socket ); exec(); delete m_socket; m_socket = 0; + delete m_streamParser; + m_streamParser = 0; } void AkonadiConnection::slotDisconnected() @@ -102,52 +112,47 @@ void AkonadiConnection::slotNewData() { - while ( m_socket->bytesAvailable() > 0 ) { - if ( m_parser->continuationSize() > 1 ) { - const QByteArray data = m_socket->read( qMin( m_socket->bytesAvailable(), m_parser->continuationSize() - 1 ) ); - Tracer::self()->connectionInput( m_identifier, QLatin1String("[binary data]") ); - m_parser->parseBlock( data ); - } else if ( m_socket->canReadLine() ) { - const QByteArray line = m_socket->readLine(); - Tracer::self()->connectionInput( m_identifier, QString::fromUtf8( line ) ); - - if ( m_parser->parseNextLine( line ) ) { - // parse the command - QByteArray command; - ImapParser::parseString( m_parser->data(), command ); - - m_currentHandler = findHandlerForCommand( command ); - m_currentHandler->setTag( m_parser->tag() ); - assert( m_currentHandler ); - connect( m_currentHandler, SIGNAL( responseAvailable( const Response & ) ), - this, SLOT( slotResponseAvailable( const Response & ) ), Qt::DirectConnection ); - connect( m_currentHandler, SIGNAL( connectionStateChange( ConnectionState ) ), - this, SLOT( slotConnectionStateChange( ConnectionState ) ), - Qt::DirectConnection ); - try { - // FIXME: remove the tag, it's only there for backward compatibility with the handlers! - if ( m_currentHandler->handleLine( m_parser->tag() + ' ' + m_parser->data() ) ) - m_currentHandler = 0; - } catch ( ... ) { - delete m_currentHandler; - m_currentHandler = 0; - } - m_parser->reset(); - } else { - if ( m_parser->continuationStarted() ) { - Response response; - response.setContinuation(); - response.setString( "Ready for literal data (expecting " + - QByteArray::number( m_parser->continuationSize() ) + " bytes)" ); - slotResponseAvailable( response ); - } + while ( m_socket->bytesAvailable() > 0 || !m_streamParser->readRemainingData().isEmpty()) { + try { + const QByteArray tag = m_streamParser->readString(); + // deal with stray newlines + if ( tag.isEmpty() && m_streamParser->atCommandEnd() ) + continue; + const QByteArray command = m_streamParser->readString(); + Tracer::self()->connectionInput( m_identifier, QString::fromUtf8( tag + " " + command + " " + m_streamParser->readRemainingData() ) ); + m_currentHandler = findHandlerForCommand( command ); + assert( m_currentHandler ); + connect( m_currentHandler, SIGNAL( responseAvailable( const Response & ) ), + this, SLOT( slotResponseAvailable( const Response & ) ), Qt::DirectConnection ); + connect( m_currentHandler, SIGNAL( connectionStateChange( ConnectionState ) ), + this, SLOT( slotConnectionStateChange( ConnectionState ) ), + Qt::DirectConnection ); + m_currentHandler->setTag( tag ); + m_currentHandler->setStreamParser( m_streamParser ); + if ( !m_currentHandler->parseStream() ) { + m_streamParser->readUntilCommandEnd(); //just eat the ending newline } - } else { - break; // nothing we can do for now with the available data + } catch ( const Akonadi::HandlerException &e ) { + m_currentHandler->failureResponse( e.what() ); + m_streamParser->readUntilCommandEnd(); //just eat the ending newline + } catch ( const Akonadi::Exception &e ) { + m_currentHandler->failureResponse( QString::fromLatin1( e.type() ) + + QLatin1String( ": " ) + QString::fromLatin1( e.what() ) ); + m_streamParser->readUntilCommandEnd(); //just eat the ending newline + } catch ( ... ) { + akError() << "Unknown exception caught: " << akBacktrace(); + m_currentHandler->failureResponse( "Unknown exception caught" ); + m_streamParser->readUntilCommandEnd(); //just eat the ending newline } + delete m_currentHandler; + m_currentHandler = 0; + + if (m_streamParser->readRemainingData().startsWith('\n') || m_streamParser->readRemainingData().startsWith("\r\n")) + m_streamParser->readUntilCommandEnd(); //just eat the ending newline } } + void AkonadiConnection::writeOut( const QByteArray &data ) { QByteArray block = data + "\r\n"; @@ -165,9 +170,10 @@ switch ( m_connectionState ) { case NonAuthenticated: - handler = Handler::findHandlerForCommandNonAuthenticated( command ); break; + handler = Handler::findHandlerForCommandNonAuthenticated( command ); + break; case Authenticated: - handler = Handler::findHandlerForCommandAuthenticated( command ); + handler = Handler::findHandlerForCommandAuthenticated( command, m_streamParser ); break; case Selected: break; @@ -175,7 +181,7 @@ break; } // we didn't have a handler for this, let the default one do its thing - if ( !handler ) handler = new Handler(); + if ( !handler ) handler = new UnknownCommandHandler( command ); handler->setConnection( this ); return handler; } @@ -205,7 +211,7 @@ } } -qint64 Akonadi::AkonadiConnection::selectedCollection( ) const +qint64 Akonadi::AkonadiConnection::selectedCollectionId( ) const { return m_selectedConnection; } @@ -215,9 +221,9 @@ m_selectedConnection = collection; } -const Location Akonadi::AkonadiConnection::selectedLocation() +const Collection Akonadi::AkonadiConnection::selectedCollection() { - return Location::retrieveById( selectedCollection() ); + return Collection::retrieveById( selectedCollectionId() ); } void Akonadi::AkonadiConnection::addStatusMessage( const QByteArray& msg ) @@ -250,4 +256,24 @@ return m_sessionId; } +Resource Akonadi::AkonadiConnection::resourceContext() const +{ + return m_resourceContext; +} + +void AkonadiConnection::setResourceContext(const Resource& res) +{ + m_resourceContext = res; +} + +bool AkonadiConnection::isOwnerResource(const PimItem& item) const +{ + if ( resourceContext().isValid() ) + return true; + // fallback for older resources + if ( sessionId() == item.collection().resource().name().toUtf8() ) + return true; + return false; +} + #include "akonadiconnection.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/akonadiconnection.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/akonadiconnection.h --- akonadi-1.1.1/server/src/akonadiconnection.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/akonadiconnection.h 2009-07-28 17:52:13.000000000 +0100 @@ -32,7 +32,8 @@ class Response; class DataStore; class ImapParser; - class Location; + class Collection; + class ImapStreamParser; /** An AkonadiConnection represents one connection of a client to the server. @@ -46,10 +47,17 @@ void run(); virtual DataStore* storageBackend(); - qint64 selectedCollection() const; + qint64 selectedCollectionId() const; void setSelectedCollection( qint64 collection ); - const Location selectedLocation(); + Resource resourceContext() const; + void setResourceContext( const Resource &res ); + /** + Returns @c true if this connection belongs to the owning resource of @p item. + */ + bool isOwnerResource( const PimItem &item ) const; + + const Collection selectedCollection(); void addStatusMessage( const QByteArray& msg ); void flushStatusMessageQueue(); @@ -59,6 +67,9 @@ protected Q_SLOTS: void slotDisconnected(); + /** + * New data arrived from the client. Creates a handler for it and passes the data to the handler. + */ void slotNewData(); void slotResponseAvailable( const Response& ); void slotConnectionStateChange( ConnectionState ); @@ -80,6 +91,8 @@ QString m_identifier; QByteArray m_sessionId; ImapParser *m_parser; + ImapStreamParser *m_streamParser; + Resource m_resourceContext; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/akonadi.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/akonadi.cpp --- akonadi-1.1.1/server/src/akonadi.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/akonadi.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -32,9 +32,10 @@ #include "xesammanager.h" #include "nepomukmanager.h" #include "debuginterface.h" +#include "storage/itemretrievalthread.h" -#include "../libs/xdgbasedirs_p.h" -#include "../libs/protocol_p.h" +#include "libs/xdgbasedirs_p.h" +#include "libs/protocol_p.h" #include #include @@ -56,6 +57,7 @@ : QLocalServer( parent ) , mCacheCleaner( 0 ) , mIntervalChecker( 0 ) + , mItemRetrievalThread( 0 ) , mDatabaseProcess( 0 ) , mAlreadyShutdown( false ) { @@ -113,7 +115,14 @@ mIntervalChecker = new IntervalCheck( this ); mIntervalChecker->start( QThread::IdlePriority ); - mSearchManager = new DummySearchManager; + mItemRetrievalThread = new ItemRetrievalThread( this ); + mItemRetrievalThread->start( QThread::HighPriority ); + + const QString searchManager = settings.value( QLatin1String( "Search/Manager" ), QLatin1String( "Nepomuk" ) ).toString(); + if ( searchManager == QLatin1String( "Nepomuk" ) ) + mSearchManager = new NepomukManager( this ); + else + mSearchManager = new DummySearchManager; new ServerAdaptor( this ); QDBusConnection::sessionBus().registerObject( QLatin1String( "/Server" ), this ); @@ -131,40 +140,39 @@ { } +template static void quitThread( T & thread ) +{ + if ( !thread ) + return; + thread->quit(); + thread->wait(); + delete thread; + thread = 0; +} + void AkonadiServer::quit() { if ( mAlreadyShutdown ) return; - mAlreadyShutdown = true; - if ( mCacheCleaner ) - QMetaObject::invokeMethod( mCacheCleaner, "quit", Qt::QueuedConnection ); - if ( mIntervalChecker ) - QMetaObject::invokeMethod( mIntervalChecker, "quit", Qt::QueuedConnection ); - QCoreApplication::instance()->processEvents(); - - if ( mCacheCleaner ) - mCacheCleaner->wait(); - if ( mIntervalChecker ) - mIntervalChecker->wait(); + qDebug() << "terminating service threads"; + quitThread( mCacheCleaner ); + quitThread( mIntervalChecker ); + quitThread( mItemRetrievalThread ); delete mSearchManager; mSearchManager = 0; - for ( int i = 0; i < mConnections.count(); ++i ) { - if ( mConnections[ i ] ) { - mConnections[ i ]->quit(); - mConnections[ i ]->wait(); - } - } + qDebug() << "terminating connection threads"; + for ( int i = 0; i < mConnections.count(); ++i ) + quitThread( mConnections[ i ] ); + mConnections.clear(); DataStore::self()->close(); + Q_ASSERT( QSqlDatabase::connectionNames().isEmpty() ); - // execute the deleteLater() calls for the threads so they free their db connections - // and the following db termination will work - QCoreApplication::instance()->processEvents(); - + qDebug() << "stopping db process"; stopDatabaseProcess(); QSettings settings( XdgBaseDirs::akonadiServerConfigFile(), QSettings::IniFormat ); @@ -191,6 +199,8 @@ void AkonadiServer::incomingConnection( quintptr socketDescriptor ) { + if ( mAlreadyShutdown ) + return; QPointer thread = new AkonadiConnection( socketDescriptor, this ); connect( thread, SIGNAL( finished() ), thread, SLOT( deleteLater() ) ); mConnections.append( thread ); @@ -218,6 +228,7 @@ const QString dataDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_data" ) ); const QString akDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/" ) ); const QString miscDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/db_misc" ) ); + const QString fileDataDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/file_db_data" ) ); // generate config file const QString globalConfig = XdgBaseDirs::findResourceFile( "config", QLatin1String( "akonadi/mysql-global.conf" ) ); @@ -225,23 +236,30 @@ const QString actualConfig = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi" ) ) + QLatin1String("/mysql.conf"); if ( globalConfig.isEmpty() ) akFatal() << "Did not find MySQL server default configuration (mysql-global.conf)"; - QFile globalFile( globalConfig ); - QFile actualFile( actualConfig ); - if ( globalFile.open( QFile::ReadOnly ) && actualFile.open( QFile::WriteOnly ) ) { - actualFile.write( globalFile.readAll() ); - if ( !localConfig.isEmpty() ) { - QFile localFile( localConfig ); - if ( localFile.open( QFile::ReadOnly ) ) { - actualFile.write( localFile.readAll() ); - localFile.close(); + bool confUpdate = false; + QFile actualFile ( actualConfig ); + // update conf only if either global (or local) is newer than actual + if ( (QFileInfo( globalConfig ).lastModified() > QFileInfo( actualFile ).lastModified()) || + (QFileInfo( localConfig ).lastModified() > QFileInfo( actualFile ).lastModified()) ) + { + QFile globalFile( globalConfig ); + QFile localFile ( localConfig ); + if ( globalFile.open( QFile::ReadOnly ) && actualFile.open( QFile::WriteOnly ) ) { + actualFile.write( globalFile.readAll() ); + if ( !localConfig.isEmpty() ) { + if ( localFile.open( QFile::ReadOnly ) ) { + actualFile.write( localFile.readAll() ); + localFile.close(); + } } + globalFile.close(); + actualFile.close(); + confUpdate = true; + } else { + akError() << "Unable to create MySQL server configuration file."; + akError() << "This means that either the default configuration file (mysql-global.conf) was not readable"; + akFatal() << "or the target file (mysql.conf) could not be written."; } - actualFile.close(); - globalFile.close(); - } else { - akError() << "Unable to create MySQL server configuration file."; - akError() << "This means that either the default configuration file (mysql-global.conf) was not readable"; - akFatal() << "or the target file (mysql.conf) could not be written."; } // MySQL doesn't like world writeable config files (which makes sense), but @@ -276,6 +294,12 @@ } } + // clear mysql ib_logfile's in case innodb_log_file_size option changed in last confUpdate + if ( confUpdate ) { + QFile(dataDir + QDir::separator() + QString::fromLatin1( "ib_logfile0" )).remove(); + QFile(dataDir + QDir::separator() + QString::fromLatin1( "ib_logfile1" )).remove(); + } + // synthesize the mysqld command QStringList arguments; arguments << QString::fromLatin1( "--defaults-file=%1/mysql.conf" ).arg( akDir ); @@ -305,7 +329,7 @@ if ( opened ) break; if ( mDatabaseProcess->waitForFinished( 500 ) ) { - akError() << "Database process existed unexpectedly during intial connection!"; + akError() << "Database process exited unexpectedly during initial connection!"; akError() << "executable:" << mysqldPath; akError() << "arguments:" << arguments; akError() << "stdout:" << mDatabaseProcess->readAllStandardOutput(); @@ -316,18 +340,20 @@ } if ( opened ) { - QSqlQuery query( db ); - if ( !query.exec( QString::fromLatin1( "USE %1" ).arg( DbConfig::databaseName() ) ) ) { - akDebug() << "Failed to use database" << DbConfig::databaseName(); - akDebug() << "Query error:" << query.lastError().text(); - akDebug() << "Database error:" << db.lastError().text(); - akDebug() << "Trying to create database now..."; - if ( !query.exec( QLatin1String( "CREATE DATABASE akonadi" ) ) ) { - akError() << "Failed to create database"; - akError() << "Query error:" << query.lastError().text(); - akFatal() << "Database error:" << db.lastError().text(); + { + QSqlQuery query( db ); + if ( !query.exec( QString::fromLatin1( "USE %1" ).arg( DbConfig::databaseName() ) ) ) { + akDebug() << "Failed to use database" << DbConfig::databaseName(); + akDebug() << "Query error:" << query.lastError().text(); + akDebug() << "Database error:" << db.lastError().text(); + akDebug() << "Trying to create database now..."; + if ( !query.exec( QLatin1String( "CREATE DATABASE akonadi" ) ) ) { + akError() << "Failed to create database"; + akError() << "Query error:" << query.lastError().text(); + akFatal() << "Database error:" << db.lastError().text(); + } } - } + } // make sure query is destroyed before we close the db db.close(); } } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/akonadi.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/akonadi.h --- akonadi-1.1.1/server/src/akonadi.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/akonadi.h 2009-07-28 17:52:13.000000000 +0100 @@ -35,6 +35,7 @@ class AkonadiConnection; class CacheCleaner; class AbstractSearchManager; +class ItemRetrievalThread; class AKONADIPRIVATE_EXPORT AkonadiServer: public QLocalServer { @@ -66,6 +67,7 @@ CacheCleaner *mCacheCleaner; IntervalCheck *mIntervalChecker; + ItemRetrievalThread *mItemRetrievalThread; QProcess *mDatabaseProcess; QList< QPointer > mConnections; AbstractSearchManager *mSearchManager; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/cachecleaner.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/cachecleaner.cpp --- akonadi-1.1.1/server/src/cachecleaner.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/cachecleaner.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -18,6 +18,7 @@ */ #include "cachecleaner.h" +#include "storage/parthelper.h" #include "storage/datastore.h" #include "storage/selectquerybuilder.h" @@ -29,6 +30,8 @@ CacheCleaner::CacheCleaner(QObject * parent) : QThread( parent ) { + mTime = 60; + mLoops = 0; } CacheCleaner::~CacheCleaner() @@ -38,54 +41,73 @@ void CacheCleaner::run() { DataStore::self(); - QTimer::singleShot( 60 * 1000, this, SLOT(cleanCache()) ); + QTimer::singleShot( mTime * 1000, this, SLOT(cleanCache()) ); exec(); DataStore::self()->close(); } void CacheCleaner::cleanCache() { - - // cycle over all locations - QList locations = Location::retrieveAll(); - foreach ( Location location, locations ) { + qint64 loopsWithExpiredItem = 0; + // cycle over all collection + QList collections = Collection::retrieveAll(); + foreach ( Collection collection, collections ) { // determine active cache policy - DataStore::self()->activeCachePolicy( location ); + DataStore::self()->activeCachePolicy( collection ); // check if there is something to expire at all - if ( location.cachePolicyLocalParts() == QLatin1String( "ALL" ) || location.cachePolicyCacheTimeout() < 0 - || !location.subscribed() ) + if ( collection.cachePolicyLocalParts() == QLatin1String( "ALL" ) || collection.cachePolicyCacheTimeout() < 0 + || !collection.subscribed() || !collection.resourceId() ) continue; - const int expireTime = qMax( 5, location.cachePolicyCacheTimeout() ); + const int expireTime = qMax( 5, collection.cachePolicyCacheTimeout() ); // find all expired item parts SelectQueryBuilder qb; qb.addTable( PimItem::tableName() ); qb.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, Part::pimItemIdFullColumnName() ); - qb.addValueCondition( PimItem::locationIdFullColumnName(), Query::Equals, location.id() ); + qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, collection.id() ); qb.addValueCondition( PimItem::atimeFullColumnName(), Query::Less, QDateTime::currentDateTime().addSecs( -60 * expireTime ) ); qb.addValueCondition( Part::dataFullColumnName(), Query::IsNot, QVariant() ); - qb.addValueCondition( QString::fromLatin1( "left( %1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "PLD:" ) ); + qb.addValueCondition( QString::fromLatin1( "substr( %1, 1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "PLD:" ) ); qb.addValueCondition( PimItem::dirtyFullColumnName(), Query::Equals, false ); - if ( !location.cachePolicyLocalParts().isEmpty() ) - qb.addValueCondition( Part::nameFullColumnName(), Query::NotIn, location.cachePolicyLocalParts().split( QLatin1String(" ") ) ); + if ( !collection.cachePolicyLocalParts().isEmpty() ) + qb.addValueCondition( Part::nameFullColumnName(), Query::NotIn, collection.cachePolicyLocalParts().split( QLatin1String(" ") ) ); if ( !qb.exec() ) continue; - QList parts = qb.result(); + Part::List parts = qb.result(); + PartHelper::loadData(parts); //FIXME: not needed anymore to read back the data itself? if ( parts.isEmpty() ) continue; - qDebug() << "found" << parts.count() << "item parts to expire in collection" << location.name(); + qDebug() << "found" << parts.count() << "item parts to expire in collection" << collection.name(); // clear data field for ( int i = 0; i < parts.count(); ++i) { - parts[ i ].setData( QByteArray() ); - if ( !parts[ i ].update() ) + if ( !PartHelper::update( &(parts[ i ]), QByteArray(), 0) ) qDebug() << "failed to update item part" << parts[ i ].id(); } + loopsWithExpiredItem++; + } + + /* if we have item parts to expire in collection the mTime is + * decreased of 60 and if there are lot of collection need to be clean + * mTime is 60 otherwise we increment mTime in 60 + */ + + if( mLoops < loopsWithExpiredItem) { + if( (mTime > 60) && (loopsWithExpiredItem - mLoops) < 50 ) + mTime -= 60; + else + mTime = 60; + } else { + if( mTime < 600) + mTime += 60; } - QTimer::singleShot( 60 * 1000, this, SLOT(cleanCache()) ); + // measured arithmetic between mLoops and loopsWithExpiredItem + mLoops = (loopsWithExpiredItem + mLoops) >> 2; + + QTimer::singleShot( mTime * 1000, this, SLOT(cleanCache()) ); } #include "cachecleaner.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/cachecleaner.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/cachecleaner.h --- akonadi-1.1.1/server/src/cachecleaner.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/cachecleaner.h 2009-07-28 17:52:13.000000000 +0100 @@ -46,6 +46,10 @@ private slots: void cleanCache(); + private: + short int mTime; + qint64 mLoops; + }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/exception.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/exception.h --- akonadi-1.1.1/server/src/exception.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/exception.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,58 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_EXCEPTION_H +#define AKONADI_EXCEPTION_H + +#include +#include +#include + +namespace Akonadi { + +/** + Base class for execpetion used internally by the Akonadi server. +*/ +class Exception : public std::exception +{ + public: + Exception( const char *what ) throw() : mWhat( what ) {} + Exception( const QByteArray &what ) throw() : mWhat( what ) {} + Exception( const QString &what ) throw() : mWhat( what.toUtf8() ) {} + Exception( const Exception &other ) throw() : std::exception(other), mWhat( other.what() ) {} + virtual ~Exception() throw() {} + const char* what() const throw() { return mWhat.constData(); } + virtual const char* type() const throw() { return "General Exception"; } + private: + QByteArray mWhat; +}; + +#define AKONADI_EXCEPTION_MAKE_INSTANCE( classname ) \ +class classname : public Akonadi::Exception \ +{ \ + public: \ + classname ( const char *what ) throw() : Akonadi::Exception( what ) {} \ + classname ( const QByteArray &what ) throw() : Akonadi::Exception( what ) {} \ + classname ( const QString &what ) throw() : Akonadi::Exception( what ) {} \ + const char* type() const throw() { return "" #classname; } \ +} + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/global.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/global.h --- akonadi-1.1.1/server/src/global.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/global.h 2009-07-28 17:52:13.000000000 +0100 @@ -22,10 +22,12 @@ namespace Akonadi { // rfc1730 section 3 + /** The state of the client + */ enum ConnectionState { - NonAuthenticated, - Authenticated, - Selected, + NonAuthenticated, ///< Not yet authenticated + Authenticated, ///< The client is authenticated + Selected, LoggingOut }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/akappend.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/akappend.cpp --- akonadi-1.1.1/server/src/handler/akappend.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/akappend.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -17,7 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include "../../libs/imapparser_p.h" +#include "libs/imapparser_p.h" +#include "imapstreamparser.h" #include "append.h" #include "akappend.h" @@ -43,92 +44,6 @@ { } -bool Akonadi::AkAppend::handleLine(const QByteArray& line ) -{ - // Arguments: mailbox name - // OPTIONAL flag parenthesized list - // OPTIONAL date/time string - // (partname literal)+ - // - // Syntax: - // x-akappend = "X-AKAPPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP (partname SP literal)+ - - const int startOfCommand = line.indexOf( ' ' ) + 1; - const int startOfMailbox = line.indexOf( ' ', startOfCommand ) + 1; - - const int startOfSize = ImapParser::parseString( line, m_mailbox, startOfMailbox ) + 1; - - QString size; - const int startOfFlags = ImapParser::parseString( line, size, startOfSize ) + 1; - m_size = size.toLongLong(); - - // parse optional flag parenthesized list - // Syntax: - // flag-list = "(" [flag *(SP flag)] ")" - // flag = "\Answered" / "\Flagged" / "\Deleted" / "\Seen" / - // "\Draft" / flag-keyword / flag-extension - // ; Does not include "\Recent" - // flag-extension = "\" atom - // flag-keyword = atom - int startOfDateTime = startOfFlags; - if ( line[startOfFlags] == '(' ) { - startOfDateTime = ImapParser::parseParenthesizedList( line, m_flags, startOfFlags ) + 1; - } - - // parse optional date/time string - int startOfPartSpec = startOfDateTime; - if ( line[startOfDateTime] == '"' ) { - startOfPartSpec = ImapParser::parseDateTime( line, m_dateTime, startOfDateTime ); - // FIXME Should we return an error if m_dateTime is invalid? - } - // if date/time is not given then it will be set to the current date/time - // by the database - - // parse part specification - QList > > partSpecs; - QByteArray partName; - qint64 partSize; - bool ok; - - int pos = startOfPartSpec + 1; // skip opening '(' - int endOfPartSpec = line.indexOf( ')', startOfPartSpec ); - while( pos < endOfPartSpec ) { - if( line[pos] == ',' ) - pos++; // skip ',' - - pos = ImapParser::parseQuotedString( line, partName, pos ); - pos++; // skip ':' - pos = ImapParser::parseNumber( line, partSize, &ok, pos ); - if( !ok ) - partSize = 0; - - int version = 0; - QByteArray plainPartName; - ImapParser::splitVersionedKey( partName, plainPartName, version ); - - partSpecs.append( qMakePair( plainPartName, qMakePair( partSize, version ) ) ); - } - - QByteArray allParts; - ImapParser::parseString( line, allParts, endOfPartSpec + 1 ); - - // chop up literal data in parts - pos = 0; // traverse through part data now - QPair > partSpec; - foreach( partSpec, partSpecs ) { - // wrap data into a part - Part part; - part.setName( QLatin1String( partSpec.first ) ); - part.setData( allParts.mid( pos, partSpec.second.first ) ); - if ( partSpec.second.second != 0 ) - part.setVersion( partSpec.second.second ); - m_parts.append( part ); - pos += partSpec.second.first; - } - - return commit(); -} - bool Akonadi::AkAppend::commit() { Response response; @@ -136,12 +51,12 @@ DataStore *db = connection()->storageBackend(); Transaction transaction( db ); - Location l = HandlerHelper::collectionFromIdOrName( m_mailbox ); - if ( !l.isValid() ) + Collection col = HandlerHelper::collectionFromIdOrName( m_mailbox ); + if ( !col.isValid() ) return failureResponse( "Unknown collection." ); QByteArray mt; - QByteArray remote_id; + QString remote_id; QList flags; foreach( const QByteArray &flag, m_flags ) { if ( flag.startsWith( "\\MimeType" ) ) { @@ -151,7 +66,7 @@ } else if ( flag.startsWith( "\\RemoteId" ) ) { int pos1 = flag.indexOf( '[' ); int pos2 = flag.indexOf( ']', pos1 ); - remote_id = flag.mid( pos1 + 1, pos2 - pos1 - 1 ); + remote_id = QString::fromUtf8( flag.mid( pos1 + 1, pos2 - pos1 - 1 ) ); } else flags << flag; } @@ -159,14 +74,17 @@ if ( mt.isEmpty() ) mt = "message/rfc822"; MimeType mimeType = MimeType::retrieveByName( QString::fromLatin1( mt ) ); if ( !mimeType.isValid() ) { - return failureResponse( QString::fromLatin1( "Unknown mime type '%1'.").arg( QString::fromLatin1( mt ) ) ); + MimeType m( QString::fromLatin1( mt ) ); + if ( !m.insert() ) + return failureResponse( QString::fromLatin1( "Unable to create mimetype '%1'.").arg( QString::fromLatin1( mt ) ) ); + mimeType = m; } PimItem item; item.setRev( 0 ); item.setSize( m_size ); - bool ok = db->appendPimItem( m_parts, mimeType, l, m_dateTime, remote_id, item ); + bool ok = db->appendPimItem( m_parts, mimeType, col, m_dateTime, remote_id, item ); response.setTag( tag() ); if ( !ok ) { @@ -174,7 +92,7 @@ } // set message flags - if ( !db->appendItemFlags( item, flags, false, l ) ) + if ( !db->appendItemFlags( item, flags, false, col ) ) return failureResponse( "Unable to append item flags." ); // TODO if the mailbox is currently selected, the normal new message @@ -195,3 +113,94 @@ deleteLater(); return true; } + +bool AkAppend::parseStream() +{ + // Arguments: mailbox name + // OPTIONAL flag parenthesized list + // OPTIONAL date/time string + // (partname literal)+ + // + // Syntax: + // x-akappend = "X-AKAPPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP (partname SP literal)+ + + m_mailbox = m_streamParser->readString(); + + m_size = m_streamParser->readNumber(); + + // parse optional flag parenthesized list + // Syntax: + // flag-list = "(" [flag *(SP flag)] ")" + // flag = "\Answered" / "\Flagged" / "\Deleted" / "\Seen" / + // "\Draft" / flag-keyword / flag-extension + // ; Does not include "\Recent" + // flag-extension = "\" atom + // flag-keyword = atom + if ( m_streamParser->hasList() ) { + m_flags = m_streamParser->readParenthesizedList(); + } + + // parse optional date/time string + if ( m_streamParser->hasDateTime() ) { + m_dateTime = m_streamParser->readDateTime(); + // FIXME Should we return an error if m_dateTime is invalid? + } + // if date/time is not given then it will be set to the current date/time + // by the database + + // parse part specification + QList > > partSpecs; + QByteArray partName = ""; + qint64 partSize = -1; + qint64 partSizes = 0; + bool ok = false; + + QList list = m_streamParser->readParenthesizedList(); + Q_FOREACH( const QByteArray &item, list ) + { + if (partName.isEmpty() && partSize == -1) + { + partName = item; + continue; + } + if (item.startsWith(':')) + { + int pos = 1; + ImapParser::parseNumber( item, partSize, &ok, pos ); + if( !ok ) + partSize = 0; + + int version = 0; + QByteArray plainPartName; + ImapParser::splitVersionedKey( partName, plainPartName, version ); + + partSpecs.append( qMakePair( plainPartName, qMakePair( partSize, version ) ) ); + partName = ""; + partSizes += partSize; + partSize = -1; + } + } + + m_size = qMax( partSizes, m_size ); + + // TODO streaming support! + QByteArray allParts = m_streamParser->readString(); + + // chop up literal data in parts + int pos = 0; // traverse through part data now + QPair > partSpec; + foreach( partSpec, partSpecs ) { + // wrap data into a part + Part part; + part.setName( QLatin1String( partSpec.first ) ); + part.setData( allParts.mid( pos, partSpec.second.first ) ); + if ( partSpec.second.second != 0 ) + part.setVersion( partSpec.second.second ); + m_parts.append( part ); + pos += partSpec.second.first; + } + + return commit(); +} + +#include "akappend.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/akappend.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/akappend.h --- akonadi-1.1.1/server/src/handler/akappend.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/akappend.h 2009-07-28 17:52:13.000000000 +0100 @@ -36,12 +36,13 @@ */ class AKONADIPRIVATE_EXPORT AkAppend : public Handler { + Q_OBJECT public: AkAppend(); ~AkAppend(); - bool handleLine(const QByteArray& line); + bool parseStream(); protected: bool commit(); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/aklist.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/aklist.cpp --- akonadi-1.1.1/server/src/handler/aklist.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/aklist.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -20,43 +20,76 @@ #include "aklist.h" #include -#include "../../libs/imapparser_p.h" #include "storage/datastore.h" #include "storage/entity.h" +#include "storage/selectquerybuilder.h" #include "akonadiconnection.h" #include "response.h" #include "handlerhelper.h" +#include "imapstreamparser.h" using namespace Akonadi; -AkList::AkList(): +AkList::AkList( Scope::SelectionScope scope, bool onlySubscribed ): Handler(), - mOnlySubscribed( false ) + mScope( scope ), + mOnlySubscribed( onlySubscribed ), + mIncludeStatistics( false ) {} AkList::~AkList() {} -bool AkList::handleLine(const QByteArray& line ) +bool AkList::listCollection(const Collection & root, int depth ) { - // parse out the reference name and mailbox name - int pos = line.indexOf( ' ' ) + 1; // skip tag - QByteArray tmp; - - // command - pos = ImapParser::parseString( line, tmp, pos ); - if ( tmp == "X-AKLSUB" ) - mOnlySubscribed = true; - - qint64 baseCollection; - bool ok = false; - pos = ImapParser::parseNumber( line, baseCollection, &ok, pos ); - if ( !ok ) - return failureResponse( "Invalid base collection" ); + // recursive listing of child collections + bool childrenFound = false; + if ( depth > 0 ) { + Collection::List children = root.children(); + foreach ( const Collection &col, children ) { + if ( listCollection( col, depth - 1 ) ) + childrenFound = true; + } + } + + // filter if this node isn't needed by it's children + bool hidden = (mResource.isValid() && root.resourceId() != mResource.id()) || (mOnlySubscribed && !root.subscribed()); + if ( !childrenFound && hidden ) + return false; + + // write out collection details + Collection dummy = root; + DataStore *db = connection()->storageBackend(); + db->activeCachePolicy( dummy ); + const QByteArray b = HandlerHelper::collectionToByteArray( dummy, hidden, mIncludeStatistics ); + + Response response; + response.setUntagged(); + response.setString( b ); + emit responseAvailable( response ); + + return true; +} + +bool AkList::parseStream() +{ + qint64 baseCollection = -1; + QString rid; + if ( mScope == Scope::None || mScope == Scope::Uid ) { + bool ok = false; + baseCollection = m_streamParser->readNumber( &ok ); + if ( !ok ) + return failureResponse( "Invalid base collection" ); + } else if ( mScope == Scope::Rid ) { + rid = m_streamParser->readUtf8String(); + if ( rid.isEmpty() ) + throw HandlerException( "No remote identifier specified" ); + } else + throw HandlerException( "WTF" ); int depth; - pos = ImapParser::parseString( line, tmp, pos ); + const QByteArray tmp = m_streamParser->readString(); if ( tmp.isEmpty() ) return failureResponse( "Specify listing depth" ); if ( tmp == "INF" ) @@ -64,8 +97,7 @@ else depth = tmp.toInt(); - QList filter; - pos = ImapParser::parseParenthesizedList( line, filter, pos ); + QList filter = m_streamParser->readParenthesizedList(); for ( int i = 0; i < filter.count() - 1; i += 2 ) { if ( filter.at( i ) == "RESOURCE" ) { @@ -76,27 +108,61 @@ return failureResponse( "Invalid filter parameter" ); } - Location::List locations; + if ( m_streamParser->hasList() ) { // We got extra options + QList options = m_streamParser->readParenthesizedList(); + for ( int i = 0; i < options.count() - 1; i += 2 ) { + if ( options.at( i ) == "STATISTICS" ) { + if ( QString::fromLatin1( options.at( i + 1 ) ).compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 ) { + mIncludeStatistics = true; + } + } else { + return failureResponse( "Invalid option parameter" ); + } + } + } + + Collection::List collections; if ( baseCollection != 0 ) { // not root - Location loc = Location::retrieveById( baseCollection ); - if ( !loc.isValid() ) + Collection col; + if ( mScope == Scope::None || mScope == Scope::Uid ) { + col = Collection::retrieveById( baseCollection ); + } else if ( mScope == Scope::Rid ) { + SelectQueryBuilder qb; + qb.addTable( Resource::tableName() ); + qb.addValueCondition( Collection::remoteIdFullColumnName(), Query::Equals, rid ); + qb.addColumnCondition( Collection::resourceIdFullColumnName(), Query::Equals, Resource::idFullColumnName() ); + if ( mResource.isValid() ) + qb.addValueCondition( Resource::idFullColumnName(), Query::Equals, mResource.id() ); + else if ( connection()->resourceContext().isValid() ) + qb.addValueCondition( Resource::idFullColumnName(), Query::Equals, connection()->resourceContext().id() ); + else + throw HandlerException( "Cannot retrieve collection based on remote identifier without a resource context" ); + if ( !qb.exec() ) + throw HandlerException( "Unable to retrieve collection for listing" ); + Collection::List results = qb.result(); + if ( results.count() != 1 ) + throw HandlerException( QByteArray::number( results.count() ) + " collections found" ); + col = results.first(); + } else + throw HandlerException( "WTF" ); + if ( !col.isValid() ) return failureResponse( "Collection " + QByteArray::number( baseCollection ) + " does not exist" ); if ( depth == 0 ) - locations << loc; + collections << col; else { - locations << loc.children(); + collections << col.children(); --depth; } } else { if ( depth != 0 ) { - Location::List list = Location::retrieveFiltered( Location::parentIdColumn(), 0 ); - locations << list; + Collection::List list = Collection::retrieveFiltered( Collection::parentIdColumn(), 0 ); + collections << list; } --depth; } - foreach ( const Location &loc, locations ) - listCollection( loc, depth ); + foreach ( const Collection &col, collections ) + listCollection( col, depth ); Response response; response.setSuccess(); @@ -107,33 +173,4 @@ return true; } -bool AkList::listCollection(const Location & root, int depth ) -{ - // recursive listing of child collections - bool childrenFound = false; - if ( depth > 0 ) { - Location::List children = root.children(); - foreach ( const Location &loc, children ) { - if ( listCollection( loc, depth - 1 ) ) - childrenFound = true; - } - } - - // filter if this node isn't needed by it's children - bool hidden = (mResource.isValid() && root.resourceId() != mResource.id()) || (mOnlySubscribed && !root.subscribed()); - if ( !childrenFound && hidden ) - return false; - - // write out collection details - Location dummy = root; - DataStore *db = connection()->storageBackend(); - db->activeCachePolicy( dummy ); - const QByteArray b = HandlerHelper::collectionToByteArray( dummy, hidden ); - - Response response; - response.setUntagged(); - response.setString( b ); - emit responseAvailable( response ); - - return true; -} +#include "aklist.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/aklist.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/aklist.h --- akonadi-1.1.1/server/src/handler/aklist.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/aklist.h 2009-07-28 17:52:13.000000000 +0100 @@ -22,6 +22,7 @@ #include #include +#include #include "akonadiprivate_export.h" namespace Akonadi { @@ -38,15 +39,21 @@ Request: @verbatim - request = tag " " command " " collection-id " " depth " (" filter-list ")" - command = "X-AKLIST" | "X-AKLSUB" + request = tag " " command " " collection-id " " depth " (" filter-list ")" " (" option-list ")" + command = "X-AKLIST" | "X-AKLSUB" | "RID X-AKLIST" | "RID X-AKLSUB" depth = number | "INF" filter-list = *(filter-key " " filter-value) filter-key = "RESOURCE" + option-list = *(option-key " " option-value) + option-key = "STATISTICS" @endverbatim @c X-AKLIST will include all known collections, @c X-AKLSUB only those that are - subscribed or contains subscribed collections (cf. RFC 3591, LIST vs. LSUB). + subscribed or contains subscribed collections (cf. RFC 3501, LIST vs. LSUB). + + The @c RID command prefix indicates that @c collection-id is a remote identifier + instead of a unique identifier. In this case a resource context has to be specified + previously using the @c RESSELECT command. @c depths chooses between recursive (@c INF), flat (1) and local (0, ie. just the base collection) listing, 0 indicates the root collection. @@ -57,7 +64,7 @@ @verbatim response = "*" collection-id " " parent-id " ("attribute-list")" attribute-list = *(attribute-identifier " " attribute-value) - attribute-identifier = "NAME" | "MIMETYPE" | "REMOTEID" | "RESOURCE" | custom-attr-identifier + attribute-identifier = "NAME" | "MIMETYPE" | "REMOTEID" | "RESOURCE" | "MESSAGES" | "UNSEEN" | "SIZE" | "custom-attr-identifier @endverbatim The name is encoded as an quoted UTF-8 string. There is no order defined for the @@ -65,18 +72,22 @@ */ class AKONADIPRIVATE_EXPORT AkList : public Handler { + Q_OBJECT + public: - AkList(); + AkList( Scope::SelectionScope scope, bool onlySubscribed ); ~AkList(); - virtual bool handleLine(const QByteArray& line); + bool parseStream(); private: - bool listCollection( const Location &root, int depth ); + bool listCollection( const Collection &root, int depth ); private: Resource mResource; + Scope::SelectionScope mScope; bool mOnlySubscribed; + bool mIncludeStatistics; }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/append.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/append.cpp --- akonadi-1.1.1/server/src/handler/append.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/append.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -20,17 +20,22 @@ #include "append.h" #include "response.h" -#include "../../libs/imapparser_p.h" #include "handlerhelper.h" +#include "libs/imapparser_p.h" +#include "imapstreamparser.h" #include "akonadi.h" #include "akonadiconnection.h" #include "storage/datastore.h" #include "storage/entity.h" #include "storage/transaction.h" +#include "storage/parthelper.h" +#include "storage/dbconfig.h" #include #include +#include +#include using namespace Akonadi; @@ -44,71 +49,51 @@ { } -bool Akonadi::Append::handleLine(const QByteArray& line ) +bool Append::commit() { - // Arguments: mailbox name - // OPTIONAL flag parenthesized list - // OPTIONAL date/time string - // message literal - // - // Syntax: - // append = "APPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP literal - - const int startOfCommand = line.indexOf( ' ' ) + 1; - const int startOfMailbox = line.indexOf( ' ', startOfCommand ) + 1; - - const int startOfSize = ImapParser::parseString( line, m_mailbox, startOfMailbox ) + 1; - - QString size; - const int startOfFlags = ImapParser::parseString( line, size, startOfSize ) + 1; - m_size = size.toLongLong(); - - QString data; - ImapParser::parseString( line, data, startOfSize); - m_size = data.toLongLong(); - - // parse optional flag parenthesized list - // Syntax: - // flag-list = "(" [flag *(SP flag)] ")" - // flag = "\Answered" / "\Flagged" / "\Deleted" / "\Seen" / - // "\Draft" / flag-keyword / flag-extension - // ; Does not include "\Recent" - // flag-extension = "\" atom - // flag-keyword = atom - int startOfDateTime = startOfFlags; - if ( line[startOfFlags] == '(' ) { - startOfDateTime = ImapParser::parseParenthesizedList( line, m_flags, startOfFlags ) + 1; - } - - // parse optional date/time string - int startOfLiteral = startOfDateTime; - if ( line[startOfDateTime] == '"' ) { - startOfLiteral = ImapParser::parseDateTime( line, m_dateTime, startOfDateTime ); - m_dateTime = m_dateTime.toUTC(); - // FIXME Should we return an error if m_dateTime is invalid? + bool storeInFile = false; + qint64 dataSize = 0; + QTemporaryFile tmpFile; + + //need to read out the data before the below code to avoid hangs, probably at some place the eventloop is reentered + //and more data is read into the stream and that causes the problems. + if ( m_streamParser->hasLiteral() ) { + dataSize = m_streamParser->remainingLiteralSize(); + m_size = qMax( m_size, dataSize ); + storeInFile = DbConfig::useExternalPayloadFile() && dataSize > DbConfig::sizeThreshold(); + if ( storeInFile ) { + if ( !tmpFile.open() ) { + storeInFile = false; + } + } + while ( !m_streamParser->atLiteralEnd() ) { + if ( !storeInFile ) { + m_data += m_streamParser->readLiteralPart(); + } else + { + m_data = m_streamParser->readLiteralPart(); + tmpFile.write( m_data ); + } + } + if ( storeInFile ) { + tmpFile.close(); + m_data = ""; + } } else { - // if date/time is not given then it will be set to the current date/time - // converted to UTC. - m_dateTime = QDateTime::currentDateTime().toUTC(); + m_data = m_streamParser->readString(); } - ImapParser::parseString( line, m_data, startOfLiteral ); - return commit(); -} - -bool Akonadi::Append::commit() -{ Response response; DataStore *db = connection()->storageBackend(); Transaction transaction( db ); - Location l = HandlerHelper::collectionFromIdOrName( m_mailbox ); - if ( !l.isValid() ) + Collection col = HandlerHelper::collectionFromIdOrName( m_mailbox ); + if ( !col.isValid() ) return failureResponse( "Unknown collection." ); QByteArray mt; - QByteArray remote_id; + QString remote_id; QList flags; foreach( const QByteArray &flag, m_flags ) { if ( flag.startsWith( "\\MimeType" ) ) { @@ -118,7 +103,7 @@ } else if ( flag.startsWith( "\\RemoteId" ) ) { int pos1 = flag.indexOf( '[' ); int pos2 = flag.indexOf( ']', pos1 ); - remote_id = flag.mid( pos1 + 1, pos2 - pos1 - 1 ); + remote_id = QString::fromUtf8( flag.mid( pos1 + 1, pos2 - pos1 - 1 ) ); } else flags << flag; } @@ -126,7 +111,10 @@ if ( mt.isEmpty() ) mt = "message/rfc822"; MimeType mimeType = MimeType::retrieveByName( QString::fromLatin1( mt ) ); if ( !mimeType.isValid() ) { - return failureResponse( QString::fromLatin1( "Unknown mime type '%1'.").arg( QString::fromLatin1( mt ) ) ); + MimeType m( QString::fromLatin1( mt ) ); + if ( !m.insert() ) + return failureResponse( QString::fromLatin1( "Unable to create mimetype '%1'.").arg( QString::fromLatin1( mt ) ) ); + mimeType = m; } PimItem item; @@ -139,19 +127,38 @@ part.setName( QLatin1String( "PLD:RFC822" ) ); part.setData( m_data ); part.setPimItemId( item.id() ); + if (storeInFile) { + part.setDatasize( dataSize ); + part.setExternal( true );//force external storage + } + QList parts; parts.append( part ); - bool ok = db->appendPimItem( parts, mimeType, l, m_dateTime, remote_id, item ); + bool ok = db->appendPimItem( parts, mimeType, col, m_dateTime, remote_id, item ); response.setTag( tag() ); if ( !ok ) { return failureResponse( "Append failed" ); } // set message flags - if ( !db->appendItemFlags( item, flags, false, l ) ) + if ( !db->appendItemFlags( item, flags, false, col ) ) return failureResponse( "Unable to append item flags." ); + if (storeInFile) { + part.setExternal( true ); + //the new item was just created and the transaction is not yet committed, so delete + overwrite should be safe, as no + //client knows about the item yet + QString fileName = QString::fromUtf8( parts[0].data() ); + QFile f( fileName ); + if ( !f.remove() ) { + return failureResponse( "Unable to remove item part file" ); + } + if ( !tmpFile.copy( fileName ) ) { + return failureResponse( "Unable to copy item part data from the temporary file" ); + } + } + // TODO if the mailbox is currently selected, the normal new message // actions SHOULD occur. Specifically, the server SHOULD notify the // client immediately via an untagged EXISTS response. @@ -178,3 +185,45 @@ return true; } + +bool Append::parseStream() +{ + // Arguments: mailbox name + // OPTIONAL flag parenthesized list + // OPTIONAL date/time string + // message literal + // + // Syntax: + // append = "APPEND" SP mailbox SP size [SP flag-list] [SP date-time] SP literal + + m_mailbox = m_streamParser->readString(); + + m_size = m_streamParser->readNumber(); + + // parse optional flag parenthesized list + // Syntax: + // flag-list = "(" [flag *(SP flag)] ")" + // flag = "\Answered" / "\Flagged" / "\Deleted" / "\Seen" / + // "\Draft" / flag-keyword / flag-extension + // ; Does not include "\Recent" + // flag-extension = "\" atom + // flag-keyword = atom + if ( m_streamParser->hasList() ) { + m_flags = m_streamParser->readParenthesizedList(); + } + + // parse optional date/time string + if ( m_streamParser->hasDateTime() ) { + m_dateTime = m_streamParser->readDateTime(); + m_dateTime = m_dateTime.toUTC(); + // FIXME Should we return an error if m_dateTime is invalid? + } else { + // if date/time is not given then it will be set to the current date/time + // converted to UTC. + m_dateTime = QDateTime::currentDateTime().toUTC(); + } + + return commit(); +} + +#include "append.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/append.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/append.h --- akonadi-1.1.1/server/src/handler/append.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/append.h 2009-07-28 17:52:13.000000000 +0100 @@ -32,12 +32,13 @@ */ class Append : public Handler { + Q_OBJECT public: Append(); ~Append(); - bool handleLine(const QByteArray& line); + bool parseStream(); protected: bool commit(); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/capability.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/capability.cpp --- akonadi-1.1.1/server/src/handler/capability.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/capability.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -33,18 +33,19 @@ } -bool Capability::handleLine( const QByteArray& ) +bool Capability::parseStream() { - Response response; - response.setString( "CAPABILITY IMAP4 IMAP4rev1" ); - response.setUntagged(); - emit responseAvailable( response ); + Response response; + response.setString( "CAPABILITY IMAP4 IMAP4rev1" ); + response.setUntagged(); + emit responseAvailable( response ); - response.setSuccess(); - response.setTag( tag() ); - response.setString( "CAPABILITY completed" ); - emit responseAvailable( response ); - deleteLater(); - return true; + response.setSuccess(); + response.setTag( tag() ); + response.setString( "CAPABILITY completed" ); + emit responseAvailable( response ); + deleteLater(); + return true; } +#include "capability.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/capability.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/capability.h --- akonadi-1.1.1/server/src/handler/capability.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/capability.h 2009-07-28 17:52:13.000000000 +0100 @@ -30,12 +30,13 @@ */ class Capability : public Handler { + Q_OBJECT public: Capability(); ~Capability(); - bool handleLine(const QByteArray& line); + bool parseStream(); }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/colcopy.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/colcopy.cpp --- akonadi-1.1.1/server/src/handler/colcopy.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/colcopy.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -21,74 +21,84 @@ #include "akonadiconnection.h" #include "handlerhelper.h" -#include "../../libs/imapparser_p.h" #include "storage/datastore.h" #include "storage/transaction.h" +#include "storage/itemretriever.h" +#include "imapstreamparser.h" using namespace Akonadi; -bool ColCopy::handleLine(const QByteArray & line) -{ - QByteArray tmp; - int pos = ImapParser::parseString( line, tmp ); // skip tag - pos = ImapParser::parseString( line, tmp, pos ); // skip command - - pos = ImapParser::parseString( line, tmp, pos ); - const Location source = HandlerHelper::collectionFromIdOrName( tmp ); - if ( !source.isValid() ) - return failureResponse( "No valid source specified" ); - - ImapParser::parseString( line, tmp, pos ); - const Location target = HandlerHelper::collectionFromIdOrName( tmp ); - if ( !target.isValid() ) - return failureResponse( "No valid target specified" ); - - DataStore *store = connection()->storageBackend(); - Transaction transaction( store ); - - if ( !copyCollection( source, target ) ) - return failureResponse( "Failed to copy collection" ); - - if ( !transaction.commit() ) - return failureResponse( "Cannot commit transaction." ); - - return successResponse( "COLCOPY complete" ); -} - -bool ColCopy::copyCollection(const Location & source, const Location & target) +bool ColCopy::copyCollection(const Collection & source, const Collection & target) { // copy the source collection - Location loc = source; - loc.setParentId( target.id() ); - loc.setResourceId( target.resourceId() ); + Collection col = source; + col.setParentId( target.id() ); + col.setResourceId( target.resourceId() ); + // clear remote id on inter-resource copies + if ( source.resourceId() != target.resourceId() ) + col.setRemoteId( QString() ); + DataStore *db = connection()->storageBackend(); - if ( !db->appendLocation( loc ) ) + if ( !db->appendCollection( col ) ) return false; foreach ( const MimeType &mt, source.mimeTypes() ) { - if ( !loc.addMimeType( mt ) ) + if ( !col.addMimeType( mt ) ) return false; } - foreach ( const LocationAttribute &attr, source.attributes() ) { - LocationAttribute newAttr = attr; + foreach ( const CollectionAttribute &attr, source.attributes() ) { + CollectionAttribute newAttr = attr; newAttr.setId( -1 ); - newAttr.setLocationId( loc.id() ); + newAttr.setCollectionId( col.id() ); if ( !newAttr.insert() ) return false; } // copy sub-collections - foreach ( const Location &child, source.children() ) { - if ( !copyCollection( child, loc ) ) + foreach ( const Collection &child, source.children() ) { + if ( !copyCollection( child, col ) ) return false; } // copy items foreach ( const PimItem &item, source.items() ) { - if ( !copyItem( item, loc ) ) + if ( !copyItem( item, col ) ) return false; } return true; } + +bool ColCopy::parseStream() +{ + QByteArray tmp = m_streamParser->readString(); + const Collection source = HandlerHelper::collectionFromIdOrName( tmp ); + if ( !source.isValid() ) + return failureResponse( "No valid source specified" ); + + tmp = m_streamParser->readString(); + const Collection target = HandlerHelper::collectionFromIdOrName( tmp ); + if ( !target.isValid() ) + return failureResponse( "No valid target specified" ); + + // retrieve all not yet cached items of the source + ItemRetriever retriever( connection() ); + retriever.setCollection( source, true ); + retriever.setRetrieveFullPayload( true ); + retriever.exec(); + + DataStore *store = connection()->storageBackend(); + Transaction transaction( store ); + + if ( !copyCollection( source, target ) ) + return failureResponse( "Failed to copy collection" ); + + if ( !transaction.commit() ) + return failureResponse( "Cannot commit transaction." ); + + return successResponse( "COLCOPY complete" ); +} + + +#include "colcopy.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/colcopy.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/colcopy.h --- akonadi-1.1.1/server/src/handler/colcopy.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/colcopy.h 2009-07-28 17:52:13.000000000 +0100 @@ -55,11 +55,12 @@ */ class AKONADIPRIVATE_EXPORT ColCopy : public Copy { + Q_OBJECT public: - bool handleLine(const QByteArray& line); + bool parseStream(); private: - bool copyCollection( const Location& source, const Location &target ); + bool copyCollection( const Collection& source, const Collection &target ); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/colmove.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/colmove.cpp --- akonadi-1.1.1/server/src/handler/colmove.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/colmove.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,70 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "colmove.h" +#include +#include +#include +#include +#include +#include + +namespace Akonadi { + +ColMove::ColMove(Scope::SelectionScope scope) : + m_scope( scope ) +{ +} + + +bool ColMove::parseStream() +{ + // TODO: use m_scope here to support collection sets and RID based operations + Collection source = HandlerHelper::collectionFromIdOrName( m_streamParser->readString() ); + if ( !source.isValid() ) + return failureResponse( "No valid source specified" ); + + qint64 target = m_streamParser->readNumber(); + if ( target < 0 ) + return failureResponse( "No valid destination specified" ); + + if ( source.parentId() == target ) + return successResponse( "COLMOVE complete - nothing to do" ); + + // retrieve all not yet cached items of the source + ItemRetriever retriever( connection() ); + retriever.setCollection( source, true ); + retriever.setRetrieveFullPayload( true ); + retriever.exec(); + + DataStore *store = connection()->storageBackend(); + Transaction transaction( store ); + + if ( !store->renameCollection( source, target, source.name() ) ) + return failureResponse( "Unable to reparent collection" ); + + if ( !transaction.commit() ) + return failureResponse( "Cannot commit transaction." ); + + return successResponse( "COLMOVE complete" ); +} + +} + +#include "colmove.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/colmove.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/colmove.h --- akonadi-1.1.1/server/src/handler/colmove.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/colmove.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,64 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_COLMOVE_H +#define AKONADI_COLMOVE_H + +#include +#include "scope.h" + +namespace Akonadi { + +/** + @ingroup akonadi_server_handler + + Handler for the COLMOVE command. + + This command is used to move a set of collections into another collection, including + all sub-collections and their content. + +

Syntax

+ + Request: + @verbatim + request = tag [" RID"] " COLMOVE " collection-ids " " collection-id + @endverbatim + + @c collection-ids is the set of collections that should be moved, either as UID-set + or as a list of RIDs (in case the @c RID prefix is given). + + @c collection-id is a single collection UID and describes the target collection. + + There is only the usual status response indicating success or failure of the + COLMOVE command +*/ +class ColMove : public Handler +{ + Q_OBJECT + public: + ColMove( Scope::SelectionScope scope ); + virtual bool parseStream(); + + private: + Scope m_scope; +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/copy.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/copy.cpp --- akonadi-1.1.1/server/src/handler/copy.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/copy.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -21,33 +21,58 @@ #include "akonadiconnection.h" #include "handlerhelper.h" -#include "../../libs/imapparser_p.h" -#include "../../libs/imapset_p.h" #include "storage/datastore.h" +#include "storage/itemqueryhelper.h" +#include "storage/itemretriever.h" #include "storage/selectquerybuilder.h" #include "storage/transaction.h" +#include "storage/parthelper.h" + +#include "libs/imapset_p.h" +#include "imapstreamparser.h" using namespace Akonadi; -bool Copy::handleLine(const QByteArray & line) +bool Copy::copyItem(const PimItem & item, const Collection & target) { - QByteArray tmp; - int pos = ImapParser::parseString( line, tmp ); // skip tag - pos = ImapParser::parseString( line, tmp, pos ); // skip command + qDebug() << "Copy::copyItem"; + DataStore *store = connection()->storageBackend(); + PimItem newItem = item; + newItem.setId( -1 ); + newItem.setRev( 0 ); + newItem.setDatetime( QDateTime::currentDateTime() ); + newItem.setAtime( QDateTime::currentDateTime() ); + newItem.setRemoteId( QString() ); + newItem.setCollectionId( target.id() ); + Part::List parts; + foreach ( const Part &part, item.parts() ) { + Part newPart( part ); + newPart.setData(PartHelper::translateData(newPart.id(), newPart.data(), part.external())); + newPart.setPimItemId( -1 ); + parts << newPart; + } + return store->appendPimItem( parts, item.mimeType(), target, QDateTime::currentDateTime(), QString(), newItem ); +} - ImapSet set; - pos = ImapParser::parseSequenceSet( line, set, pos ); +bool Copy::parseStream() +{ + ImapSet set = m_streamParser->readSequenceSet(); if ( set.isEmpty() ) return failureResponse( "No items specified" ); - ImapParser::parseString( line, tmp, pos ); - const Location loc = HandlerHelper::collectionFromIdOrName( tmp ); - if ( !loc.isValid() ) + ItemRetriever retriever( connection() ); + retriever.setItemSet( set ); + retriever.setRetrieveFullPayload( true ); + retriever.exec(); + + const QByteArray tmp = m_streamParser->readString(); + const Collection col = HandlerHelper::collectionFromIdOrName( tmp ); + if ( !col.isValid() ) return failureResponse( "No valid target specified" ); SelectQueryBuilder qb; - imapSetToQuery( set, true, qb ); + ItemQueryHelper::itemSetToQuery( set, qb ); if ( !qb.exec() ) return failureResponse( "Unable to retrieve items" ); PimItem::List items = qb.result(); @@ -56,7 +81,7 @@ Transaction transaction( store ); foreach ( const PimItem &item, items ) { - if ( !copyItem( item, loc ) ) + if ( !copyItem( item, col ) ) return failureResponse( "Unable to copy item" ); } @@ -66,21 +91,4 @@ return successResponse( "COPY complete" ); } -bool Copy::copyItem(const PimItem & item, const Location & target) -{ - DataStore *store = connection()->storageBackend(); - PimItem newItem = item; - newItem.setId( -1 ); - newItem.setRev( 0 ); - newItem.setSize( 0 ); - newItem.setDatetime( QDateTime::currentDateTime() ); - newItem.setRemoteId( QByteArray() ); - newItem.setLocationId( target.id() ); - Part::List parts; - foreach ( const Part &part, item.parts() ) { - Part newPart( part ); - newPart.setPimItemId( -1 ); - parts << newPart; - } - return store->appendPimItem( parts, item.mimeType(), target, QDateTime::currentDateTime(), QByteArray(), newItem ); -} +#include "copy.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/copy.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/copy.h --- akonadi-1.1.1/server/src/handler/copy.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/copy.h 2009-07-28 17:52:13.000000000 +0100 @@ -50,15 +50,17 @@ */ class AKONADIPRIVATE_EXPORT Copy : public Handler { + Q_OBJECT public: - bool handleLine(const QByteArray& line); + + bool parseStream(); protected: /** Copy the given item and all its parts into the @p target. The changes mentioned above are applied. */ - bool copyItem( const PimItem& item, const Location &target ); + bool copyItem( const PimItem& item, const Collection &target ); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/create.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/create.cpp --- akonadi-1.1.1/server/src/handler/create.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/create.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -27,53 +27,65 @@ #include "storage/entity.h" #include "storage/transaction.h" #include "handlerhelper.h" +#include "storage/selectquerybuilder.h" -#include "../../libs/imapparser_p.h" #include "response.h" +#include "libs/imapparser_p.h" +#include "imapstreamparser.h" using namespace Akonadi; -Create::Create(): Handler() +Create::Create( Scope::SelectionScope scope ) : + Handler(), + m_scope( scope ) { } - -Create::~Create() -{ -} - - -bool Create::handleLine(const QByteArray& line ) +bool Create::parseStream() { - int pos = line.indexOf( ' ' ); // skip tag - pos = line.indexOf( ' ', pos + 1 ); // skip command - - QString name; - pos = ImapParser::parseString( line, name, pos ); + QString name = m_streamParser->readUtf8String(); + if ( name.isEmpty() ) + return failureResponse( "Invalid collection name" ); bool ok = false; - qint64 parentId = -1; - Location parent; - pos = ImapParser::parseNumber( line, parentId, &ok, pos ); - if ( !ok ) { // RFC 3501 compat - QString parentPath; - int index = name.lastIndexOf( QLatin1Char('/') ); - if ( index > 0 ) { - parentPath = name.left( index ); - name = name.mid( index + 1 ); - parent = HandlerHelper::collectionFromIdOrName( parentPath.toUtf8() ); - } else - parentId = 0; - } else { - if ( parentId > 0 ) - parent = Location::retrieveById( parentId ); - } + Collection parent; - if ( parentId != 0 && !parent.isValid() ) - return failureResponse( "Parent collection not found" ); + if ( m_scope == Scope::Uid || m_scope == Scope::None ) { + qint64 parentId = m_streamParser->readNumber( &ok ); + if ( !ok ) { // RFC 3501 compat + QString parentPath; + int index = name.lastIndexOf( QLatin1Char('/') ); + if ( index > 0 ) { + parentPath = name.left( index ); + name = name.mid( index + 1 ); + parent = HandlerHelper::collectionFromIdOrName( parentPath.toUtf8() ); + } else + parentId = 0; + } else { + if ( parentId > 0 ) + parent = Collection::retrieveById( parentId ); + } - if ( name.isEmpty() ) - return failureResponse( "Invalid collection name" ); + if ( parentId != 0 && !parent.isValid() ) + return failureResponse( "Parent collection not found" ); + } else if ( m_scope == Scope::Rid ) { + const QString rid = m_streamParser->readUtf8String(); + if ( rid.isEmpty() ) + throw HandlerException( "Empty parent remote identifier" ); + if ( !connection()->resourceContext().isValid() ) + throw HandlerException( "Invalid resource context" ); + SelectQueryBuilder qb; + qb.addValueCondition( Collection::remoteIdColumn(), Query::Equals, rid ); + qb.addValueCondition( Collection::resourceIdColumn(), Query::Equals, connection()->resourceContext().id() ); + if ( !qb.exec() ) + throw HandlerException( "Unable to execute collection query" ); + const Collection::List cols = qb.result(); + if ( cols.size() == 0 ) + throw HandlerException( "Parent collection not found" ); + else if ( cols.size() > 1 ) + throw HandlerException( "Parent collection is not unique" ); + parent = cols.first(); + } qint64 resourceId = 0; MimeType::List parentContentTypes; @@ -101,27 +113,27 @@ resourceId = res.id(); } - Location location; - location.setParentId( parentId ); - location.setName( name.toUtf8() ); - location.setResourceId( resourceId ); + Collection collection; + collection.setParentId( parent.isValid() ? parent.id() : 0 ); + collection.setName( name.toUtf8() ); + collection.setResourceId( resourceId ); // attributes QList attributes; QList mimeTypes; QList< QPair > userDefAttrs; bool mimeTypesSet = false; - pos = ImapParser::parseParenthesizedList( line, attributes, pos ); + attributes = m_streamParser->readParenthesizedList(); for ( int i = 0; i < attributes.count() - 1; i += 2 ) { const QByteArray key = attributes.at( i ); const QByteArray value = attributes.at( i + 1 ); if ( key == "REMOTEID" ) { - location.setRemoteId( QString::fromUtf8( value ) ); + collection.setRemoteId( QString::fromUtf8( value ) ); } else if ( key == "MIMETYPE" ) { ImapParser::parseParenthesizedList( value, mimeTypes ); mimeTypesSet = true; } else if ( key == "CACHEPOLICY" ) { - HandlerHelper::parseCachePolicy( value, location ); + HandlerHelper::parseCachePolicy( value, collection ); } else { userDefAttrs << qMakePair( key, value ); } @@ -130,7 +142,7 @@ DataStore *db = connection()->storageBackend(); Transaction transaction( db ); - if ( !db->appendLocation( location ) ) + if ( !db->appendCollection( collection ) ) return failureResponse( "Could not create collection" ); QStringList effectiveMimeTypes; @@ -141,13 +153,13 @@ foreach ( const MimeType &mt, parentContentTypes ) effectiveMimeTypes << mt.name(); } - if ( !db->appendMimeTypeForLocation( location.id(), effectiveMimeTypes ) ) + if ( !db->appendMimeTypeForCollection( collection.id(), effectiveMimeTypes ) ) return failureResponse( "Unable to append mimetype for collection." ); // store user defined attributes typedef QPair QByteArrayPair; foreach ( const QByteArrayPair &attr, userDefAttrs ) { - if ( !db->addCollectionAttribute( location, attr.first, attr.second ) ) + if ( !db->addCollectionAttribute( collection, attr.first, attr.second ) ) return failureResponse( "Unable to add collection attribute." ); } @@ -155,8 +167,8 @@ response.setUntagged(); // write out collection details - db->activeCachePolicy( location ); - const QByteArray b = HandlerHelper::collectionToByteArray( location ); + db->activeCachePolicy( collection ); + const QByteArray b = HandlerHelper::collectionToByteArray( collection ); response.setString( b ); emit responseAvailable( response ); @@ -166,3 +178,4 @@ return successResponse( "CREATE completed" ); } +#include "create.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/create.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/create.h --- akonadi-1.1.1/server/src/handler/create.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/create.h 2009-07-28 17:52:13.000000000 +0100 @@ -20,6 +20,7 @@ #define AKONADICREATE_H #include +#include "scope.h" namespace Akonadi { @@ -33,23 +34,25 @@ Request: @verbatim - tag " CREATE " collection-name " " parent-collection " (" attribute-list ")" + tag [" RID"] " CREATE " collection-name " " parent-collection " (" attribute-list ")" @endverbatim @c attribute-list is the same as defined in AkList. + @c parent-collection is either a collection UID or a collection RID, depending on the command prefix Response: A untagged response identical to AkList is sent for every created collection. */ class Create : public Handler { + Q_OBJECT public: - Create(); + Create( Scope::SelectionScope scope ); - ~Create(); - - bool handleLine(const QByteArray& line); + bool parseStream(); + private: + Scope::SelectionScope m_scope; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/delete.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/delete.cpp --- akonadi-1.1.1/server/src/handler/delete.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/delete.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -21,53 +21,67 @@ #include #include -#include "../../libs/imapparser_p.h" #include #include #include #include #include "abstractsearchmanager.h" +#include "imapstreamparser.h" +#include +#include using namespace Akonadi; -Delete::Delete() : Handler() +Delete::Delete( Scope scope ) : + Handler(), + m_scope( scope ) { } -Delete::~Delete() +bool Delete::deleteRecursive(Collection & col) { + Collection::List children = col.children(); + foreach ( Collection child, children ) { + if ( !deleteRecursive( child ) ) + return false; + } + DataStore *db = connection()->storageBackend(); + return db->cleanupCollection( col ); } -bool Delete::handleLine(const QByteArray & line) +bool Delete::parseStream() { - int begin = line.indexOf( " DELETE" ) + 7; - QByteArray collection; - if ( line.length() > begin ) - ImapParser::parseString( line, collection, begin ); - - // prevent deletion of the root node - if ( collection.isEmpty() ) - return failureResponse( "Deleting everything is not allowed." ); + m_scope.parseScope( m_streamParser ); + SelectQueryBuilder qb; + CollectionQueryHelper::scopeToQuery( m_scope, connection(), qb ); + if ( !qb.exec() ) + throw HandlerException( "Unable to execute collection query" ); + const Collection::List collections = qb.result(); + if ( collections.isEmpty() ) + throw HandlerException( "No collection selected" ); + else if ( collections.size() > 1 ) + throw HandlerException( "Deleting multiple collections is not yet implemented" ); + // check if collection exists DataStore *db = connection()->storageBackend(); Transaction transaction( db ); - Location location = HandlerHelper::collectionFromIdOrName( collection ); - if ( !location.isValid() ) + Collection collection = collections.first(); + if ( !collection.isValid() ) return failureResponse( "No such collection." ); // handle virtual folders - if ( location.resource().name() == QLatin1String("akonadi_search_resource") ) { + if ( collection.resource().name() == QLatin1String("akonadi_search_resource") ) { // don't delete virtual root - if ( location.parentId() == 0 ) + if ( collection.parentId() == 0 ) return failureResponse( "Cannot delete virtual root collection" ); - if ( !AbstractSearchManager::instance()->removeSearch( location.id() ) ) + if ( !AbstractSearchManager::instance()->removeSearch( collection.id() ) ) return failureResponse( "Failed to remove search from search manager" ); } - if ( !deleteRecursive( location ) ) + if ( !deleteRecursive( collection ) ) return failureResponse( "Unable to delete collection" ); if ( !transaction.commit() ) @@ -81,13 +95,4 @@ return true; } -bool Delete::deleteRecursive(Location & loc) -{ - Location::List children = loc.children(); - foreach ( Location child, children ) { - if ( !deleteRecursive( child ) ) - return false; - } - DataStore *db = connection()->storageBackend(); - return db->cleanupLocation( loc ); -} +#include "delete.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/delete.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/delete.h --- akonadi-1.1.1/server/src/handler/delete.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/delete.h 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -22,24 +22,41 @@ #include #include +#include namespace Akonadi { /** @ingroup akonadi_server_handler - Handler for collection deletion commands. It's basically RFC 3051 compatible, - but also works with still existing sub-collections and content. + Handler for the collection deletion command. + + This commands deletes the selected collections including all their content + and that of any child collection. + +

Syntax

+ + Request: + @verbatim + request = tag [" RID"] " DELETE " collection-ids + @endverbatim + + @c collection-ids is the set of collections that should be deleted, either as UID-set + or as a list of RIDs (in case the @c RID prefix is given). + + There is only the usual status response indicating success or failure of the + DELETE command */ class Delete : public Handler { + Q_OBJECT public: - Delete(); - ~Delete(); - virtual bool handleLine( const QByteArray &line ); + Delete( Scope scope ); + bool parseStream(); private: - bool deleteRecursive( Location &loc ); + bool deleteRecursive( Collection &col ); + Scope m_scope; }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/expunge.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/expunge.cpp --- akonadi-1.1.1/server/src/handler/expunge.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/expunge.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -23,7 +23,9 @@ #include "akonadiconnection.h" #include "response.h" #include "storage/datastore.h" +#include "storage/selectquerybuilder.h" #include "storage/transaction.h" +#include "imapstreamparser.h" using namespace Akonadi; @@ -36,11 +38,10 @@ { } -bool Expunge::handleLine( const QByteArray& ) +bool Expunge::parseStream() { Response response; - Location location = connection()->selectedLocation(); DataStore *store = connection()->storageBackend(); Transaction transaction( store ); @@ -55,23 +56,32 @@ return true; } - QList items = store->listPimItems( location, flag ); - for ( int i = 0; i < items.count(); ++i ) { - if ( store->cleanupPimItem( items[ i ] ) ) { - response.setUntagged(); - // IMAP protocol violation: should actually be the sequence number - response.setString( QByteArray::number( items[i].id() ) + " EXPUNGE" ); - - emit responseAvailable( response ); - } else { - response.setTag( tag() ); - response.setError(); - response.setString( "internal error" ); - - emit responseAvailable( response ); - deleteLater(); - return true; + SelectQueryBuilder qb; + qb.addTable( PimItemFlagRelation::tableName() ); + qb.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, PimItemFlagRelation::leftFullColumnName() ); + qb.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, flag.id() ); + + if ( qb.exec() ) { + const QList items = qb.result(); + foreach ( const PimItem &item, items ) { + if ( store->cleanupPimItem( item ) ) { + response.setUntagged(); + // IMAP protocol violation: should actually be the sequence number + response.setString( QByteArray::number( item.id() ) + " EXPUNGE" ); + + emit responseAvailable( response ); + } else { + response.setTag( tag() ); + response.setError(); + response.setString( "internal error" ); + + emit responseAvailable( response ); + deleteLater(); + return true; + } } + } else { + throw HandlerException( "Unable to execute query." ); } if ( !transaction.commit() ) @@ -86,3 +96,5 @@ return true; } + +#include "expunge.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/expunge.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/expunge.h --- akonadi-1.1.1/server/src/handler/expunge.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/expunge.h 2009-07-28 17:52:13.000000000 +0100 @@ -31,12 +31,13 @@ */ class Expunge : public Handler { + Q_OBJECT public: Expunge(); ~Expunge(); - bool handleLine(const QByteArray& line); + bool parseStream(); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/fetch.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/fetch.cpp --- akonadi-1.1.1/server/src/handler/fetch.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/fetch.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -21,11 +21,17 @@ #include "akonadi.h" #include "akonadiconnection.h" -#include "../../libs/imapparser_p.h" -#include "../../libs/protocol_p.h" +#include "libs/imapparser_p.h" +#include "libs/protocol_p.h" #include "response.h" #include "storage/selectquerybuilder.h" #include "resourceinterface.h" +#include "storage/itemqueryhelper.h" +#include "storage/itemretrievalmanager.h" +#include "storage/parthelper.h" +#include "akdebug.h" +#include "imapstreamparser.h" + #include #include @@ -35,20 +41,8 @@ #include #include -#include - using namespace Akonadi; -class HandlerException : public std::exception -{ - public: - HandlerException( const char *what ) throw() : mWhat( what ) {} - ~HandlerException() throw() {} - const char* what() const throw() { return mWhat.data(); } - private: - QByteArray mWhat; -}; - static const int itemQueryIdColumn = 0; static const int itemQueryRevColumn = 1; static const int itemQueryRidColumn = 2; @@ -56,237 +50,24 @@ static const int itemQueryResouceColumn = 4; static const int itemQuerySizeColumn = 5; static const int itemQueryDatetimeColumn = 6; - +static const int itemQueryCollectionIdColumn = 7; + static const int partQueryIdColumn = 0; static const int partQueryNameColumn = 1; static const int partQueryDataColumn = 2; static const int partQueryVersionColumn = 3; +static const int partQueryExternalColumn = 4; -Fetch::Fetch() +Fetch::Fetch( Scope::SelectionScope scope ) : Handler(), - mIsUidFetch( false ), + mScope( scope ), mCacheOnly( false ), mFullPayload( false ), mAllAttrs( false ), mSizeRequested( false ), - mMTimeRequested( false ) -{ -} - -Fetch::~Fetch() -{ -} - -void Fetch::parseCommand( const QByteArray &line ) + mMTimeRequested( false ), + mExternalPayloadSupported( false ) { - // parse the command - QByteArray buffer; - int pos = ImapParser::parseString( line, buffer ); - - // command - pos = ImapParser::parseString( line, buffer, pos ); - if ( buffer == AKONADI_CMD_UID ) { - mIsUidFetch = true; - pos = ImapParser::parseString( line, buffer, pos ); - } - - // sequence set - pos = ImapParser::parseSequenceSet( line, mSet, pos ); - if ( mSet.isEmpty() ) - throw HandlerException( "No items selected" ); - - // macro vs. attribute list - forever { - pos = ImapParser::stripLeadingSpaces( line, pos ); - if ( pos >= line.count() ) - break; - if ( line[pos] == '(' ) { - QList tmp; - pos = ImapParser::parseParenthesizedList( line, tmp, pos ); - mRequestedParts += tmp; - break; - } else { - pos = ImapParser::parseString( line, buffer, pos ); - if ( buffer == AKONADI_PARAM_CACHEONLY ) { - mCacheOnly = true; - } else if ( buffer == AKONADI_PARAM_ALLATTRIBUTES ) { - mAllAttrs = true; - } else if ( buffer == AKONADI_PARAM_FULLPAYLOAD ) { - mRequestedParts << "PLD:RFC822"; // HACK: temporary workaround until we have support for detecting the availability of the full payload in the server - mFullPayload = true; - } -#if 0 - // IMAP compat stuff - else if ( buffer == "ALL" ) { - mRequestedParts << "FLAGS" << "INTERNALDATE" << "RFC822.SIZE" << "ENVELOPE"; - } else if ( buffer == "FULL" ) { - mRequestedParts << "FLAGS" << "INTERNALDATE" << "RFC822.SIZE"; - } else if ( buffer == "FAST" ) { - mRequestedParts << "FLAGS" << "INTERNALDATE" << "RFC822.SIZE" << "ENVELOPE" << "BODY"; - } -#endif - else { - throw HandlerException( "Invalid command argument" ); - } - } - } -} - -bool Fetch::handleLine( const QByteArray& line ) -{ - try { - parseCommand( line ); - - triggerOnDemandFetch(); - - buildItemQuery(); - - // build flag query if needed - QueryBuilder flagQuery; - if ( mRequestedParts.contains( "FLAGS" ) ) { - flagQuery.addTable( PimItem::tableName() ); - flagQuery.addTable( PimItemFlagRelation::tableName() ); - flagQuery.addTable( Flag::tableName() ); - flagQuery.addColumn( PimItem::idFullColumnName() ); - flagQuery.addColumn( Flag::nameFullColumnName() ); - flagQuery.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, PimItemFlagRelation::leftFullColumnName() ); - flagQuery.addColumnCondition( Flag::idFullColumnName(), Query::Equals, PimItemFlagRelation::rightFullColumnName() ); - imapSetToQuery( mSet, mIsUidFetch, flagQuery ); - flagQuery.addSortColumn( PimItem::idFullColumnName(), Query::Ascending ); - - if ( !flagQuery.exec() ) - return failureResponse( "Unable to retrieve item flags" ); - flagQuery.query().next(); - } - const int flagQueryIdColumn = 0; - const int flagQueryNameColumn = 1; - - // retrieve missing parts - QStringList partList, payloadList; - foreach( const QByteArray &b, mRequestedParts ) { - // filter out non-part attributes - if ( b == "REV" || b == "FLAGS" || b == "UID" || b == "REMOTEID" ) - continue; - if ( b == "SIZE" ) { - mSizeRequested = true; - continue; - } - if ( b == "DATETIME" ) { - mMTimeRequested = true; - continue; - } - partList << QString::fromLatin1( b ); - if ( b.startsWith( "PLD:" ) ) - payloadList << QString::fromLatin1( b ); - } - retrieveMissingPayloads( payloadList ); - - // build part query if needed - QueryBuilder partQuery; - if ( !partList.isEmpty() || mFullPayload || mAllAttrs ) { - partQuery = buildPartQuery( partList, mFullPayload, mAllAttrs ); - if ( !partQuery.exec() ) - return failureResponse( "Unable to retrieve item parts" ); - partQuery.query().next(); - } - - // build responses - Response response; - response.setUntagged(); - while ( mItemQuery.query().isValid() ) { - const qint64 pimItemId = mItemQuery.query().value( itemQueryIdColumn ).toLongLong(); - const int pimItemRev = mItemQuery.query().value( itemQueryRevColumn ).toInt(); - - QList attributes; - attributes.append( "UID " + QByteArray::number( pimItemId ) ); - attributes.append( "REV " + QByteArray::number( pimItemRev ) ); - attributes.append( "REMOTEID " + ImapParser::quote( mItemQuery.query().value( itemQueryRidColumn ).toString().toUtf8() ) ); - attributes.append( "MIMETYPE " + ImapParser::quote( mItemQuery.query().value( itemQueryMimeTypeColumn ).toString().toUtf8() ) ); - - if ( mSizeRequested ) { - const qint64 pimItemSize = mItemQuery.query().value( itemQuerySizeColumn ).toLongLong(); - attributes.append( "SIZE " + QByteArray::number( pimItemSize ) ); - } - if ( mMTimeRequested ) { - const QDateTime pimItemDatetime = mItemQuery.query().value( itemQueryDatetimeColumn ).toDateTime(); - // Date time is always stored in UTC time zone by the server. - QString datetime = QLocale::c().toString( pimItemDatetime, QLatin1String( "dd-MMM-yyyy hh:mm:ss +0000" ) ); - attributes.append( "DATETIME " + ImapParser::quote( datetime.toUtf8() ) ); - } - - if ( mRequestedParts.contains( "FLAGS" ) ) { - QList flags; - while ( flagQuery.query().isValid() ) { - const qint64 id = flagQuery.query().value( flagQueryIdColumn ).toLongLong(); - if ( id < pimItemId ) { - flagQuery.query().next(); - continue; - } else if ( id > pimItemId ) { - break; - } - flags << flagQuery.query().value( flagQueryNameColumn ).toString().toUtf8(); - flagQuery.query().next(); - } - attributes.append( "FLAGS (" + ImapParser::join( flags, " " ) + ')' ); - } - - while ( partQuery.query().isValid() ) { - const qint64 id = partQuery.query().value( partQueryIdColumn ).toLongLong(); - if ( id < pimItemId ) { - partQuery.query().next(); - continue; - } else if ( id > pimItemId ) { - break; - } - QByteArray partName = partQuery.query().value( partQueryNameColumn ).toString().toUtf8(); - QByteArray part = partQuery.query().value( partQueryNameColumn ).toString().toUtf8(); - QByteArray data = partQuery.query().value( partQueryDataColumn ).toByteArray(); - int version = partQuery.query().value( partQueryVersionColumn ).toInt(); - if ( version != 0 ) { // '0' is the default, so don't send it - part += "[" + QByteArray::number( version ) + "]"; - } - if ( data.isNull() ) { - part += " NIL"; - } else if ( data.isEmpty() ) { - part += " \"\""; - } else { - part += " {" + QByteArray::number( data.length() ) + "}\n"; - part += data; - } - - if ( mRequestedParts.contains( partName ) || mFullPayload || mAllAttrs ) - attributes << part; - - partQuery.query().next(); - } - - // IMAP protocol violation: should actually be the sequence number - QByteArray attr = QByteArray::number( pimItemId ) + " FETCH ("; - attr += ImapParser::join( attributes, " " ) + ')'; - response.setUntagged(); - response.setString( attr ); - emit responseAvailable( response ); - - mItemQuery.query().next(); - } - - // update atime - updateItemAccessTime(); - - } catch ( std::exception &e ) { - return failureResponse( e.what() ); - } - - Response response; - response.setTag( tag() ); - response.setSuccess(); - if ( mIsUidFetch ) - response.setString( "UID FETCH completed" ); - else - response.setString( "FETCH completed" ); - emit responseAvailable( response ); - deleteLater(); - return true; } void Fetch::updateItemAccessTime() @@ -294,24 +75,24 @@ QueryBuilder qb( QueryBuilder::Update ); qb.addTable( PimItem::tableName() ); qb.updateColumnValue( PimItem::atimeColumn(), QDateTime::currentDateTime() ); - imapSetToQuery( mSet, mIsUidFetch, qb ); + ItemQueryHelper::scopeToQuery( mScope, connection(), qb ); if ( !qb.exec() ) qWarning() << "Unable to update item access time"; } void Fetch::triggerOnDemandFetch() { - if ( mIsUidFetch || connection()->selectedCollection() <= 0 ) + if ( mScope.scope() != Scope::None || connection()->selectedCollectionId() <= 0 ) return; - Location loc = connection()->selectedLocation(); + Collection col = connection()->selectedCollection(); // HACK: don't trigger on-demand syncing if the resource is the one triggering it - if ( connection()->sessionId() == loc.resource().name().toLatin1() ) + if ( connection()->sessionId() == col.resource().name().toLatin1() ) return; DataStore *store = connection()->storageBackend(); - store->activeCachePolicy( loc ); - if ( !loc.cachePolicySyncOnDemand() ) + store->activeCachePolicy( col ); + if ( !col.cachePolicySyncOnDemand() ) return; - store->triggerCollectionSync( loc ); + ItemRetrievalManager::instance()->requestCollectionSync( col ); } QueryBuilder Fetch::buildPartQuery( const QStringList &partList, bool allPayload, bool allAttrs ) @@ -324,16 +105,17 @@ partQuery.addColumn( Part::nameFullColumnName() ); partQuery.addColumn( Part::dataFullColumnName() ); partQuery.addColumn( Part::versionFullColumnName() ); + partQuery.addColumn( Part::externalFullColumnName() ); partQuery.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, Part::pimItemIdFullColumnName() ); Query::Condition cond( Query::Or ); if ( !partList.isEmpty() ) cond.addValueCondition( Part::nameFullColumnName(), Query::In, partList ); if ( allPayload ) - cond.addValueCondition( QString::fromLatin1( "left( %1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "PLD:" ) ); + cond.addValueCondition( QString::fromLatin1( "substr( %1, 1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "PLD:" ) ); if ( allAttrs ) - cond.addValueCondition( QString::fromLatin1( "left( %1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "ATR:" ) ); + cond.addValueCondition( QString::fromLatin1( "substr( %1, 1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "ATR:" ) ); partQuery.addCondition( cond ); - imapSetToQuery( mSet, mIsUidFetch, partQuery ); + ItemQueryHelper::scopeToQuery( mScope, connection(), partQuery ); partQuery.addSortColumn( PimItem::idFullColumnName(), Query::Ascending ); } return partQuery; @@ -343,7 +125,7 @@ { mItemQuery.addTable( PimItem::tableName() ); mItemQuery.addTable( MimeType::tableName() ); - mItemQuery.addTable( Location::tableName() ); + mItemQuery.addTable( Collection::tableName() ); mItemQuery.addTable( Resource::tableName() ); // make sure the columns indexes here and in the constants defined above match mItemQuery.addColumn( PimItem::idFullColumnName() ); @@ -353,10 +135,11 @@ mItemQuery.addColumn( Resource::nameFullColumnName() ); mItemQuery.addColumn( PimItem::sizeFullColumnName() ); mItemQuery.addColumn( PimItem::datetimeFullColumnName() ); + mItemQuery.addColumn( PimItem::collectionIdFullColumnName() ); mItemQuery.addColumnCondition( PimItem::mimeTypeIdFullColumnName(), Query::Equals, MimeType::idFullColumnName() ); - mItemQuery.addColumnCondition( PimItem::locationIdFullColumnName(), Query::Equals, Location::idFullColumnName() ); - mItemQuery.addColumnCondition( Location::resourceIdFullColumnName(), Query::Equals, Resource::idFullColumnName() ); - imapSetToQuery( mSet, mIsUidFetch, mItemQuery ); + mItemQuery.addColumnCondition( PimItem::collectionIdFullColumnName(), Query::Equals, Collection::idFullColumnName() ); + mItemQuery.addColumnCondition( Collection::resourceIdFullColumnName(), Query::Equals, Resource::idFullColumnName() ); + ItemQueryHelper::scopeToQuery( mScope, connection(), mItemQuery ); mItemQuery.addSortColumn( PimItem::idFullColumnName(), Query::Ascending ); if ( !mItemQuery.exec() ) @@ -369,8 +152,6 @@ if ( mCacheOnly || (payloadList.isEmpty() && !mFullPayload) ) return; - DataStore *store = connection()->storageBackend(); - // TODO: I'm sure this can be done with a single query instead of manually QueryBuilder partQuery = buildPartQuery( payloadList, mFullPayload, false ); if ( !partQuery.exec() ) @@ -390,7 +171,10 @@ } QString partName = partQuery.query().value( partQueryNameColumn ).toString(); if ( partName.startsWith( QLatin1String( "PLD:" ) ) ) { - if ( partQuery.query().value( partQueryDataColumn ).toByteArray().isNull() ) { + qint64 partId = partQuery.query().value( partQueryIdColumn ).toLongLong(); + QByteArray data = partQuery.query().value( partQueryDataColumn ).toByteArray(); + data = PartHelper::translateData(partId, data, partQuery.query().value( partQueryExternalColumn ).toBool()); + if ( data.isNull() ) { if ( mFullPayload && !missingParts.contains( partName ) ) missingParts << partName; } else { @@ -403,9 +187,16 @@ QStringList missingPayloadIds; foreach ( const QString &s, missingParts ) missingPayloadIds << s.mid( 4 ); - store->retrieveDataFromResource( pimItemId, mItemQuery.query().value( itemQueryRidColumn ).toString().toUtf8(), - mItemQuery.query().value( itemQueryMimeTypeColumn ).toString().toUtf8(), - mItemQuery.query().value( itemQueryResouceColumn ).toString(), missingPayloadIds ); + // TODO: how should we handle retrieval errors here? so far they have been ignored, + // which makes sense in some cases, do we need a command parameter for this? + try { + ItemRetrievalManager::instance()->requestItemDelivery( pimItemId, + mItemQuery.query().value( itemQueryRidColumn ).toString().toUtf8(), + mItemQuery.query().value( itemQueryMimeTypeColumn ).toString().toUtf8(), + mItemQuery.query().value( itemQueryResouceColumn ).toString(), missingPayloadIds ); + } catch ( const ItemRetrieverException &e ) { + akError() << e.type() << ": " << e.what(); + } } mItemQuery.query().next(); } @@ -413,3 +204,203 @@ // rewind item query mItemQuery.query().first(); } + +bool Fetch::parseStream() +{ + parseCommandStream(); + + triggerOnDemandFetch(); + + buildItemQuery(); + + // build flag query if needed + QueryBuilder flagQuery; + if ( mRequestedParts.contains( "FLAGS" ) ) { + flagQuery.addTable( PimItem::tableName() ); + flagQuery.addTable( PimItemFlagRelation::tableName() ); + flagQuery.addTable( Flag::tableName() ); + flagQuery.addColumn( PimItem::idFullColumnName() ); + flagQuery.addColumn( Flag::nameFullColumnName() ); + flagQuery.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, PimItemFlagRelation::leftFullColumnName() ); + flagQuery.addColumnCondition( Flag::idFullColumnName(), Query::Equals, PimItemFlagRelation::rightFullColumnName() ); + ItemQueryHelper::scopeToQuery( mScope, connection(), flagQuery ); + flagQuery.addSortColumn( PimItem::idFullColumnName(), Query::Ascending ); + + if ( !flagQuery.exec() ) + return failureResponse( "Unable to retrieve item flags" ); + flagQuery.query().next(); + } + const int flagQueryIdColumn = 0; + const int flagQueryNameColumn = 1; + + // retrieve missing parts + QStringList partList, payloadList; + foreach( const QByteArray &b, mRequestedParts ) { + // filter out non-part attributes + if ( b == "REV" || b == "FLAGS" || b == "UID" || b == "REMOTEID" ) + continue; + if ( b == "SIZE" ) { + mSizeRequested = true; + continue; + } + if ( b == "DATETIME" ) { + mMTimeRequested = true; + continue; + } + partList << QString::fromLatin1( b ); + if ( b.startsWith( "PLD:" ) ) + payloadList << QString::fromLatin1( b ); + } + retrieveMissingPayloads( payloadList ); + + // build part query if needed + QueryBuilder partQuery; + if ( !partList.isEmpty() || mFullPayload || mAllAttrs ) { + partQuery = buildPartQuery( partList, mFullPayload, mAllAttrs ); + if ( !partQuery.exec() ) + return failureResponse( "Unable to retrieve item parts" ); + partQuery.query().next(); + } + + // build responses + Response response; + response.setUntagged(); + while ( mItemQuery.query().isValid() ) { + const qint64 pimItemId = mItemQuery.query().value( itemQueryIdColumn ).toLongLong(); + const int pimItemRev = mItemQuery.query().value( itemQueryRevColumn ).toInt(); + + QList attributes; + attributes.append( "UID " + QByteArray::number( pimItemId ) ); + attributes.append( "REV " + QByteArray::number( pimItemRev ) ); + attributes.append( "REMOTEID " + ImapParser::quote( mItemQuery.query().value( itemQueryRidColumn ).toString().toUtf8() ) ); + attributes.append( "MIMETYPE " + ImapParser::quote( mItemQuery.query().value( itemQueryMimeTypeColumn ).toString().toUtf8() ) ); + attributes.append( "COLLECTIONID " + ImapParser::quote( mItemQuery.query().value( itemQueryCollectionIdColumn ).toString().toUtf8() ) ); + + if ( mSizeRequested ) { + const qint64 pimItemSize = mItemQuery.query().value( itemQuerySizeColumn ).toLongLong(); + attributes.append( "SIZE " + QByteArray::number( pimItemSize ) ); + } + if ( mMTimeRequested ) { + const QDateTime pimItemDatetime = mItemQuery.query().value( itemQueryDatetimeColumn ).toDateTime(); + // Date time is always stored in UTC time zone by the server. + QString datetime = QLocale::c().toString( pimItemDatetime, QLatin1String( "dd-MMM-yyyy hh:mm:ss +0000" ) ); + attributes.append( "DATETIME " + ImapParser::quote( datetime.toUtf8() ) ); + } + + if ( mRequestedParts.contains( "FLAGS" ) ) { + QList flags; + while ( flagQuery.query().isValid() ) { + const qint64 id = flagQuery.query().value( flagQueryIdColumn ).toLongLong(); + if ( id < pimItemId ) { + flagQuery.query().next(); + continue; + } else if ( id > pimItemId ) { + break; + } + flags << flagQuery.query().value( flagQueryNameColumn ).toString().toUtf8(); + flagQuery.query().next(); + } + attributes.append( "FLAGS (" + ImapParser::join( flags, " " ) + ')' ); + } + + while ( partQuery.query().isValid() ) { + const qint64 id = partQuery.query().value( partQueryIdColumn ).toLongLong(); + if ( id < pimItemId ) { + partQuery.query().next(); + continue; + } else if ( id > pimItemId ) { + break; + } + QByteArray partName = partQuery.query().value( partQueryNameColumn ).toString().toUtf8(); + QByteArray part = partQuery.query().value( partQueryNameColumn ).toString().toUtf8(); + QByteArray data = partQuery.query().value( partQueryDataColumn ).toByteArray(); + bool partIsExternal = partQuery.query().value( partQueryExternalColumn ).toBool(); + if ( !mExternalPayloadSupported && partIsExternal ) //external payload not supported by the client, translate the data + data = PartHelper::translateData(id, data, partIsExternal ); + int version = partQuery.query().value( partQueryVersionColumn ).toInt(); + if ( version != 0 ) { // '0' is the default, so don't send it + part += "[" + QByteArray::number( version ) + "]"; + } + if ( mExternalPayloadSupported && partIsExternal ) { // external data and this is supported by the client + part += " [FILE] "; + } + if ( data.isNull() ) { + part += " NIL"; + } else if ( data.isEmpty() ) { + part += " \"\""; + } else { + part += " {" + QByteArray::number( data.length() ) + "}\n"; + part += data; + } + + if ( mRequestedParts.contains( partName ) || mFullPayload || mAllAttrs ) + attributes << part; + + partQuery.query().next(); + } + + // IMAP protocol violation: should actually be the sequence number + QByteArray attr = QByteArray::number( pimItemId ) + " FETCH ("; + attr += ImapParser::join( attributes, " " ) + ')'; + response.setUntagged(); + response.setString( attr ); + emit responseAvailable( response ); + + mItemQuery.query().next(); + } + + // update atime + updateItemAccessTime(); + + if ( mScope.scope() == Scope::Uid ) + successResponse( "UID FETCH completed" ); + else if ( mScope.scope() == Scope::Rid ) + successResponse( "RID FETCH completed" ); + else + successResponse( "FETCH completed" ); + deleteLater(); + return true; +} + +void Fetch::parseCommandStream() +{ + // sequence set + mScope.parseScope( m_streamParser ); + + // macro vs. attribute list + forever { + if ( m_streamParser->atCommandEnd() ) + break; + if ( m_streamParser->hasList() ) { + QList tmp =m_streamParser->readParenthesizedList(); + mRequestedParts += tmp; + break; + } else { + const QByteArray buffer = m_streamParser->readString(); + if ( buffer == AKONADI_PARAM_CACHEONLY ) { + mCacheOnly = true; + } else if ( buffer == AKONADI_PARAM_ALLATTRIBUTES ) { + mAllAttrs = true; + } else if ( buffer == AKONADI_PARAM_EXTERNALPAYLOAD ) { + mExternalPayloadSupported = true; + }else if ( buffer == AKONADI_PARAM_FULLPAYLOAD ) { + mRequestedParts << "PLD:RFC822"; // HACK: temporary workaround until we have support for detecting the availability of the full payload in the server + mFullPayload = true; + } +#if 0 + // IMAP compat stuff + else if ( buffer == "ALL" ) { + mRequestedParts << "FLAGS" << "INTERNALDATE" << "RFC822.SIZE" << "ENVELOPE"; + } else if ( buffer == "FULL" ) { + mRequestedParts << "FLAGS" << "INTERNALDATE" << "RFC822.SIZE"; + } else if ( buffer == "FAST" ) { + mRequestedParts << "FLAGS" << "INTERNALDATE" << "RFC822.SIZE" << "ENVELOPE" << "BODY"; + } +#endif + else { + throw HandlerException( "Invalid command argument" ); + } + } + } +} + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/fetch.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/fetch.h --- akonadi-1.1.1/server/src/handler/fetch.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/fetch.h 2009-07-28 17:52:13.000000000 +0100 @@ -22,8 +22,8 @@ #include -#include "../../libs/imapset_p.h" - +#include "scope.h" +#include "libs/imapset_p.h" #include "storage/datastore.h" namespace Akonadi { @@ -38,12 +38,11 @@ */ class Fetch : public Handler { + Q_OBJECT public: - Fetch(); - - ~Fetch(); + Fetch( Scope::SelectionScope scope ); - bool handleLine(const QByteArray& line); + bool parseStream(); private: void parseCommand( const QByteArray &line ); @@ -52,17 +51,18 @@ void buildItemQuery(); QueryBuilder buildPartQuery( const QStringList &partList, bool allPayload, bool allAttrs ); void retrieveMissingPayloads( const QStringList &payloadList ); + void parseCommandStream(); private: QueryBuilder mItemQuery; - ImapSet mSet; QList mRequestedParts; - bool mIsUidFetch; + Scope mScope; bool mCacheOnly; bool mFullPayload; bool mAllAttrs; bool mSizeRequested; bool mMTimeRequested; + bool mExternalPayloadSupported; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/link.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/link.cpp --- akonadi-1.1.1/server/src/handler/link.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/link.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -22,11 +22,12 @@ #include "akonadiconnection.h" #include "handlerhelper.h" #include "storage/datastore.h" +#include "storage/itemqueryhelper.h" #include "storage/transaction.h" #include "storage/selectquerybuilder.h" #include "entities.h" -#include "../../libs/imapparser_p.h" +#include "imapstreamparser.h" using namespace Akonadi; @@ -34,24 +35,19 @@ { } -bool Link::handleLine(const QByteArray & line) +bool Link::parseStream() { - QByteArray tmp; - int pos = ImapParser::parseString( line, tmp ); // skip tag - pos = ImapParser::parseString( line, tmp, pos ); // skip command - - pos = ImapParser::parseString( line, tmp, pos ); - const Location collection = HandlerHelper::collectionFromIdOrName( tmp ); + QByteArray tmp = m_streamParser->readString(); + const Collection collection = HandlerHelper::collectionFromIdOrName( tmp ); if ( !collection.isValid() ) return failureResponse( "No valid collection specified" ); - ImapSet set; - ImapParser::parseSequenceSet( line, set, pos ); + ImapSet set = m_streamParser->readSequenceSet(); if ( set.isEmpty() ) return failureResponse( "No valid sequence set specified" ); SelectQueryBuilder qb; - imapSetToQuery( set, true, qb ); + ItemQueryHelper::itemSetToQuery( set, qb ); if ( !qb.exec() ) return failureResponse( "Unable to execute item query" ); @@ -79,3 +75,5 @@ return successResponse( "LINK complete" ); } + +#include "link.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/link.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/link.h --- akonadi-1.1.1/server/src/handler/link.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/link.h 2009-07-28 17:52:13.000000000 +0100 @@ -45,12 +45,13 @@ */ class Link : public Handler { + Q_OBJECT public: /** * @param create @c true adds references, @c false removes them */ Link( bool create ); - bool handleLine( const QByteArray &line ); + bool parseStream(); private: bool mCreateLinks; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/list.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/list.cpp --- akonadi-1.1.1/server/src/handler/list.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/list.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -24,8 +24,8 @@ #include "akonadiconnection.h" #include "storage/datastore.h" #include "storage/entity.h" -#include "../../libs/imapparser_p.h" #include "handlerhelper.h" +#include "imapstreamparser.h" #include "response.h" @@ -41,78 +41,9 @@ } -bool List::handleLine(const QByteArray& line ) -{ - // parse out the reference name and mailbox name - int pos = line.indexOf( ' ' ) + 1; // skip tag - pos = line.indexOf( ' ', pos ) + 1; // skip command - QString reference; - pos = ImapParser::parseString( line, reference, pos ); - QString mailbox; - ImapParser::parseString( line, mailbox, pos ); - -// qDebug() << "reference:" << reference << "mailbox:" << mailbox << "::" << endl; - - Response response; - response.setUntagged(); - - if ( mailbox.isEmpty() ) { // special case of asking for the delimiter - response.setString( "LIST (\\Noselect) \"/\" \"\"" ); - emit responseAvailable( response ); - } else { - QList collections; - if ( !listCollections( reference, mailbox, collections ) ) { - return failureResponse( "Unable to find collection" ); - } - - foreach ( const Location &loc, collections ) { - QByteArray list( "LIST "); - list += '('; - bool first = true; - QList supportedMimeTypes = loc.mimeTypes(); - if ( supportedMimeTypes.isEmpty() ) { - list += "\\Noinferiors"; - first = false; - } - bool canContainFolders = false; - foreach ( const MimeType &mt, supportedMimeTypes ) { - if ( mt.name() == QLatin1String("inode/directory") ) { - canContainFolders = true; - break; - } - } - if ( canContainFolders ) { - if ( !first ) list += ' '; - list += "\\Noselect"; - first = false; - } - if ( !supportedMimeTypes.isEmpty() ) { - if ( !first ) list += ' '; - list += "\\MimeTypes[" + MimeType::joinByName( supportedMimeTypes, QLatin1String(",") ).toLatin1() + ']'; - } - list += ") "; - list += "\"/\" \""; // FIXME delimiter - if ( loc.isValid() ) - list += HandlerHelper::pathForCollection( loc ).toUtf8(); - else - list += loc.name(); // search folder - list += "\""; - response.setString( list ); - emit responseAvailable( response ); - } - } - - response.setSuccess(); - response.setTag( tag() ); - response.setString( "List completed" ); - emit responseAvailable( response ); - deleteLater(); - return true; -} - bool List::listCollections( const QString & prefix, const QString & mailboxPattern, - QList &result ) + QList &result ) { bool rv = true; result.clear(); @@ -121,18 +52,18 @@ return true; DataStore *db = connection()->storageBackend(); - const QString locationDelimiter = db->locationDelimiter(); + const QString collectionDelimiter = db->collectionDelimiter(); // normalize path and pattern QString sanitizedPattern( mailboxPattern ); QString fullPrefix( prefix ); const bool hasPercent = mailboxPattern.contains( QLatin1Char('%') ); const bool hasStar = mailboxPattern.contains( QLatin1Char('*') ); - const int endOfPath = mailboxPattern.lastIndexOf( locationDelimiter ) + 1; + const int endOfPath = mailboxPattern.lastIndexOf( collectionDelimiter ) + 1; Resource resource; if ( fullPrefix.startsWith( QLatin1Char('#') ) ) { - int endOfRes = fullPrefix.indexOf( locationDelimiter ); + int endOfRes = fullPrefix.indexOf( collectionDelimiter ); QString resourceName; if ( endOfRes < 0 ) { resourceName = fullPrefix.mid( 1 ); @@ -150,8 +81,8 @@ } } - if ( !mailboxPattern.startsWith( locationDelimiter ) && fullPrefix != locationDelimiter ) - fullPrefix += locationDelimiter; + if ( !mailboxPattern.startsWith( collectionDelimiter ) && fullPrefix != collectionDelimiter ) + fullPrefix += collectionDelimiter; fullPrefix += mailboxPattern.left( endOfPath ); if ( hasPercent ) @@ -167,30 +98,96 @@ rv = false; } - QList locations; + QList collections; if ( resource.isValid() ) - locations = Location::retrieveFiltered( Location::resourceIdColumn(), resource.id() ); + collections = Collection::retrieveFiltered( Collection::resourceIdColumn(), resource.id() ); else - locations = Location::retrieveAll(); + collections = Collection::retrieveAll(); - foreach( const Location &l, locations ) { - const QString location = locationDelimiter + HandlerHelper::pathForCollection( l ); + foreach( const Collection &col, collections ) { + const QString collection = collectionDelimiter + HandlerHelper::pathForCollection( col ); #if 0 - qDebug() << "Location: " << location << " l: " << l << " prefix: " << fullPrefix; + qDebug() << "Collection: " << collection << " col: " << col << " prefix: " << fullPrefix; #endif const bool atFirstLevel = - location.lastIndexOf( locationDelimiter ) == fullPrefix.lastIndexOf( locationDelimiter ); - if ( location.startsWith( fullPrefix ) ) { + collection.lastIndexOf( collectionDelimiter ) == fullPrefix.lastIndexOf( collectionDelimiter ); + if ( collection.startsWith( fullPrefix ) ) { if ( hasStar || ( hasPercent && atFirstLevel ) || - location == fullPrefix + sanitizedPattern ) { - result.append( l ); + collection == fullPrefix + sanitizedPattern ) { + result.append( col ); } } // Check, if requested folder has been found to distinguish between // non-existant folder and empty folder. - if ( location + locationDelimiter == fullPrefix || fullPrefix == locationDelimiter ) + if ( collection + collectionDelimiter == fullPrefix || fullPrefix == collectionDelimiter ) rv = true; } return rv; } + +bool List::parseStream() +{ + QString reference = m_streamParser->readUtf8String(); + QString mailbox = m_streamParser->readUtf8String(); + +// qDebug() << "reference:" << reference << "mailbox:" << mailbox << "::" << endl; + + Response response; + response.setUntagged(); + + if ( mailbox.isEmpty() ) { // special case of asking for the delimiter + response.setString( "LIST (\\Noselect) \"/\" \"\"" ); + emit responseAvailable( response ); + } else { + QList collections; + if ( !listCollections( reference, mailbox, collections ) ) { + return failureResponse( "Unable to find collection" ); + } + + foreach ( const Collection &col, collections ) { + QByteArray list( "LIST "); + list += '('; + bool first = true; + QList supportedMimeTypes = col.mimeTypes(); + if ( supportedMimeTypes.isEmpty() ) { + list += "\\Noinferiors"; + first = false; + } + bool canContainFolders = false; + foreach ( const MimeType &mt, supportedMimeTypes ) { + if ( mt.name() == QLatin1String("inode/directory") ) { + canContainFolders = true; + break; + } + } + if ( canContainFolders ) { + if ( !first ) list += ' '; + list += "\\Noselect"; + first = false; + } + if ( !supportedMimeTypes.isEmpty() ) { + if ( !first ) list += ' '; + list += "\\MimeTypes[" + MimeType::joinByName( supportedMimeTypes, QLatin1String(",") ).toLatin1() + ']'; + } + list += ") "; + list += "\"/\" \""; // FIXME delimiter + if ( col.isValid() ) + list += HandlerHelper::pathForCollection( col ).toUtf8(); + else + list += col.name(); // search folder + list += "\""; + response.setString( list ); + emit responseAvailable( response ); + } + } + + response.setSuccess(); + response.setTag( tag() ); + response.setString( "List completed" ); + emit responseAvailable( response ); + deleteLater(); + return true; +} + +#include "list.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/list.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/list.h --- akonadi-1.1.1/server/src/handler/list.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/list.h 2009-07-28 17:52:13.000000000 +0100 @@ -24,7 +24,7 @@ namespace Akonadi { -class Location; +class Collection; /** @ingroup akonadi_server_handler @@ -33,17 +33,18 @@ */ class AKONADIPRIVATE_EXPORT List : public Handler { + Q_OBJECT public: List(); ~List(); - bool handleLine(const QByteArray& line); + bool parseStream(); private: virtual bool listCollections( const QString& prefix, const QString& mailboxPattern, - QList &result ); + QList &result ); }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/login.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/login.cpp --- akonadi-1.1.1/server/src/handler/login.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/login.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -21,8 +21,8 @@ #include #include "response.h" -#include "../../libs/imapparser_p.h" #include "akonadiconnection.h" +#include "imapstreamparser.h" using namespace Akonadi; @@ -36,15 +36,9 @@ } -bool Login::handleLine( const QByteArray &line ) +bool Login::parseStream() { - int pos = line.indexOf( ' ' ) + 1; // skip tag - pos = line.indexOf( ' ', pos ); // skip command - if ( pos < 0 ) - return failureResponse( "Invalid syntax" ); - - QByteArray sessionId; - pos = ImapParser::parseString( line, sessionId, pos ); + QByteArray sessionId = m_streamParser->readString(); if ( sessionId.isEmpty() ) return failureResponse( "Missing session identifier." ); connection()->setSessionId( sessionId ); @@ -60,3 +54,4 @@ return true; } +#include "login.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/login.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/login.h --- akonadi-1.1.1/server/src/handler/login.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/login.h 2009-07-28 17:52:13.000000000 +0100 @@ -30,13 +30,13 @@ */ class Login : public Handler { -public: + Q_OBJECT + public: Login(); ~Login(); - bool handleLine(const QByteArray& line); - + bool parseStream(); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/logout.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/logout.cpp --- akonadi-1.1.1/server/src/handler/logout.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/logout.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -33,21 +33,21 @@ { } - -bool Logout::handleLine(const QByteArray& ) +bool Logout::parseStream() { - Response response; - response.setBye(); - response.setString( "Akonadi server logging out" ); - response.setUntagged(); - emit responseAvailable( response ); + Response response; + response.setBye(); + response.setString( "Akonadi server logging out" ); + response.setUntagged(); + emit responseAvailable( response ); - response.setSuccess(); - response.setTag( tag() ); - response.setString( "Logout completed" ); - emit responseAvailable( response ); - emit connectionStateChange( LoggingOut ); - deleteLater(); - return true; + response.setSuccess(); + response.setTag( tag() ); + response.setString( "Logout completed" ); + emit responseAvailable( response ); + emit connectionStateChange( LoggingOut ); + deleteLater(); + return true; } +#include "logout.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/logout.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/logout.h --- akonadi-1.1.1/server/src/handler/logout.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/logout.h 2009-07-28 17:52:13.000000000 +0100 @@ -30,12 +30,13 @@ */ class Logout : public Handler { -public: + Q_OBJECT + public: Logout(); ~Logout(); - bool handleLine(const QByteArray& line); + bool parseStream(); }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/modify.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/modify.cpp --- akonadi-1.1.1/server/src/handler/modify.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/modify.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -23,100 +23,165 @@ #include #include #include -#include "../../libs/imapparser_p.h" +#include "libs/imapparser_p.h" +#include "imapstreamparser.h" #include #include +#include +#include +#include +#include using namespace Akonadi; -Akonadi::Modify::Modify() +Modify::Modify( Scope::SelectionScope scope ) : m_scope( scope ) { } -Akonadi::Modify::~ Modify() +bool Modify::parseStream() { -} - -bool Akonadi::Modify::handleLine(const QByteArray & line) -{ - int pos = line.indexOf( ' ' ) + 1; // skip tag - pos = line.indexOf( ' ', pos ); // skip command - if ( pos < 0 ) - return failureResponse( "Invalid syntax" ); - - QByteArray collection; - pos = ImapParser::parseString( line, collection, pos ); - Location location = HandlerHelper::collectionFromIdOrName( collection ); - if ( !location.isValid() ) - return failureResponse( "No such collection" ); - if ( location.id() == 0 ) - return failureResponse( "Cannot modify root collection" ); + m_scope.parseScope( m_streamParser ); + SelectQueryBuilder qb; + CollectionQueryHelper::scopeToQuery( m_scope, connection(), qb ); + if ( !qb.exec() ) + throw HandlerException( "Unable to execute collection query" ); + const Collection::List collections = qb.result(); + if ( collections.isEmpty() ) + throw HandlerException( "No such collection" ); + if ( collections.size() > 1 ) // TODO + throw HandlerException( "Mass-modifying collections is not yet implemented" ); + Collection collection = collections.first(); + + + //TODO: do it cleanly with the streaming parser, which doesn't have look-ahead at this moment + QByteArray line = m_streamParser->readUntilCommandEnd(); + m_streamParser->insertData( "\n" ); + + int p = 0; + if ( (p = line.indexOf( "PARENT ")) > 0 ) { + QByteArray tmp; + ImapParser::parseString( line, tmp, p + 6 ); + const Collection newParent = HandlerHelper::collectionFromIdOrName( tmp ); + if ( newParent.isValid() && collection.parentId() != newParent.id() + && collection.resourceId() != newParent.resourceId() ) + { + ItemRetriever retriever( connection() ); + retriever.setCollection( collection, true ); + retriever.setRetrieveFullPayload( true ); + retriever.exec(); + } + } DataStore *db = connection()->storageBackend(); Transaction transaction( db ); + QList changes; + int pos = 0; while ( pos < line.length() ) { QByteArray type; pos = ImapParser::parseString( line, type, pos ); - if ( type == "MIMETYPE" ) { + if ( type == AKONADI_PARAM_MIMETYPE ) { QList mimeTypes; pos = ImapParser::parseParenthesizedList( line, mimeTypes, pos ); - if ( !db->removeMimeTypesForLocation( location.id() ) ) - return failureResponse( "Unable to modify collection mimetypes." ); QStringList mts; foreach ( const QByteArray &ba, mimeTypes ) mts << QString::fromLatin1(ba); - if ( !db->appendMimeTypeForLocation( location.id(), mts ) ) - return failureResponse( "Unable to modify collection mimetypes." ); - } else if ( type == "CACHEPOLICY" ) { - pos = HandlerHelper::parseCachePolicy( line, location, pos ); - db->notificationCollector()->collectionChanged( location ); - if ( !location.update() ) - return failureResponse( "Unable to change cache policy" ); - } else if ( type == "NAME" ) { + MimeType::List currentMts = collection.mimeTypes(); + bool equal = true; + foreach ( const MimeType ¤tMt, currentMts ) { + if ( mts.contains( currentMt.name() ) ) { + mts.removeAll( currentMt.name() ); + continue; + } + equal = false; + if ( !collection.removeMimeType( currentMt ) ) + throw HandlerException( "Unable to remove collection mimetype" ); + } + if ( !db->appendMimeTypeForCollection( collection.id(), mts ) ) + return failureResponse( "Unable to add collection mimetypes" ); + if ( !equal || !mts.isEmpty() ) + changes.append( AKONADI_PARAM_MIMETYPE ); + } else if ( type == AKONADI_PARAM_CACHEPOLICY ) { + bool changed = false; + pos = HandlerHelper::parseCachePolicy( line, collection, pos, &changed ); + if ( changed ) + changes.append( AKONADI_PARAM_CACHEPOLICY ); + } else if ( type == AKONADI_PARAM_NAME ) { QByteArray newName; pos = ImapParser::parseString( line, newName, pos ); - if ( !db->renameLocation( location, location.parentId(), newName ) ) + if ( collection.name() == newName ) + continue; + if ( !db->renameCollection( collection, collection.parentId(), newName ) ) return failureResponse( "Unable to rename collection" ); + changes.append( AKONADI_PARAM_NAME ); } else if ( type == "PARENT" ) { qint64 newParent; bool ok = false; pos = ImapParser::parseNumber( line, newParent, &ok, pos ); if ( !ok ) return failureResponse( "Invalid syntax" ); - if ( !db->renameLocation( location, newParent, location.name() ) ) - return failureResponse( "Unable to reparent colleciton" ); - } else if ( type == "REMOTEID" ) { - // FIXME: missing change notification + if ( collection.parentId() == newParent ) + continue; + if ( !db->renameCollection( collection, newParent, collection.name() ) ) + return failureResponse( "Unable to reparent collection" ); + changes.append( "PARENT" ); + } else if ( type == AKONADI_PARAM_REMOTEID ) { QString rid; pos = ImapParser::parseString( line, rid, pos ); - if ( rid == location.remoteId() ) + if ( rid == collection.remoteId() ) continue; - location.setRemoteId( rid ); - if ( !location.update() ) - return failureResponse( "Unable to change remote identifier" ); + collection.setRemoteId( rid ); + changes.append( AKONADI_PARAM_REMOTEID ); } else if ( type.isEmpty() ) { break; // input end } else { // custom attribute - bool removeOnly = false; if ( type.startsWith( '-' ) ) { type = type.mid( 1 ); - removeOnly = true; - } - if ( !db->removeCollectionAttribute( location, type ) ) - return failureResponse( "Unable to remove custom collection attribute" ); - if ( ! removeOnly ) { + if ( !db->removeCollectionAttribute( collection, type ) ) + return failureResponse( "Unable to remove custom collection attribute" ); + changes.append( type ); + } else { QByteArray value; pos = ImapParser::parseString( line, value, pos ); - if ( !db->addCollectionAttribute( location, type, value ) ) - return failureResponse( "Unable to add custom collection attribute" ); + + SelectQueryBuilder qb; + qb.addValueCondition( CollectionAttribute::collectionIdColumn(), Query::Equals, collection.id() ); + qb.addValueCondition( CollectionAttribute::typeColumn(), Query::Equals, type ); + if ( !qb.exec() ) + throw HandlerException( "Unable to retrieve collection attribute" ); + + const CollectionAttribute::List attrs = qb.result(); + if ( attrs.isEmpty() ) { + CollectionAttribute attr; + attr.setCollectionId( collection.id() ); + attr.setType( type ); + attr.setValue( value ); + if ( !attr.insert() ) + throw HandlerException( "Unable to add collection attribute" ); + changes.append( type ); + } else if ( attrs.size() == 1 ) { + CollectionAttribute attr = attrs.first(); + if ( attr.value() == value ) + continue; + attr.setValue( value ); + if ( !attr.update() ) + throw HandlerException( "Unable to update collection attribute" ); + changes.append( type ); + } else { + throw HandlerException( "WTF: more than one attribute with the same name" ); + } } } } - if ( !transaction.commit() ) - return failureResponse( "Unable to commit transaction." ); + if ( !changes.isEmpty() ) { + if ( collection.hasPendingChanges() && !collection.update() ) + return failureResponse( "Unable to update collection" ); + db->notificationCollector()->collectionChanged( collection, changes ); + if ( !transaction.commit() ) + return failureResponse( "Unable to commit transaction" ); + } Response response; response.setTag( tag() ); @@ -124,3 +189,5 @@ emit responseAvailable( response ); return true; } + +#include "modify.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/modify.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/modify.h --- akonadi-1.1.1/server/src/handler/modify.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/modify.h 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -21,6 +21,7 @@ #define AKONADI_MODIFY_H #include +#include namespace Akonadi { @@ -36,19 +37,24 @@ Request: @verbatim - request = tag " MODIFY " collection-id " " attribute-list + request = tag " [ "RID " ] MODIFY " collection-ids " " attribute-list attribute-list = *([-]attribute-name [" " attribute-value]) attribute-name = "NAME" | "MIMETYPE" | "REMOTEID" | "CACHEPOLICY" | "PARENT" | [-]custom-attr-name @endverbatim + @c collection-ids is either a UID set or a RID list, depending on the command prefix. + Attributes marked with a leading '-' will be deleted, they don't have any attribute value. */ class Modify : public Handler { + Q_OBJECT public: - Modify(); - ~Modify(); - bool handleLine( const QByteArray &line ); + Modify( Scope::SelectionScope scope ); + bool parseStream(); + + private: + Scope m_scope; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/move.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/move.cpp --- akonadi-1.1.1/server/src/handler/move.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/move.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,102 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "move.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Akonadi { + +Move::Move( Scope::SelectionScope scope ) : + mScope( scope ) +{ +} + +bool Move::parseStream() +{ + mScope.parseScope( m_streamParser ); + const Collection destination = HandlerHelper::collectionFromIdOrName( m_streamParser->readString() ); + if ( !destination.isValid() ) + throw HandlerException( "Unknown destination collection" ); + const Resource destResource = destination.resource(); + + // make sure all the items we want to move are in the cache + ItemRetriever retriever( connection() ); + retriever.setScope( mScope ); + retriever.setRetrieveFullPayload( true ); + retriever.exec(); + + DataStore *store = connection()->storageBackend(); + Transaction transaction( store ); + + SelectQueryBuilder qb; + ItemQueryHelper::scopeToQuery( mScope, connection(), qb ); + qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::NotEquals, destination.id() ); + + const QDateTime mtime = QDateTime::currentDateTime(); + + if ( qb.exec() ) { + const QList items = qb.result(); + if ( items.isEmpty() ) + throw HandlerException( "No items found" ); + foreach ( PimItem item, items ) { + if ( !item.isValid() ) + throw HandlerException( "Invalid item in result set!?" ); + Q_ASSERT( item.collectionId() != destination.id() ); + const Collection source = item.collection(); + if ( !source.isValid() ) + throw HandlerException( "Item without collection found!?" ); + + store->notificationCollector()->collectionChanged( source, QList() ); // ### why? + + item.setCollectionId( destination.id() ); + item.setAtime( mtime ); + item.setDatetime( mtime ); + // if the resource moved itself, we assume it did so because the change happend in the backend + if ( connection()->resourceContext().id() != destResource.id() ) + item.setDirty( true ); + + if ( !item.update() ) + throw HandlerException( "Unable to update item" ); + + store->notificationCollector()->itemMoved( item, source, destination ); + } + store->notificationCollector()->collectionChanged( destination, QList(), destResource.name().toUtf8() ); // ### why? + } else { + throw HandlerException( "Unable to execute query" ); + } + + if ( !transaction.commit() ) + return failureResponse( "Unable to commit transaction." ); + + return successResponse( "MOVE complete" ); +} + +} + +#include "move.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/move.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/move.h --- akonadi-1.1.1/server/src/handler/move.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/move.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,62 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_MOVE_H +#define AKONADI_MOVE_H + +#include "handler.h" +#include "scope.h" + +namespace Akonadi { + +/** + @ingroup akonadi_server_handler + + Handler for the item move command. + +

Syntax

+ One of the following three: + @verbatim + MOVE + UID MOVE + RID MOVE + @endverbatim + +

Semantics

+ Moves the selected items. Item selection can happen within the usual three scopes: + - based on a uid set relative to the currently selected collection + - based on a global uid set (UID) + - based on a list of remote identifiers within the currently selected collection (RID) + + Destination is a collection id. +*/ +class Move : public Handler +{ + Q_OBJECT + public: + Move( Scope::SelectionScope scope ); + bool parseStream(); + + private: + Scope mScope; +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/noop.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/noop.cpp --- akonadi-1.1.1/server/src/handler/noop.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/noop.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -34,7 +34,7 @@ { } -bool Noop::handleLine( const QByteArray& ) +bool Noop::parseStream() { Response response; response.setTag( tag() ); @@ -49,3 +49,6 @@ return true; } + + +#include "noop.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/noop.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/noop.h --- akonadi-1.1.1/server/src/handler/noop.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/noop.h 2009-07-28 17:52:13.000000000 +0100 @@ -31,12 +31,13 @@ */ class Noop : public Handler { + Q_OBJECT public: Noop(); ~Noop(); - bool handleLine(const QByteArray& line); + bool parseStream(); }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/remove.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/remove.cpp --- akonadi-1.1.1/server/src/handler/remove.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/remove.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,68 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "remove.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Akonadi { + +Remove::Remove( Scope::SelectionScope scope ) : + mScope( scope ) +{ +} + +bool Remove::parseStream() +{ + mScope.parseScope( m_streamParser ); + SelectQueryBuilder qb; + ItemQueryHelper::scopeToQuery( mScope, connection(), qb ); + + DataStore *store = connection()->storageBackend(); + Transaction transaction( store ); + + if ( qb.exec() ) { + const QList items = qb.result(); + if ( items.isEmpty() ) + throw HandlerException( "No items found" ); + foreach ( const PimItem &item, items ) { + if ( !store->cleanupPimItem( item ) ) + throw HandlerException( "Deletion failed" ); + } + } else { + throw HandlerException( "Unable to execute query" ); + } + + if ( !transaction.commit() ) + return failureResponse( "Unable to commit transaction." ); + + deleteLater(); + return successResponse( "REMOVE complete" ); +} + +} + +#include "remove.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/remove.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/remove.h --- akonadi-1.1.1/server/src/handler/remove.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/remove.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,60 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_REMOVE_H +#define AKONADI_REMOVE_H + +#include "handler.h" +#include "scope.h" + +namespace Akonadi { + +/** + @ingroup akonadi_server_handler + + Handler for the item deletion command. + +

Syntax

+ One of the following three: + @verbatim + REMOVE + UID REMOVE + RID REMOVE + @endverbatim + +

Semantics

+ Removes the selected items. Item selection can happen within the usual three scopes: + - based on a uid set relative to the currently selected collection + - based on a global uid set (UID) + - based on a remote identifier within the currently selected collection (RID) +*/ +class Remove : public Handler +{ + Q_OBJECT + public: + Remove( Scope::SelectionScope scope ); + bool parseStream(); + + private: + Scope mScope; +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/rename.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/rename.cpp --- akonadi-1.1.1/server/src/handler/rename.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/rename.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -20,7 +20,7 @@ #include "rename.h" #include #include -#include "../../libs/imapparser_p.h" +#include "imapstreamparser.h" #include #include #include @@ -36,17 +36,10 @@ { } -bool Akonadi::Rename::handleLine(const QByteArray & line) +bool Akonadi::Rename::parseStream() { - int pos = line.indexOf( ' ' ) + 1; // skip tag - pos = line.indexOf( ' ', pos ); // skip command - QByteArray oldName; - QByteArray newName; - if ( pos < 0 ) - return failureResponse( "Bad syntax" ); - - pos = ImapParser::parseString( line, oldName, pos ); - ImapParser::parseString( line, newName, pos ); + QByteArray oldName = m_streamParser->readString(); + QByteArray newName = m_streamParser->readString(); if ( oldName.isEmpty() || newName.isEmpty() ) return failureResponse( "Collection name must not be empty" ); @@ -54,24 +47,24 @@ DataStore *db = connection()->storageBackend(); Transaction transaction( db ); - Location location = HandlerHelper::collectionFromIdOrName( newName ); - if ( location.isValid() ) + Collection collection = HandlerHelper::collectionFromIdOrName( newName ); + if ( collection.isValid() ) return failureResponse( "Collection already exists" ); - location = HandlerHelper::collectionFromIdOrName( oldName ); - if ( !location.isValid() ) + collection = HandlerHelper::collectionFromIdOrName( oldName ); + if ( !collection.isValid() ) return failureResponse( "No such collection" ); QByteArray parentPath; int index = newName.lastIndexOf( '/' ); if ( index > 0 ) parentPath = newName.mid( index + 1 ); - Location parent = HandlerHelper::collectionFromIdOrName( parentPath ); + Collection parent = HandlerHelper::collectionFromIdOrName( parentPath ); newName = newName.left( index ); qint64 parentId = 0; if ( parent.isValid() ) parentId = parent.id(); - if ( !db->renameLocation( location, parentId, newName ) ) + if ( !db->renameCollection( collection, parentId, newName ) ) return failureResponse( "Failed to rename collection." ); if ( !transaction.commit() ) @@ -84,3 +77,5 @@ deleteLater(); return true; } + +#include "rename.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/rename.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/rename.h --- akonadi-1.1.1/server/src/handler/rename.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/rename.h 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -31,10 +31,11 @@ */ class Rename : public Handler { + Q_OBJECT public: Rename(); ~Rename(); - virtual bool handleLine( const QByteArray &line ); + bool parseStream(); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/resourceselect.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/resourceselect.cpp --- akonadi-1.1.1/server/src/handler/resourceselect.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/resourceselect.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,52 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "resourceselect.h" + +#include +#include +#include + +using namespace Akonadi; + +ResourceSelect::ResourceSelect() : + Handler() +{ +} + +bool ResourceSelect::parseStream() +{ + const QString resourceName = m_streamParser->readUtf8String(); + if ( resourceName.isEmpty() ) { + connection()->setResourceContext( Resource() ); + deleteLater(); + return successResponse( "Resource deselected" ); + } + + const Resource res = Resource::retrieveByName( resourceName ); + if ( !res.isValid() ) + throw HandlerException( resourceName.toUtf8() + " is not a valid resource identifier" ); + + connection()->setResourceContext( res ); + + deleteLater(); + return successResponse( resourceName.toUtf8() + " selected" ); +} + +#include "resourceselect.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/resourceselect.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/resourceselect.h --- akonadi-1.1.1/server/src/handler/resourceselect.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/resourceselect.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,52 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_RESOURCESELECT_H +#define AKONADI_RESOURCESELECT_H + +#include + +namespace Akonadi { + +/** + @ingroup akonadi_server_handler + + Handler for the resource selection command. + +

Syntax

+ @verbatim + RESSELECT + @endverbatim + +

Semantics

+ Limits the scope of remote id based operations. Remote ids of collections are only guaranteed + to be unique per resource, so this command should be issued before running any RID based + collection commands. +*/ +class ResourceSelect : public Handler +{ + Q_OBJECT + public: + ResourceSelect(); + bool parseStream(); +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/scope.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/scope.cpp --- akonadi-1.1.1/server/src/handler/scope.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/scope.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,77 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "scope.h" +#include "imapstreamparser.h" +#include "handler.h" + +#include + +using namespace Akonadi; + +Scope::Scope( SelectionScope scope ) : + mScope( scope ) +{ +} + +void Scope::parseScope( ImapStreamParser* parser ) +{ + if ( mScope == None || mScope == Uid ) { + mUidSet = parser->readSequenceSet(); + if ( mUidSet.isEmpty() ) + throw HandlerException( "Empty uid set specified" ); + } else if ( mScope == Rid ) { + if ( parser->hasList() ) { + parser->beginList(); + while ( !parser->atListEnd() ) + mRidSet << parser->readUtf8String(); + } else { + mRidSet << parser->readUtf8String(); + } + if ( mRidSet.isEmpty() ) + throw HandlerException( "Empty remote identifier set specified" ); + } else { + throw HandlerException( "WTF?!?" ); + } +} + +Scope::SelectionScope Scope::scope() const +{ + return mScope; +} + +void Scope::setScope( SelectionScope scope ) +{ + mScope = scope; +} + +ImapSet Scope::uidSet() const +{ + return mUidSet; +} + +void Scope::setUidSet(const ImapSet& set) +{ + mUidSet = set; +} + +QStringList Scope::ridSet() const +{ + return mRidSet; +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/scope.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/scope.h --- akonadi-1.1.1/server/src/handler/scope.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/handler/scope.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,65 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_SCOPE_H +#define AKONADI_SCOPE_H + +#include "libs/imapset_p.h" +#include + +namespace Akonadi { + +class ImapStreamParser; + +/** + Represents a set of Akonadi objects (eg. items or collections) selected for an operations. +*/ +class Scope +{ + public: + enum SelectionScope + { + Invalid, + None, + Uid, + Rid + }; + + Scope( SelectionScope scope ); + /** + Parse the object set dependent on the set selection scope. + The set has to be non-empty. If not a HandlerException is thrown. + */ + void parseScope( ImapStreamParser *parser ); + + SelectionScope scope() const; + void setScope( SelectionScope scope ); + ImapSet uidSet() const; + void setUidSet( const ImapSet &set ); + QStringList ridSet() const; + + private: + SelectionScope mScope; + ImapSet mUidSet; + QStringList mRidSet; +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/searchpersistent.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/searchpersistent.cpp --- akonadi-1.1.1/server/src/handler/searchpersistent.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/searchpersistent.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -21,13 +21,13 @@ #include "akonadi.h" #include "akonadiconnection.h" -#include "../../libs/imapparser_p.h" #include "response.h" #include "storage/datastore.h" #include "storage/entity.h" #include "storage/transaction.h" #include "handlerhelper.h" #include "abstractsearchmanager.h" +#include "imapstreamparser.h" #include @@ -42,33 +42,29 @@ { } -bool SearchPersistent::handleLine( const QByteArray& line ) -{ - int pos = line.indexOf( ' ' ) + 1; // skip tag - pos = line.indexOf( ' ', pos ) + 1; // skip command - QByteArray collectionName; - pos = ImapParser::parseString( line, collectionName, pos ); +bool SearchPersistent::parseStream() +{ + QByteArray collectionName = m_streamParser->readString(); if ( collectionName.isEmpty() ) return failureResponse( "No name specified" ); DataStore *db = connection()->storageBackend(); Transaction transaction( db ); - QByteArray queryString; - ImapParser::parseString( line, queryString, pos ); + QByteArray queryString = m_streamParser->readString(); if ( queryString.isEmpty() ) return failureResponse( "No query specified" ); - Location l; - l.setRemoteId( QString::fromUtf8( queryString ) ); - l.setParentId( 1 ); // search root - l.setResourceId( 1 ); // search resource - l.setName( collectionName ); - if ( !db->appendLocation( l ) ) + Collection col; + col.setRemoteId( QString::fromUtf8( queryString ) ); + col.setParentId( 1 ); // search root + col.setResourceId( 1 ); // search resource + col.setName( collectionName ); + if ( !db->appendCollection( col ) ) return failureResponse( "Unable to create persistent search" ); - if ( !AbstractSearchManager::instance()->addSearch( l ) ) + if ( !AbstractSearchManager::instance()->addSearch( col ) ) return failureResponse( "Unable to add search to search manager" ); if ( !transaction.commit() ) @@ -83,3 +79,5 @@ deleteLater(); return true; } + +#include "searchpersistent.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/searchpersistent.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/searchpersistent.h --- akonadi-1.1.1/server/src/handler/searchpersistent.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/searchpersistent.h 2009-07-28 17:52:13.000000000 +0100 @@ -36,12 +36,13 @@ */ class SearchPersistent : public Handler { + Q_OBJECT public: SearchPersistent(); ~SearchPersistent(); - bool handleLine( const QByteArray& line ); + bool parseStream(); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/select.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/select.cpp --- akonadi-1.1.1/server/src/handler/select.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/select.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -25,13 +25,16 @@ #include "storage/datastore.h" #include "storage/entity.h" #include "handlerhelper.h" -#include "../../libs/imapparser_p.h" +#include "imapstreamparser.h" +#include "storage/selectquerybuilder.h" #include "response.h" using namespace Akonadi; -Select::Select(): Handler() +Select::Select( Scope::SelectionScope scope ) : + Handler(), + mScope( scope ) { } @@ -41,70 +44,89 @@ } -bool Select::handleLine(const QByteArray& line ) +bool Select::parseStream() { - // as per rfc, even if the following select fails, we need to reset - connection()->setSelectedCollection( 0 ); + // as per rfc, even if the following select fails, we need to reset + connection()->setSelectedCollection( 0 ); - int pos = line.indexOf( ' ' ) + 1; // skip tag - QByteArray buffer; + QByteArray buffer = m_streamParser->readString(); - // command - pos = ImapParser::parseString( line, buffer, pos ); - pos = ImapParser::parseString( line, buffer, pos ); - bool silent = false; - if ( buffer == "SILENT" ) { - silent = true; - pos = ImapParser::parseString( line, buffer, pos ); - } - - // collection - Location l = HandlerHelper::collectionFromIdOrName( buffer ); - if ( !l.isValid() ) { + bool silent = false; + if ( buffer == "SILENT" ) { + silent = true; + buffer = m_streamParser->readString(); + } + + // collection + Collection col; + + if ( mScope == Scope::None || mScope == Scope::Uid ) { + col = HandlerHelper::collectionFromIdOrName( buffer ); + if ( !col.isValid() ) { bool ok = false; if ( buffer.toLongLong( &ok ) == 0 && ok ) silent = true; else return failureResponse( "Cannot select this collection" ); } + } else if ( mScope == Scope::Rid ) { + if ( buffer.isEmpty() ) { + silent = true; // unselect + } else { + if ( !connection()->resourceContext().isValid() ) + throw HandlerException( "Cannot select based on remote identifier without a resource scope" ); + SelectQueryBuilder qb; + qb.addValueCondition( Collection::remoteIdColumn(), Query::Equals, buffer ); + qb.addValueCondition( Collection::resourceIdColumn(), Query::Equals, connection()->resourceContext().id() ); + if ( !qb.exec() ) + throw HandlerException( "Failed to select collection" ); + Collection::List results = qb.result(); + if ( results.count() != 1 ) + throw HandlerException( QByteArray::number( results.count() ) + " collections found" ); + col = results.first(); + } + } // Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT // OPTIONAL OK untagged responses: UNSEEN, PERMANENTFLAGS - Response response; - if ( !silent ) { - response.setUntagged(); - response.setString( Flag::joinByName( Flag::retrieveAll(), QLatin1String(" ") ) ); - emit responseAvailable( response ); - - int count = HandlerHelper::itemCount( l ); - if ( count < 0 ) - return failureResponse( "Unable to determine item count" ); - response.setString( QByteArray::number( count ) + " EXISTS" ); - emit responseAvailable( response ); - - count = HandlerHelper::itemWithFlagCount( l, QLatin1String( "\\Recent" ) ); - if ( count < 0 ) - return failureResponse( "Unable to determine recent count" ); - response.setString( QByteArray::number( count ) + " RECENT" ); - emit responseAvailable( response ); - - count = HandlerHelper::itemWithoutFlagCount( l, QLatin1String( "\\Seen" ) ); - if ( count < 0 ) - return failureResponse( "Unable to retrieve unseen count" ); - response.setString( "OK [UNSEEN " + QByteArray::number( count ) + "] Message 0 is first unseen" ); - emit responseAvailable( response ); + Response response; + if ( !silent ) { + response.setUntagged(); + response.setString( Flag::joinByName( Flag::retrieveAll(), QLatin1String(" ") ) ); + emit responseAvailable( response ); - response.setString( "OK [UIDVALIDITY 1] UIDs valid" ); - emit responseAvailable( response ); - } + int count = HandlerHelper::itemCount( col ); + if ( count < 0 ) + return failureResponse( "Unable to determine item count" ); + response.setString( QByteArray::number( count ) + " EXISTS" ); + emit responseAvailable( response ); - response.setSuccess(); - response.setTag( tag() ); - response.setString( "Completed" ); + count = HandlerHelper::itemWithFlagCount( col, QLatin1String( "\\Recent" ) ); + if ( count < 0 ) + return failureResponse( "Unable to determine recent count" ); + response.setString( QByteArray::number( count ) + " RECENT" ); emit responseAvailable( response ); - connection()->setSelectedCollection( l.id() ); - deleteLater(); - return true; + count = HandlerHelper::itemWithoutFlagCount( col, QLatin1String( "\\Seen" ) ); + if ( count < 0 ) + return failureResponse( "Unable to retrieve unseen count" ); + response.setString( "OK [UNSEEN " + QByteArray::number( count ) + "] Message 0 is first unseen" ); + emit responseAvailable( response ); + + response.setString( "OK [UIDVALIDITY 1] UIDs valid" ); + emit responseAvailable( response ); + } + + response.setSuccess(); + response.setTag( tag() ); + response.setString( "Completed" ); + emit responseAvailable( response ); + + connection()->setSelectedCollection( col.id() ); + deleteLater(); + return true; } + + +#include "select.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/select.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/select.h --- akonadi-1.1.1/server/src/handler/select.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/select.h 2009-07-28 17:52:13.000000000 +0100 @@ -20,6 +20,7 @@ #define AKONADISELECT_H #include +#include "scope.h" namespace Akonadi { @@ -30,12 +31,16 @@ */ class Select : public Handler { -public: - Select(); + Q_OBJECT + public: + Select( Scope::SelectionScope scope ); ~Select(); - bool handleLine(const QByteArray& line); + bool parseStream(); + + private: + Scope::SelectionScope mScope; }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/status.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/status.cpp --- akonadi-1.1.1/server/src/handler/status.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/status.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -27,8 +27,8 @@ #include "storage/countquerybuilder.h" #include "response.h" -#include "../../libs/imapparser_p.h" #include "handlerhelper.h" +#include "imapstreamparser.h" using namespace Akonadi; @@ -41,90 +41,100 @@ { } - -bool Status::handleLine( const QByteArray& line ) +bool Status::parseStream() { // Arguments: mailbox name // status data item names // Syntax: // status = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")" - // status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / "UNSEEN" - const int startOfCommand = line.indexOf( ' ' ) + 1; - const int startOfMailbox = line.indexOf( ' ', startOfCommand ) + 1; - QByteArray mailbox; - const int endOfMailbox = ImapParser::parseString( line, mailbox, startOfMailbox ); - QList attributeList; - ImapParser::parseParenthesizedList( line, attributeList, endOfMailbox ); + // status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / "UNSEEN" / "SIZE" + + QByteArray mailbox = m_streamParser->readString(); + QList attributeList = m_streamParser->readParenthesizedList(); - Response response; + Response response; - DataStore *db = connection()->storageBackend(); - Location l = HandlerHelper::collectionFromIdOrName( mailbox ); + DataStore *db = connection()->storageBackend(); + Collection col = HandlerHelper::collectionFromIdOrName( mailbox ); - if ( !l.isValid() ) - return failureResponse( "No status for this folder" ); + if ( !col.isValid() ) + return failureResponse( "No status for this folder" ); // Responses: // REQUIRED untagged responses: STATUS // build STATUS response - QByteArray statusResponse; + QByteArray statusResponse; // MESSAGES - The number of messages in the mailbox - if ( attributeList.contains( "MESSAGES" ) ) { - statusResponse += "MESSAGES "; - const int count = HandlerHelper::itemCount( l ); - if ( count < 0 ) - return failureResponse( "Could not determine message count." ); - statusResponse += QByteArray::number( count ); - } + if ( attributeList.contains( "MESSAGES" ) ) { + statusResponse += "MESSAGES "; + const int count = HandlerHelper::itemCount( col ); + if ( count < 0 ) + return failureResponse( "Could not determine message count." ); + statusResponse += QByteArray::number( count ); + } // RECENT - The number of messages with the \Recent flag set - if ( attributeList.contains( "RECENT" ) ) { - if ( !statusResponse.isEmpty() ) - statusResponse += " RECENT "; - else - statusResponse += "RECENT "; - const int count = HandlerHelper::itemWithFlagCount( l, QLatin1String( "\\Recent" ) ); - if ( count < 0 ) - return failureResponse( "Could not determine recent item count" ); - statusResponse += QByteArray::number( count ); - } + if ( attributeList.contains( "RECENT" ) ) { + if ( !statusResponse.isEmpty() ) + statusResponse += " RECENT "; + else + statusResponse += "RECENT "; + const int count = HandlerHelper::itemWithFlagCount( col, QLatin1String( "\\Recent" ) ); + if ( count < 0 ) + return failureResponse( "Could not determine recent item count" ); + statusResponse += QByteArray::number( count ); + } // UIDNEXT - The next unique identifier value of the mailbox - if ( attributeList.contains( "UIDNEXT" ) ) { - if ( !statusResponse.isEmpty() ) - statusResponse += " UIDNEXT "; - else - statusResponse += "UIDNEXT "; - statusResponse += QByteArray::number( db->uidNext() ); - } + if ( attributeList.contains( "UIDNEXT" ) ) { + if ( !statusResponse.isEmpty() ) + statusResponse += " UIDNEXT "; + else + statusResponse += "UIDNEXT "; + statusResponse += QByteArray::number( db->uidNext() ); + } // UIDVALIDITY - The unique identifier validity value of the mailbox - if ( attributeList.contains( "UIDVALIDITY" ) ) { - if ( !statusResponse.isEmpty() ) - statusResponse += " UIDVALIDITY 1"; - else - statusResponse += "UIDVALIDITY 1"; - } - if ( attributeList.contains( "UNSEEN" ) ) { - if ( !statusResponse.isEmpty() ) - statusResponse += " UNSEEN "; - else - statusResponse += "UNSEEN "; - - const int count = HandlerHelper::itemWithoutFlagCount( l, QLatin1String( "\\Seen" ) ); - if ( count < 0 ) - return failureResponse( "Unable to retrieve unread count" ); - statusResponse += QByteArray::number( count ); - } - - response.setUntagged(); - response.setString( "STATUS \"" + HandlerHelper::pathForCollection( l ).toUtf8() + "\" (" + statusResponse + ')' ); - emit responseAvailable( response ); - - response.setSuccess(); - response.setTag( tag() ); - response.setString( "STATUS completed" ); - emit responseAvailable( response ); + if ( attributeList.contains( "UIDVALIDITY" ) ) { + if ( !statusResponse.isEmpty() ) + statusResponse += " UIDVALIDITY 1"; + else + statusResponse += "UIDVALIDITY 1"; + } + if ( attributeList.contains( "UNSEEN" ) ) { + if ( !statusResponse.isEmpty() ) + statusResponse += " UNSEEN "; + else + statusResponse += "UNSEEN "; + + const int count = HandlerHelper::itemWithoutFlagCount( col, QLatin1String( "\\Seen" ) ); + if ( count < 0 ) + return failureResponse( "Unable to retrieve unread count" ); + statusResponse += QByteArray::number( count ); + } + if ( attributeList.contains( "SIZE" ) ) { + if ( !statusResponse.isEmpty() ) + statusResponse += " SIZE "; + else + statusResponse += "SIZE "; + + const qint64 size = HandlerHelper::itemsTotalSize( col ); + if ( size < 0 ) + return failureResponse( "Unable to retrieve collection total size" ); + statusResponse += QByteArray::number( size ); + } + + response.setUntagged(); + response.setString( "STATUS \"" + HandlerHelper::pathForCollection( col ).toUtf8() + "\" (" + statusResponse + ')' ); + emit responseAvailable( response ); + + response.setSuccess(); + response.setTag( tag() ); + response.setString( "STATUS completed" ); + emit responseAvailable( response ); + + deleteLater(); + return true; - deleteLater(); - return true; } + +#include "status.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/status.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/status.h --- akonadi-1.1.1/server/src/handler/status.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/status.h 2009-07-28 17:52:13.000000000 +0100 @@ -30,12 +30,13 @@ */ class Status : public Handler { + Q_OBJECT public: Status(); ~Status(); - bool handleLine(const QByteArray& line); + bool parseStream(); }; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/store.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/store.cpp --- akonadi-1.1.1/server/src/handler/store.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/store.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -19,223 +19,38 @@ #include "store.h" -#include -#include - #include "akonadi.h" #include "akonadiconnection.h" +#include "handlerhelper.h" #include "response.h" #include "storage/datastore.h" #include "storage/transaction.h" -#include "handlerhelper.h" -#include "../../libs/imapparser_p.h" +#include "storage/itemqueryhelper.h" #include "storage/selectquerybuilder.h" +#include "storage/parthelper.h" +#include "storage/dbconfig.h" +#include "storage/itemretriever.h" + +#include "libs/imapparser_p.h" +#include "libs/protocol_p.h" +#include "imapstreamparser.h" + +#include +#include +#include +#include using namespace Akonadi; -Store::Store() +Store::Store( Scope::SelectionScope scope ) : Handler() + , mScope( scope ) + , mPos( 0 ) + , mPreviousRevision( -1 ) + , mSize( 0 ) { } -Store::~Store() -{ -} - -bool Store::handleLine( const QByteArray& line ) -{ - int pos = line.indexOf( ' ' ) + 1; // skip tag - QByteArray buffer; - pos = ImapParser::parseString( line, buffer, pos ); - bool uidStore = false; - if ( buffer == "UID" ) { - uidStore = true; - pos = ImapParser::parseString( line, buffer, pos ); // skip 'STORE' - } - - Response response; - response.setUntagged(); - - ImapSet set; - pos = ImapParser::parseSequenceSet( line, set, pos ); - if ( set.isEmpty() ) - return failureResponse( "No item specified" ); - - DataStore *store = connection()->storageBackend(); - Transaction transaction( store ); - - SelectQueryBuilder qb; - imapSetToQuery( set, uidStore, qb ); - if ( !qb.exec() ) - return failureResponse( "Unable to retrieve items" ); - QList pimItems = qb.result(); - if ( pimItems.isEmpty() ) - return failureResponse( "No items found" ); - - // parse revision - qint64 rev = 0; - bool revCheck = false; - bool ok; - pos = ImapParser::parseString( line, buffer, pos ); // skip 'REV' - if ( buffer == "REV" ) { - revCheck = true; - pos = ImapParser::parseNumber( line, rev, &ok, pos ); - if ( !ok ) { - return failureResponse( "Unable to parse item revision number." ); - } - } - - // Set the same modification time for each item. - QDateTime modificationtime = QDateTime::currentDateTime().toUTC(); - - for ( int i = 0; i < pimItems.count(); ++i ) { - if ( revCheck ) { - // check if revision number of given items and their database equivalents match - if ( pimItems.at( i ).rev() != (int)rev ) { - return failureResponse( "Item was modified elsewhere, aborting STORE." ); - } - } - - // update item revision - pimItems[ i ].setRev( pimItems[ i ].rev() + 1 ); - pimItems[ i ].setDatetime( modificationtime ); - if ( !pimItems[ i ].update() ) { - return failureResponse( "Unable to update item revision" ); - } - } - - // parse size - qint64 size = 0; - pos = ImapParser::parseString( line, buffer, pos ); - if ( buffer == "SIZE" ) { - pos = ImapParser::parseNumber( line, size, &ok, pos ); - if ( !ok ) { - return failureResponse( "Unable to parse the size." + line); - } - } - - QList changes; - pos = ImapParser::parseParenthesizedList( line, changes, pos ); - for ( int i = 0; i < changes.size() - 1; i += 2 ) { - // parse command - QByteArray command = changes[ i ]; - Operation op = Replace; - bool silent = false; - if ( command.isEmpty() ) - break; - - if ( command.startsWith( '+' ) ) { - op = Add; - command = command.mid( 1 ); - } else if ( command.startsWith( '-' ) ) { - op = Delete; - command = command.mid( 1 ); - } - if ( command.endsWith( ".SILENT" ) ) { - silent = true; - command.chop( 7 ); - } - const QByteArray value = changes[i + 1]; - - for ( int i = 0; i < pimItems.count(); ++i ) { - // handle command - if ( command == "FLAGS" ) { - QList flags; - ImapParser::parseParenthesizedList( value, flags ); - if ( op == Replace ) { - if ( !replaceFlags( pimItems[ i ], flags ) ) - return failureResponse( "Unable to replace item flags." ); - } else if ( op == Add ) { - if ( !addFlags( pimItems[ i ], flags ) ) - return failureResponse( "Unable to add item flags." ); - } else if ( op == Delete ) { - if ( !deleteFlags( pimItems[ i ], flags ) ) - return failureResponse( "Unable to remove item flags." ); - } - } else if ( command == "PARTS" ) { - QList parts; - ImapParser::parseParenthesizedList( value, parts ); - if ( op == Delete ) { - if ( !deleteParts( pimItems[ i ], parts ) ) - return failureResponse( "Unable to remove item parts." ); - } - } else if ( command == "COLLECTION" ) { - if ( !store->updatePimItem( pimItems[ i ], HandlerHelper::collectionFromIdOrName( value ) ) ) - return failureResponse( "Unable to move item." ); - } else if ( command == "REMOTEID" ) { - if ( !store->updatePimItem( pimItems[i], QString::fromUtf8( value ) ) ) - return failureResponse( "Unable to change remote id for item." ); - } else if ( command == "DIRTY" ) { - PimItem item = pimItems.at( i ); - item.setDirty( false ); - if ( !item.update() ) - return failureResponse( "Unable to update item dirtyness" ); - } else { - Part part; - int version = 0; - QByteArray plainCommand; - - ImapParser::splitVersionedKey( command, plainCommand, version ); - - SelectQueryBuilder qb; - qb.addValueCondition( Part::pimItemIdColumn(), Query::Equals, pimItems[ i ].id() ); - qb.addValueCondition( Part::nameColumn(), Query::Equals, QString::fromUtf8( plainCommand ) ); - if ( !qb.exec() ) - return failureResponse( "Unable to check item part existence" ); - Part::List result = qb.result(); - if ( !result.isEmpty() ) { - part = result.first(); - } - - // only update if part contents are not yet in the storage - if ( part.data() != value ) - { - part.setData( value ); - part.setDatasize( buffer.size() ); - part.setName( QString::fromUtf8( plainCommand ) ); - part.setVersion( version ); - part.setPimItemId( pimItems[ i ].id() ); - if ( part.isValid() ) { - if ( !part.update() ) - return failureResponse( "Unable to update item part" ); - } else { - if ( !part.insert() ) - return failureResponse( "Unable to add item part" ); - } - store->updatePimItem( pimItems[ i ] ); - } - } - - if ( !silent ) { - QList flags = pimItems[ i ].flags(); - QStringList flagList; - for ( int j = 0; j < flags.count(); ++j ) - flagList.append( flags[ j ].name() ); - - response.setUntagged(); - // IMAP protocol violation: should actually be the sequence number - response.setString( QByteArray::number( pimItems[i].id() ) + " FETCH (FLAGS (" + flagList.join( QLatin1String(" ") ).toUtf8() + "))" ); - emit responseAvailable( response ); - } - } - } - - if ( !transaction.commit() ) - return failureResponse( "Cannot commit transaction." ); - - QString datetime = QLocale::c().toString( modificationtime, QLatin1String( "dd-MMM-yyyy hh:mm:ss +0000" ) ); - - response.setTag( tag() ); - response.setSuccess(); - response.setString( "DATETIME " + ImapParser::quote( datetime.toUtf8() ) + " STORE completed" ); - - emit responseAvailable( response ); - deleteLater(); - - return true; -} - - bool Store::replaceFlags( const PimItem &item, const QList &flags ) { DataStore *store = connection()->storageBackend(); @@ -318,23 +133,272 @@ return true; } -bool Store::deleteParts( const PimItem &item, const QList &parts ) +bool Store::parseStream() { + parseCommand(); DataStore *store = connection()->storageBackend(); + Transaction transaction( store ); + // Set the same modification time for each item. + const QDateTime modificationtime = QDateTime::currentDateTime().toUTC(); + + // retrieve selected items + SelectQueryBuilder qb; + ItemQueryHelper::scopeToQuery( mScope, connection(), qb ); + if ( !qb.exec() ) + return failureResponse( "Unable to retrieve items" ); + PimItem::List pimItems = qb.result(); + if ( pimItems.isEmpty() ) + return failureResponse( "No items found" ); + + // check and update revisions + for ( int i = 0; i < pimItems.size(); ++i ) { + if ( mPreviousRevision >= 0 && pimItems.at( i ).rev() != (int)mPreviousRevision ) + throw HandlerException( "Item was modified elsewhere, aborting STORE." ); + pimItems[ i ].setRev( pimItems[ i ].rev() + 1 ); + } + + QSet changes; + qint64 partSizes = 0; + + // apply modifications + m_streamParser->beginList(); + while ( !m_streamParser->atListEnd() ) { + // parse the command + QByteArray command = m_streamParser->readString(); + if ( command.isEmpty() ) + throw HandlerException( "Syntax error" ); + Operation op = Replace; + bool silent = false; + if ( command.startsWith( '+' ) ) { + op = Add; + command = command.mid( 1 ); + } else if ( command.startsWith( '-' ) ) { + op = Delete; + command = command.mid( 1 ); + } + if ( command.endsWith( ".SILENT" ) ) { + command.chop( 7 ); + silent = true; + } + qDebug() << "STORE: handling command: " << command; + - QList partList; - for ( int i = 0; i < parts.count(); ++i ) { - Part part = Part::retrieveByName( QString::fromUtf8( parts[ i ] ) ); - if ( !part.isValid() ) + // handle commands that can be applied to more than one item + if ( command == AKONADI_PARAM_FLAGS ) { + const QList flags = m_streamParser->readParenthesizedList(); + // TODO move this iteration to an SQL query. + for ( int i = 0; i < pimItems.count(); ++i ) { + if ( op == Replace ) { + if ( !replaceFlags( pimItems[ i ], flags ) ) + return failureResponse( "Unable to replace item flags." ); + } else if ( op == Add ) { + if ( !addFlags( pimItems[ i ], flags ) ) + return failureResponse( "Unable to add item flags." ); + } else if ( op == Delete ) { + if ( !deleteFlags( pimItems[ i ], flags ) ) + return failureResponse( "Unable to remove item flags." ); + } + // TODO what is this about? + if ( !silent ) { + sendPimItemResponse( pimItems[i] ); + } + } + + changes << AKONADI_PARAM_FLAGS; continue; + } + + + // handle commands that can only be applied to one item + if ( pimItems.size() > 1 ) + throw HandlerException( "This Modification can only be applied to a single item" ); + PimItem &item = pimItems.first(); + if ( !item.isValid() ) + throw HandlerException( "Invalid item in query result!?" ); + + if ( command == AKONADI_PARAM_REMOTEID ) { + const QString rid = m_streamParser->readUtf8String(); + if ( item.remoteId() != rid ) { + if ( !connection()->isOwnerResource( item ) ) + throw HandlerException( "Only resources can modify remote identifiers" ); + item.setRemoteId( rid ); + changes << AKONADI_PARAM_REMOTEID; + } + } + + else if ( command == AKONADI_PARAM_UNDIRTY ) { + m_streamParser->readString(); // ### ??? + item.setDirty( false ); + } + + else if ( command == AKONADI_PARAM_SIZE ) { + mSize = m_streamParser->readNumber(); + changes << AKONADI_PARAM_SIZE; + } + + else if ( command == "PARTS" ) { + const QList parts = m_streamParser->readParenthesizedList(); + if ( op == Delete ) { + if ( !store->removeItemParts( item, parts ) ) + return failureResponse( "Unable to remove item parts." ); + changes += QSet::fromList( parts ); + } + } - partList.append( part.name().toLatin1() ); + else if ( command == "COLLECTION" ) { + throw HandlerException( "Item moving via STORE is deprecated, update your Akonadi client" ); + } + + // parts/attributes + else { + // obtain and configure the part object + int partVersion = 0; + QByteArray partName; + ImapParser::splitVersionedKey( command, partName, partVersion ); + + SelectQueryBuilder qb; + qb.addValueCondition( Part::pimItemIdColumn(), Query::Equals, item.id() ); + qb.addValueCondition( Part::nameColumn(), Query::Equals, QString::fromUtf8( partName ) ); + if ( !qb.exec() ) + return failureResponse( "Unable to check item part existence" ); + Part::List result = qb.result(); + Part part; + if ( !result.isEmpty() ) + part = result.first(); + part.setName( QString::fromUtf8( partName ) ); + part.setVersion( partVersion ); + part.setPimItemId( item.id() ); + + QByteArray value; + if ( m_streamParser->hasLiteral() ) { + const qint64 dataSize = m_streamParser->remainingLiteralSize(); + partSizes += dataSize; + const bool storeInFile = ( DbConfig::useExternalPayloadFile() && dataSize > DbConfig::sizeThreshold() ); + //actual case when streaming storage is used: external payload is enabled, data is big enough in a literal + if ( storeInFile ) { + part.setExternal( true ); //the part WILL be external + value = m_streamParser->readLiteralPart(); // ### why? + + if ( part.isValid() ) { + if ( !PartHelper::update( &part, value, dataSize ) ) + return failureResponse( "Unable to update item part" ); + } else { + qDebug() << "insert from Store::handleLine"; + part.setData( value ); + part.setDatasize( value.size() ); // ### why not datasize? + if ( !PartHelper::insert( &part ) ) + return failureResponse( "Unable to add item part" ); + } + + //the actual streaming code, reads from the parser, writes immediately to the file + // ### move this entire block to part helper? should be useful for append as well + const QString fileName = QString::fromUtf8( part.data() ); + QFile file( fileName ); + if ( file.open( QIODevice::WriteOnly | QIODevice::Append ) ) { + while ( !m_streamParser->atLiteralEnd() ) { + value = m_streamParser->readLiteralPart(); + file.write( value ); // ### error handling? + } + file.close(); + } else { + return failureResponse( "Unable to update item part" ); + } + + changes << partName; + continue; + } else { // not store in file + //don't write in streaming way as the data goes to the database + while (!m_streamParser->atLiteralEnd()) { + value += m_streamParser->readLiteralPart(); + } + } + } else { //not a literal + value = m_streamParser->readString(); + partSizes += value.size(); + } + + const QByteArray origData = PartHelper::translateData( part ); + if ( origData != value ) { + if ( part.isValid() ) { + if ( !PartHelper::update( &part, value, value.size() ) ) + return failureResponse( "Unable to update item part" ); + } else { + qDebug() << "insert from Store::handleLine: " << value.left(100); + part.setData( value ); + part.setDatasize( value.size() ); + if ( !PartHelper::insert( &part ) ) + return failureResponse( "Unable to add item part" ); + } + changes << partName; + } + + } // parts/attribute modification } - if ( !store->removeItemParts( item, partList ) ) { - qDebug( "Store::deleteParts: Unable to remove item parts" ); - return false; + QString datetime; + if ( !changes.isEmpty() ) { + // update item size + if ( pimItems.size() == 1 && (mSize > 0 || partSizes > 0) ) + pimItems.first().setSize( qMax( mSize, partSizes ) ); + + // run update query and prepare change notifications + for ( int i = 0; i < pimItems.count(); ++i ) { + PimItem &item = pimItems[ i ]; + item.setDatetime( modificationtime ); + item.setAtime( modificationtime ); + if ( !connection()->isOwnerResource( item ) ) + item.setDirty( true ); + if ( !item.update() ) + throw HandlerException( "Unable to write item changes into the database" ); + store->notificationCollector()->itemChanged( item, changes ); + } + + if ( !transaction.commit() ) + return failureResponse( "Cannot commit transaction." ); + + datetime = QLocale::c().toString( modificationtime, QLatin1String( "dd-MMM-yyyy hh:mm:ss +0000" ) ); + } else { + datetime = QLocale::c().toString( pimItems.first().datetime(), QLatin1String( "dd-MMM-yyyy hh:mm:ss +0000" ) ); } + + Response response; + response.setTag( tag() ); + response.setSuccess(); + response.setString( "DATETIME " + ImapParser::quote( datetime.toUtf8() ) + " STORE completed" ); + + emit responseAvailable( response ); return true; } +void Store::parseCommand() +{ + mScope.parseScope( m_streamParser ); + + // parse the stuff before the modification list + while ( !m_streamParser->hasList() ) { + const QByteArray command = m_streamParser->readString(); + if ( command.isEmpty() ) { // ie. we are at command end + throw HandlerException( "No modification list provided in STORE command" ); + } else if ( command == AKONADI_PARAM_REVISION ) { + mPreviousRevision = m_streamParser->readNumber(); + } else if ( command == AKONADI_PARAM_SIZE ) { + mSize = m_streamParser->readNumber(); + } + } +} + +void Store::sendPimItemResponse( const PimItem &pimItem ) +{ + QList flags = pimItem.flags(); + QStringList flagList; + for ( int j = 0; j < flags.count(); ++j ) + flagList.append( flags[ j ].name() ); + + Response response; + response.setUntagged(); + // IMAP protocol violation: should actually be the sequence number + response.setString( QByteArray::number( pimItem.id() ) + " FETCH (FLAGS (" + flagList.join( QLatin1String(" ") ).toUtf8() + "))" ); + emit responseAvailable( response ); +} + +#include "store.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/store.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/store.h --- akonadi-1.1.1/server/src/handler/store.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/store.h 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,6 @@ /*************************************************************************** * Copyright (C) 2006 by Tobias Koenig * + * Copyright (C) 2009 by Volker Krause * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * @@ -21,8 +22,10 @@ #define AKONADISTORE_H #include +#include #include "storage/entity.h" +#include "libs/imapset_p.h" namespace Akonadi { @@ -31,16 +34,69 @@ /** @ingroup akonadi_server_handler - Handler for the store command. - */ + Handler for the item modification command. + +

Syntax

+ One of the following three: + @verbatim + STORE + UID MOVE [] + RID MOVE [] + @endverbatim + + @c revision-check is one of the following and allowed iff one item was selected for modification: + @verbatim + NOREV + REV + @endverbatim + + @c modifcations is a parenthesized list containing any of the following: + @verbatim + SIZE + [+-]FLAGS + REMOTEID + DIRTY + + + @endverbatim + +

Semantics

+ Modifies the selected items. Item selection can happen within the usual three scopes: + - based on a uid set relative to the currently selected collection + - based on a global uid set (UID) + - based on a list of remote identifiers within the currently selected collection (RID) + + The following item properties can be mofidied: + - the remote identifier (@c REMOTEID) + - resetting the dirty flag indication local changes not yet replicated to the backend (@c DIRTY) + - adding/deleting/setting item flags (@c FLAGS) + - setting the item size hint (@c SIZE) + - changing item attributes + - changing item payload parts + + If multiple items are selected only the following operations are valid: + - adding flags + - removing flags + - settings flags + + The following operations are only allowed by resources: + - resetting the dirty flag + - modifying the remote identifier + + Conflict detection: + - only available when modifying a single item + - requires the previous item revision to be provided (@c REV) +*/ + class Store : public Handler { - public: - Store(); - ~Store(); + Q_OBJECT - bool handleLine(const QByteArray& line); + public: + Store( Scope::SelectionScope scope ); + bool parseStream(); + private: enum Operation { Replace, @@ -48,12 +104,18 @@ Delete }; + void parseCommand(); - private: bool replaceFlags( const PimItem &item, const QList &flags ); bool addFlags( const PimItem &item, const QList &flags ); bool deleteFlags( const PimItem &item, const QList &flags ); - bool deleteParts( const PimItem &item, const QList &parts ); + void sendPimItemResponse( const PimItem &pimItem ); + + private: + Scope mScope; + int mPos; + qint64 mPreviousRevision; + qint64 mSize; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/subscribe.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/subscribe.cpp --- akonadi-1.1.1/server/src/handler/subscribe.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/subscribe.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -19,7 +19,7 @@ #include "subscribe.h" -#include "../../libs/imapparser_p.h" +#include "imapstreamparser.h" #include #include #include @@ -27,30 +27,29 @@ using namespace Akonadi; -bool Subscribe::handleLine(const QByteArray & line) +Subscribe::Subscribe(bool subscribe) : + mSubscribe( subscribe ) { - QByteArray buffer; - int pos = ImapParser::parseString( line, buffer ); // tag - - // command - pos = ImapParser::parseString( line, buffer, pos ); - const bool subscribe = buffer == QByteArray( "SUBSCRIBE" ); +} +bool Subscribe::parseStream() +{ DataStore *store = connection()->storageBackend(); Transaction transaction( store ); - forever { - pos = ImapParser::parseString( line, buffer, pos ); - if ( pos == line.length() || buffer.isEmpty() ) + QByteArray buffer; + while (!m_streamParser->atCommandEnd()) { + buffer = m_streamParser->readString(); + if ( buffer.isEmpty() ) break; - Location loc = HandlerHelper::collectionFromIdOrName( buffer ); - if ( !loc.isValid() ) + Collection col = HandlerHelper::collectionFromIdOrName( buffer ); + if ( !col.isValid() ) return failureResponse( "Invalid collection" ); - if ( loc.subscribed() == subscribe ) + if ( col.subscribed() == mSubscribe ) continue; // TODO do all changes in one db operation - loc.setSubscribed( subscribe ); - if ( !loc.update() ) + col.setSubscribed( mSubscribe ); + if ( !col.update() ) return failureResponse( "Unable to change subscription" ); } @@ -60,3 +59,5 @@ return successResponse( "Completed" ); } + +#include "subscribe.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/subscribe.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/subscribe.h --- akonadi-1.1.1/server/src/handler/subscribe.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/subscribe.h 2009-07-28 17:52:13.000000000 +0100 @@ -40,8 +40,13 @@ */ class Subscribe : public Handler { + Q_OBJECT public: - bool handleLine( const QByteArray &line ); + Subscribe( bool subscribe ); + bool parseStream(); + + private: + bool mSubscribe; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/transaction.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/transaction.cpp --- akonadi-1.1.1/server/src/handler/transaction.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/transaction.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -20,51 +20,42 @@ #include "transaction.h" #include "storage/datastore.h" #include "akonadiconnection.h" -#include "../../libs/imapparser_p.h" #include "response.h" +#include "imapstreamparser.h" -Akonadi::TransactionHandler::TransactionHandler() -{ -} +#include -Akonadi::TransactionHandler::~ TransactionHandler() +Akonadi::TransactionHandler::TransactionHandler( Mode mode ) : + mMode( mode ) { } -bool Akonadi::TransactionHandler::handleLine(const QByteArray & line) +bool Akonadi::TransactionHandler::parseStream() { - int pos = line.indexOf( ' ' ) + 1; // skip tag - - QByteArray command; - pos = ImapParser::parseString( line, command, pos ); - DataStore *store = connection()->storageBackend(); - if ( command == "BEGIN" ) { + if ( mMode == Begin ) { if ( !store->beginTransaction() ) return failureResponse( "Unable to begin transaction." ); } - if ( command == "ROLLBACK" ) { + if ( mMode == Rollback ) { if ( !store->inTransaction() ) return failureResponse( "There is no transaction in progress." ); if ( !store->rollbackTransaction() ) return failureResponse( "Unable to roll back transaction." ); } - if ( command == "COMMIT" ) { + if ( mMode == Commit ) { if ( !store->inTransaction() ) return failureResponse( "There is no transaction in progress." ); if ( !store->commitTransaction() ) return failureResponse( "Unable to commit transaction." ); } - Response response; - response.setTag( tag() ); - response.setSuccess(); - response.setString( command + " completed." ); - emit responseAvailable( response ); - - return true; + deleteLater(); + const QMetaEnum me = metaObject()->enumerator( metaObject()->indexOfEnumerator( "Mode" ) ); + return successResponse( me.valueToKey( mMode ) + QByteArray( " completed" ) ); } +#include "transaction.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/transaction.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/transaction.h --- akonadi-1.1.1/server/src/handler/transaction.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/transaction.h 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -31,11 +31,21 @@ */ class TransactionHandler : public Handler { + Q_OBJECT + Q_ENUMS( Mode ) + public: - TransactionHandler(); - ~TransactionHandler(); + enum Mode { + Begin, + Commit, + Rollback + }; + + TransactionHandler( Mode mode ); + bool parseStream(); - bool handleLine( const QByteArray &line ); + private: + Mode mMode; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/uid.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/uid.cpp --- akonadi-1.1.1/server/src/handler/uid.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/uid.cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,97 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include "uid.h" - -#include "akonadi.h" -#include "response.h" - -#include "fetch.h" -#include "store.h" - -using namespace Akonadi; - -Uid::Uid() - : Handler() -{ -} - -Uid::~Uid() -{ -} - -bool Uid::handleLine( const QByteArray& line ) -{ - int start = line.indexOf( ' ' ); // skip tag - - QByteArray subCommand; - if ( !mSubHandler ) { - start = line.indexOf( ' ', start + 1 ); - if ( start == -1 ) { - Response response; - response.setTag( tag() ); - response.setError(); - response.setString( "Syntax error" ); - - emit responseAvailable( response ); - deleteLater(); - - return true; - } - - int end = line.indexOf( ' ', start + 1 ); - - subCommand = line.mid( start + 1, end - start - 1 ).toUpper(); - - mSubHandler = 0; - if ( subCommand == "FETCH" ) - mSubHandler = new Fetch(); - else if ( subCommand == "STORE" ) - mSubHandler = new Store(); - - if ( !mSubHandler ) { - Response response; - response.setTag( tag() ); - response.setError(); - response.setString( "Syntax error" ); - - emit responseAvailable( response ); - deleteLater(); - - return true; - } - - mSubHandler->setTag( tag() ); - mSubHandler->setConnection( connection() ); - - connect( mSubHandler, SIGNAL( responseAvailable( const Response & ) ), - this, SIGNAL( responseAvailable( const Response & ) ) ); - connect( mSubHandler, SIGNAL( connectionStateChange( ConnectionState ) ), - this, SIGNAL( connectionStateChange( ConnectionState ) ) ); - } - - if ( mSubHandler->handleLine( line ) ) - mSubHandler = 0; - else - return false; - - deleteLater(); - - return true; -} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler/uid.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler/uid.h --- akonadi-1.1.1/server/src/handler/uid.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler/uid.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,48 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Tobias Koenig * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#ifndef AKONADIUID_H -#define AKONADIUID_H - -#include - -#include - -namespace Akonadi { - -/** - @ingroup akonadi_server_handler - - Proxy handler for the uid command. - */ -class Uid : public Handler -{ - public: - Uid(); - ~Uid(); - - bool handleLine(const QByteArray& line); - - private: - QPointer mSubHandler; -}; - -} - -#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler.cpp --- akonadi-1.1.1/server/src/handler.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -22,16 +22,19 @@ #include #include -#include "../libs/imapset_p.h" +#include "libs/imapset_p.h" +#include "libs/protocol_p.h" #include "akonadiconnection.h" #include "response.h" +#include "scope.h" #include "handler/akappend.h" #include "handler/aklist.h" #include "handler/append.h" #include "handler/capability.h" #include "handler/copy.h" #include "handler/colcopy.h" +#include "handler/colmove.h" #include "handler/create.h" #include "handler/delete.h" #include "handler/expunge.h" @@ -41,23 +44,27 @@ #include "handler/login.h" #include "handler/logout.h" #include "handler/modify.h" +#include "handler/move.h" #include "handler/noop.h" +#include "handler/remove.h" #include "handler/rename.h" +#include "handler/resourceselect.h" #include "handler/searchpersistent.h" #include "handler/select.h" #include "handler/subscribe.h" #include "handler/status.h" #include "handler/store.h" #include "handler/transaction.h" -#include "uid.h" #include "storage/querybuilder.h" +#include "imapstreamparser.h" using namespace Akonadi; Handler::Handler() :QObject() , m_connection( 0 ) + , m_streamParser( 0 ) { } @@ -66,18 +73,6 @@ { } -bool Handler::handleLine( const QByteArray & command ) -{ - Response response; - response.setError(); - response.setTag( m_tag ); - response.setString( "Unrecognized command: " + command ); - - emit responseAvailable( response ); - deleteLater(); - return true; -} - Handler * Handler::findHandlerForCommandNonAuthenticated( const QByteArray & command ) { // allowed are LOGIN and AUTHENTICATE @@ -107,53 +102,79 @@ return m_tag; } -Handler * Handler::findHandlerForCommandAuthenticated( const QByteArray & command ) +Handler * Handler::findHandlerForCommandAuthenticated( const QByteArray &_command, ImapStreamParser *streamParser ) { + QByteArray command( _command ); + Scope::SelectionScope scope = Scope::None; + + // deal with command prefixes + if ( command == AKONADI_CMD_UID ) { + scope = Scope::Uid; + command = streamParser->readString(); + } else if ( command == AKONADI_CMD_RID ) { + scope = Scope::Rid; + command = streamParser->readString(); + } + // allowed commands are listed below ;-). if ( command == "APPEND" ) return new Append(); - if ( command == "CREATE" ) - return new Create(); + if ( command == AKONADI_CMD_COLLECTIONCREATE ) + return new Create( scope ); if ( command == "LIST" ) return new List(); if ( command == "SELECT" ) - return new Select(); + return new Select( scope ); if ( command == "SEARCH_STORE" ) return new SearchPersistent(); if ( command == "NOOP" ) return new Noop(); - if ( command == "FETCH" ) - return new Fetch(); + if ( command == AKONADI_CMD_ITEMFETCH ) + return new Fetch( scope ); if ( command == "EXPUNGE" ) return new Expunge(); - if ( command == "UID" ) - return new Uid(); - if ( command == "STORE" ) - return new Store(); + if ( command == AKONADI_CMD_ITEMMODIFY ) + return new Store( scope ); if ( command == "STATUS" ) return new Status(); - if ( command == "DELETE" ) - return new Delete(); - if ( command == "MODIFY" ) - return new Modify(); + if ( command == AKONADI_CMD_COLLECTIONDELETE ) + return new Delete( scope ); + if ( command == AKONADI_CMD_COLLECTIONMODIFY ) + return new Modify( scope ); if ( command == "RENAME" ) return new Rename(); - if ( command == "BEGIN" || command == "ROLLBACK" || command == "COMMIT" ) - return new TransactionHandler(); - if ( command == "X-AKAPPEND" ) + if ( command == "BEGIN" ) + return new TransactionHandler( TransactionHandler::Begin ); + if ( command == "ROLLBACK" ) + return new TransactionHandler( TransactionHandler::Rollback ); + if ( command == "COMMIT" ) + return new TransactionHandler( TransactionHandler::Commit ); + if ( command == AKONADI_CMD_ITEMCREATE ) return new AkAppend(); - if ( command == "X-AKLIST" || command == "X-AKLSUB" ) - return new AkList(); - if ( command == "SUBSCRIBE" || command == "UNSUBSCRIBE" ) - return new Subscribe(); - if ( command == "COPY" ) + if ( command == "X-AKLIST" ) + return new AkList( scope, false ); + if ( command == "X-AKLSUB" ) + return new AkList( scope, true ); + if ( command == "SUBSCRIBE" ) + return new Subscribe( true ); + if ( command == "UNSUBSCRIBE" ) + return new Subscribe( false ); + if ( command == AKONADI_CMD_ITEMCOPY ) return new Copy(); - if ( command == "COLCOPY" ) + if ( command == AKONADI_CMD_COLLECTIONCOPY ) return new ColCopy(); if ( command == "LINK" ) return new Link( true ); if ( command == "UNLINK" ) return new Link( false ); + if ( command == AKONADI_CMD_RESOURCESELECT ) + return new ResourceSelect(); + if ( command == AKONADI_CMD_ITEMDELETE ) + return new Remove( scope ); + if ( command == AKONADI_CMD_ITEMMOVE ) + return new Move( scope ); + if ( command == AKONADI_CMD_COLLECTIONMOVE ) + return new ColMove( scope ); return 0; } @@ -165,7 +186,7 @@ } -AkonadiConnection* Akonadi::Handler::connection() +AkonadiConnection* Akonadi::Handler::connection() const { return m_connection; } @@ -179,8 +200,7 @@ response.setFailure(); response.setString( failureMessage ); emit responseAvailable( response ); - deleteLater(); - return true; + return false; } bool Akonadi::Handler::failureResponse( const QByteArray &failureMessage ) @@ -200,46 +220,33 @@ response.setSuccess(); response.setString( QString::fromLatin1(successMessage) ); emit responseAvailable( response ); - deleteLater(); return true; } -void Handler::imapSetToQuery(const ImapSet & set, bool isUid, QueryBuilder & qb) +void Handler::setStreamParser( ImapStreamParser *parser ) { - Query::Condition cond( Query::Or ); - foreach ( const ImapInterval i, set.intervals() ) { - if ( i.hasDefinedBegin() && i.hasDefinedEnd() ) { - if ( i.size() == 1 ) { - cond.addValueCondition( PimItem::idFullColumnName(), Query::Equals, i.begin() ); - } else { - Query::Condition subCond( Query::And ); - subCond.addValueCondition( PimItem::idFullColumnName(), Query::GreaterOrEqual, i.begin() ); - subCond.addValueCondition( PimItem::idFullColumnName(), Query::LessOrEqual, i.end() ); - cond.addCondition( subCond ); - } - } else if ( i.hasDefinedBegin() ) { - cond.addValueCondition( PimItem::idFullColumnName(), Query::GreaterOrEqual, i.begin() ); - } else if ( i.hasDefinedEnd() ) { - cond.addValueCondition( PimItem::idFullColumnName(), Query::LessOrEqual, i.end() ); - } - } - if ( !cond.isEmpty() ) - qb.addCondition( cond ); + m_streamParser = parser; +} - if ( !isUid && connection()->selectedCollection() >= 0 ) { - const Location loc = connection()->selectedLocation(); - // FIXME: we probably want to do both paths here in all cases, but that is apparently - // non-trivial with SQL - if ( loc.resource().name() == QLatin1String("akonadi_search_resource") || - loc.resource().name().contains( QLatin1String("nepomuk") ) ) { - qb.addTable( LocationPimItemRelation::tableName() ); - qb.addValueCondition( LocationPimItemRelation::leftFullColumnName(), Query::Equals, loc.id() ); - qb.addColumnCondition( LocationPimItemRelation::rightFullColumnName(), Query::Equals, - PimItem::idFullColumnName() ); - } else { - qb.addValueCondition( PimItem::locationIdColumn(), Query::Equals, loc.id() ); - } - } + +UnknownCommandHandler::UnknownCommandHandler(const QByteArray command) : + mCommand( command ) +{ +} + +bool UnknownCommandHandler::parseStream() +{ + Response response; + response.setError(); + response.setTag( tag() ); + if ( mCommand.isEmpty() ) + response.setString( "No command specified" ); + else + response.setString( "Unrecognized command: " + mCommand ); + m_streamParser->readUntilCommandEnd(); + + emit responseAvailable( response ); + return true; } #include "handler.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handler.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handler.h --- akonadi-1.1.1/server/src/handler.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handler.h 2009-07-28 17:52:13.000000000 +0100 @@ -26,6 +26,7 @@ #include "akonadiprivate_export.h" #include "global.h" +#include "exception.h" namespace Akonadi { @@ -33,6 +34,9 @@ class AkonadiConnection; class QueryBuilder; class ImapSet; +class ImapStreamParser; + +AKONADI_EXCEPTION_MAKE_INSTANCE( HandlerException ); /** \defgroup akonadi_server_handler Command handlers @@ -62,19 +66,28 @@ QByteArray tag() const; /** - * Process one line of input. - * @param line The input. - * @return false if the handler expects to read more data from the client, true otherwise. + * Find a handler for a command that is always allowed, like LOGOUT. + * @param line the command string + * @return an instance to the handler. The handler is deleted after @see handelLine is executed. The caller needs to delete the handler in case an exception is thrown from handelLine. */ - virtual bool handleLine( const QByteArray & line ); - static Handler* findHandlerForCommandAlwaysAllowed( const QByteArray& line ); + + /** + * Find a handler for a command that is allowed when the client is not yet authenticated, like LOGIN. + * @param line the command string + * @return an instance to the handler. The handler is deleted after @see handelLine is executed. The caller needs to delete the handler in case an exception is thrown from handelLine. + */ static Handler* findHandlerForCommandNonAuthenticated( const QByteArray& line ); - static Handler* findHandlerForCommandAuthenticated( const QByteArray& line ); - static Handler* findHandlerForCommandSelected( const QByteArray& line ); + + /** + * Find a handler for a command that is allowed when the client is authenticated, like LIST, FETCH, etc. + * @param line the command string + * @return an instance to the handler. The handler is deleted after @see handelLine is executed. The caller needs to delete the handler in case an exception is thrown from handelLine. + */ + static Handler* findHandlerForCommandAuthenticated( const QByteArray& line, ImapStreamParser *streamParser ); void setConnection( AkonadiConnection* connection ); - AkonadiConnection* connection(); + AkonadiConnection* connection() const; /** Send a failure response with the given message. */ bool failureResponse( const QString& failureMessage ); @@ -92,6 +105,18 @@ /** Send a success response with the given message. */ bool successResponse( const char *successMessage ); + /** + * Assigns the streaming IMAP parser to the handler. Useful only if supportsStreamParser() returns true. + * @param parser the imap parser object + */ + void setStreamParser( ImapStreamParser *parser ); + + /** + * Parse and handle the IMAP message using the streaming parser. The implementation MUST leave the trailing newline character(s) in the stream! + * @return true if parsed successfully, false in case of parse failure + */ + virtual bool parseStream() = 0; + Q_SIGNALS: /** @@ -109,16 +134,23 @@ */ void connectionStateChange( ConnectionState state ); - protected: - /** - Adds WHERE conditions to the given query builder which represent - the given IMAP sequence set. - */ - void imapSetToQuery( const ImapSet &set, bool isUid, QueryBuilder &qb ); - private: QByteArray m_tag; AkonadiConnection* m_connection; + +protected: + ImapStreamParser *m_streamParser; +}; + +class UnknownCommandHandler : public Handler +{ + Q_OBJECT + public: + UnknownCommandHandler( const QByteArray command ); + bool parseStream(); + + private: + QByteArray mCommand; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handlerhelper.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handlerhelper.cpp --- akonadi-1.1.1/server/src/handlerhelper.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handlerhelper.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -27,48 +27,48 @@ QByteArray Akonadi::HandlerHelper::normalizeCollectionName(const QByteArray &name) { - QByteArray collection = name; - if ( collection.startsWith( '/' ) ) - collection = collection.right( collection.length() - 1 ); - if ( collection.endsWith( '/' ) ) - collection = collection.left( collection.length() - 1 ); - return collection; + QByteArray collectionByteArray = name; + if ( collectionByteArray.startsWith( '/' ) ) + collectionByteArray = collectionByteArray.right( collectionByteArray.length() - 1 ); + if ( collectionByteArray.endsWith( '/' ) ) + collectionByteArray = collectionByteArray.left( collectionByteArray.length() - 1 ); + return collectionByteArray; } -Location HandlerHelper::collectionFromIdOrName(const QByteArray & id) +Collection HandlerHelper::collectionFromIdOrName(const QByteArray & id) { // id is a number bool ok = false; qint64 collectionId = id.toLongLong( &ok ); if ( ok ) - return Location::retrieveById( collectionId ); + return Collection::retrieveById( collectionId ); // id is a path QString path = QString::fromUtf8( normalizeCollectionName( id ) ); // ### should be UTF-7 for real IMAP compatibility const QStringList pathParts = path.split( QLatin1Char('/') ); - Location loc; + Collection col; foreach ( const QString &part, pathParts ) { - SelectQueryBuilder qb; - qb.addValueCondition( Location::nameColumn(), Query::Equals, part ); - if ( loc.isValid() ) - qb.addValueCondition( Location::parentIdColumn(), Query::Equals, loc.id() ); + SelectQueryBuilder qb; + qb.addValueCondition( Collection::nameColumn(), Query::Equals, part ); + if ( col.isValid() ) + qb.addValueCondition( Collection::parentIdColumn(), Query::Equals, col.id() ); else - qb.addValueCondition( Location::parentIdColumn(), Query::Equals, 0 ); + qb.addValueCondition( Collection::parentIdColumn(), Query::Equals, 0 ); if ( !qb.exec() ) - return Location(); - Location::List list = qb.result(); + return Collection(); + Collection::List list = qb.result(); if ( list.count() != 1 ) - return Location(); - loc = list.first(); + return Collection(); + col = list.first(); } - return loc; + return col; } -QString HandlerHelper::pathForCollection(const Location & loc) +QString HandlerHelper::pathForCollection(const Collection & col) { QStringList parts; - Location current = loc; + Collection current = col; while ( current.isValid() ) { parts.prepend( QString::fromUtf8( current.name() ) ); current = current.parent(); @@ -76,23 +76,23 @@ return parts.join( QLatin1String("/") ); } -int HandlerHelper::itemCount(const Location & loc) +int HandlerHelper::itemCount(const Collection & col) { CountQueryBuilder qb; qb.addTable( PimItem::tableName() ); - qb.addValueCondition( PimItem::locationIdColumn(), Query::Equals, loc.id() ); + qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() ); if ( !qb.exec() ) return -1; return qb.result(); } -int HandlerHelper::itemWithFlagCount(const Location & loc, const QString & flag) +int HandlerHelper::itemWithFlagCount(const Collection & col, const QString & flag) { CountQueryBuilder qb; qb.addTable( PimItem::tableName() ); qb.addTable( Flag::tableName() ); qb.addTable( PimItemFlagRelation::tableName() ); - qb.addValueCondition( PimItem::locationIdFullColumnName(), Query::Equals, loc.id() ); + qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, col.id() ); qb.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, PimItemFlagRelation::leftFullColumnName() ); qb.addColumnCondition( Flag::idFullColumnName(), Query::Equals, PimItemFlagRelation::rightFullColumnName() ); qb.addValueCondition( Flag::nameFullColumnName(), Query::Equals, flag ); @@ -101,75 +101,118 @@ return qb.result(); } -int HandlerHelper::itemWithoutFlagCount(const Location & loc, const QString & flag) +int HandlerHelper::itemWithoutFlagCount(const Collection & col, const QString & flag) { // FIXME optimize me: use only one query or reuse previously done count - const int flagCount = itemWithFlagCount( loc, flag ); - const int totalCount = itemCount( loc ); + const int flagCount = itemWithFlagCount( col, flag ); + const int totalCount = itemCount( col ); if ( totalCount < 0 || flagCount < 0 ) return -1; return totalCount - flagCount; } -int HandlerHelper::parseCachePolicy(const QByteArray & data, Location & loc, int start) +qint64 HandlerHelper::itemsTotalSize(const Collection & col) { + QueryBuilder qb; + qb.addTable( PimItem::tableName() ); + qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, col.id() ); + qb.addColumn( QLatin1String("sum(size)") ); + + if ( !qb.exec() ) + return -1; + if ( !qb.query().next() ) { + qDebug() << "Error during retrieving result of query:" << qb.query().lastError().text(); + return -1; + } + return qb.query().value( 0 ).toLongLong(); +} + +int HandlerHelper::parseCachePolicy(const QByteArray & data, Collection & col, int start, bool *changed ) +{ + bool inheritChanged = false; + bool somethingElseChanged = false; + QList params; int end = Akonadi::ImapParser::parseParenthesizedList( data, params, start ); for ( int i = 0; i < params.count() - 1; i += 2 ) { const QByteArray key = params[i]; const QByteArray value = params[i + 1]; - if ( key == "INHERIT" ) - loc.setCachePolicyInherit( value == "true" ); - else if ( key == "INTERVAL" ) - loc.setCachePolicyCheckInterval( value.toInt() ); - else if ( key == "CACHETIMEOUT" ) - loc.setCachePolicyCacheTimeout( value.toInt() ); - else if ( key == "SYNCONDEMAND" ) - loc.setCachePolicySyncOnDemand( value == "true" ); - else if ( key == "LOCALPARTS" ) { + if ( key == "INHERIT" ) { + const bool inherit = value == "true"; + inheritChanged = col.cachePolicyInherit() != inherit; + col.setCachePolicyInherit( inherit ); + } else if ( key == "INTERVAL" ) { + const int interval = value.toInt(); + somethingElseChanged = somethingElseChanged || interval != col.cachePolicyCheckInterval(); + col.setCachePolicyCheckInterval( interval ); + } else if ( key == "CACHETIMEOUT" ) { + const int timeout = value.toInt(); + somethingElseChanged = somethingElseChanged || timeout != col.cachePolicyCacheTimeout(); + col.setCachePolicyCacheTimeout( timeout ); + } else if ( key == "SYNCONDEMAND" ) { + const bool syncOnDemand = value == "true"; + somethingElseChanged = somethingElseChanged || syncOnDemand != col.cachePolicySyncOnDemand(); + col.setCachePolicySyncOnDemand( syncOnDemand ); + } else if ( key == "LOCALPARTS" ) { QList tmp; - QStringList parts; + QStringList partsList; Akonadi::ImapParser::parseParenthesizedList( value, tmp ); foreach ( const QByteArray &ba, tmp ) - parts << QString::fromLatin1( ba ); - loc.setCachePolicyLocalParts( parts.join( QLatin1String(" ") ) ); + partsList << QString::fromLatin1( ba ); + const QString parts = partsList.join( QLatin1String( " " ) ); + somethingElseChanged = somethingElseChanged || col.cachePolicyLocalParts() != parts; + col.setCachePolicyLocalParts( parts ); } } + + if ( changed && (inheritChanged || (!col.cachePolicyInherit() && somethingElseChanged)) ) + *changed = true; + return end; } -QByteArray HandlerHelper::cachePolicyToByteArray(const Location & loc) +QByteArray HandlerHelper::cachePolicyToByteArray(const Collection & col) { QByteArray rv = "CACHEPOLICY ("; - rv += "INHERIT " + ( loc.cachePolicyInherit() ? QByteArray("true") : QByteArray("false") ); - rv += " INTERVAL " + QByteArray::number( loc.cachePolicyCheckInterval() ); - rv += " CACHETIMEOUT " + QByteArray::number( loc.cachePolicyCacheTimeout() ); - rv += " SYNCONDEMAND " + ( loc.cachePolicySyncOnDemand() ? QByteArray("true") : QByteArray("false") ); - rv += " LOCALPARTS (" + loc.cachePolicyLocalParts().toLatin1() + ')'; + rv += "INHERIT " + ( col.cachePolicyInherit() ? QByteArray("true") : QByteArray("false") ); + rv += " INTERVAL " + QByteArray::number( col.cachePolicyCheckInterval() ); + rv += " CACHETIMEOUT " + QByteArray::number( col.cachePolicyCacheTimeout() ); + rv += " SYNCONDEMAND " + ( col.cachePolicySyncOnDemand() ? QByteArray("true") : QByteArray("false") ); + rv += " LOCALPARTS (" + col.cachePolicyLocalParts().toLatin1() + ')'; rv += ')'; return rv; } -QByteArray HandlerHelper::collectionToByteArray( const Location & loc, bool hidden ) +QByteArray HandlerHelper::collectionToByteArray( const Collection & col, bool hidden, bool includeStatistics ) { - QByteArray b = QByteArray::number( loc.id() ) + ' ' - + QByteArray::number( loc.parentId() ) + " ("; + QByteArray b = QByteArray::number( col.id() ) + ' ' + + QByteArray::number( col.parentId() ) + " ("; // FIXME: escape " and "\" - b += "NAME \"" + loc.name() + "\" "; + b += "NAME \"" + col.name() + "\" "; if ( hidden ) b+= "MIMETYPE () "; else - b += "MIMETYPE (" + MimeType::joinByName( loc.mimeTypes(), QLatin1String( " " ) ).toLatin1() + ") "; - b += "REMOTEID \"" + loc.remoteId().toUtf8() + "\" "; - b += "RESOURCE \"" + loc.resource().name().toUtf8() + "\" "; + b += "MIMETYPE (" + MimeType::joinByName( col.mimeTypes(), QLatin1String( " " ) ).toLatin1() + ") "; + b += "REMOTEID \"" + col.remoteId().toUtf8() + "\" "; + b += "RESOURCE \"" + col.resource().name().toUtf8() + "\" "; + + if ( includeStatistics ) { + b += "MESSAGES " + QByteArray::number( HandlerHelper::itemCount( col ) ) + ' '; + b += "UNSEEN " + QByteArray::number( HandlerHelper::itemWithoutFlagCount( col, QLatin1String( "\\Seen" ) ) ) + ' '; + b += "SIZE " + QByteArray::number( HandlerHelper::itemsTotalSize( col ) ) + ' '; + } - b += HandlerHelper::cachePolicyToByteArray( loc ) + ' '; + b += HandlerHelper::cachePolicyToByteArray( col ) + ' '; - LocationAttribute::List attrs = loc.attributes(); - foreach ( const LocationAttribute &attr, attrs ) + const CollectionAttribute::List attrs = col.attributes(); + for ( int i = 0; i < attrs.size(); ++i ) { + const CollectionAttribute &attr = attrs[i]; b += attr.type() + ' ' + ImapParser::quote( attr.value() ); + if ( i != attrs.size() - 1 ) + b += ' '; + } b+= ')'; return b; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/handlerhelper.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/handlerhelper.h --- akonadi-1.1.1/server/src/handlerhelper.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/handlerhelper.h 2009-07-28 17:52:13.000000000 +0100 @@ -42,51 +42,59 @@ /** Returns the collection identified by the given id or path. */ - static Location collectionFromIdOrName( const QByteArray &id ); + static Collection collectionFromIdOrName( const QByteArray &id ); /** Returns the full path for the given collection. */ - static QString pathForCollection( const Location &loc ); + static QString pathForCollection( const Collection &col ); /** Returns the amount of existing items in the given collection. @return -1 on error */ - static int itemCount( const Location &loc ); + static int itemCount( const Collection &col ); /** Returns the amount of existing items in the given collection which have a given flag set. @return -1 on error. */ - static int itemWithFlagCount( const Location &loc, const QString &flag ); + static int itemWithFlagCount( const Collection &col, const QString &flag ); /** Returns the amount of existing items in the given collection which have a given flag not set. @return -1 on error */ - static int itemWithoutFlagCount( const Location &loc, const QString &flag ); + static int itemWithoutFlagCount( const Collection &col, const QString &flag ); /** - Parse cache policy and update the given Location object accoordingly. + Returns the total size of all the items in the given collection. + @return -1 on error + */ + static qint64 itemsTotalSize( const Collection &col ); + + /** + Parse cache policy and update the given Collection object accoordingly. + @param changed Indicates wether or not the cache policy already available in @p col + has actually changed @todo Error handling. */ - static int parseCachePolicy( const QByteArray &data, Location &loc, int start = 0 ); + static int parseCachePolicy( const QByteArray& data, Akonadi::Collection& col, int start = 0, bool* changed = 0 ); /** Returns the protocol representation of the cache policy of the given - Location object. + Collection object. */ - static QByteArray cachePolicyToByteArray( const Location &loc ); + static QByteArray cachePolicyToByteArray( const Collection &col ); /** - Returns the protocl representation of the given collection. + Returns the protocol representation of the given collection. Make sure DataStore::activeCachePolicy() has been called before to include the effective cache policy */ - static QByteArray collectionToByteArray( const Location &loc, bool hidden = false ); + static QByteArray collectionToByteArray( const Collection &col, bool hidden = false, bool includeStatistics = false ); }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/imapstreamparser.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/imapstreamparser.cpp --- akonadi-1.1.1/server/src/imapstreamparser.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/imapstreamparser.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,748 @@ +/* + Copyright (c) 2006 - 2007 Volker Krause + Copyright (c) 2009 Andras Mantia + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "imapstreamparser.h" +#include "response.h" +#include "tracer.h" + +#include +#include +#include +#include +#include + +using namespace Akonadi; + +ImapStreamParser::ImapStreamParser( QIODevice *socket ) +{ + m_socket = socket; + m_position = 0; + m_literalSize = 0; + m_continuationSize = 0; +} + +ImapStreamParser::~ImapStreamParser() +{ +} + +QString ImapStreamParser::readUtf8String() +{ + QByteArray tmp; + tmp = readString(); + QString result = QString::fromUtf8( tmp ); + return result; +} + + +QByteArray ImapStreamParser::readString() +{ + QByteArray result; + if ( !waitForMoreData( m_data.length() == 0 ) ) + throw ImapParserException("Unable to read more data"); + stripLeadingSpaces(); + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data");; + + // literal string + // TODO: error handling + if ( hasLiteral() ) { + while (!atLiteralEnd()) { + result += readLiteralPart(); + } + return result; + } + + // quoted string + return parseQuotedString(); +} + +bool ImapStreamParser::hasString() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + int savedPos = m_position; + stripLeadingSpaces(); + int pos = m_position; + m_position = savedPos; + if ( m_data[pos] == '{' ) + return true; //literal string + if (m_data[pos] == '"' ) + return true; //quoted string + if ( m_data[pos] != ' ' && + m_data[pos] != '(' && + m_data[pos] != ')' && + m_data[pos] != '[' && + m_data[pos] != ']' && + m_data[pos] != '\n' && + m_data[pos] != '\r' ) + return true; //unquoted string + + return false; //something else, not a string +} + +bool ImapStreamParser::hasLiteral() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + int savedPos = m_position; + stripLeadingSpaces(); + if ( m_data[m_position] == '{' ) + { + int end = -1; + do { + end = m_data.indexOf( '}', m_position ); + if ( !waitForMoreData( end == -1 ) ) + throw ImapParserException("Unable to read more data"); + } while (end == -1); + Q_ASSERT( end > m_position ); + m_literalSize = m_data.mid( m_position + 1, end - m_position - 1 ).toInt(); + // strip CRLF + m_position = end + 1; + + if ( m_position < m_data.length() && m_data[m_position] == '\r' ) + ++m_position; + if ( m_position < m_data.length() && m_data[m_position] == '\n' ) + ++m_position; + + m_continuationSize = qMin(m_position + m_literalSize - m_data.length(), (qint64)4096); + if (m_continuationSize >= 0) + sendContinuationResponse(); + return true; + } else + { + m_position = savedPos; + return false; + } +} + +bool ImapStreamParser::atLiteralEnd() const +{ + return (m_literalSize == 0); +} + +qint64 ImapStreamParser::remainingLiteralSize() +{ + return m_literalSize; +} + +QByteArray ImapStreamParser::readLiteralPart() +{ + static qint64 maxLiteralPartSize = 4096; + int size = qMin(maxLiteralPartSize, m_literalSize); + + if ( !waitForMoreData( m_data.length() < m_position + size ) ) + throw ImapParserException("Unable to read more data"); + + if ( m_data.length() < m_position + size ) { // Still not enough data + // Take what's already there + size = m_data.length() - m_position; + } + + QByteArray result = m_data.mid(m_position, size); + m_position += size; + m_literalSize -= size; + Q_ASSERT(m_literalSize >= 0); + m_data = m_data.right( m_data.size() - m_position ); + m_position = 0; + return result; +} + +bool ImapStreamParser::hasSequenceSet() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + int savedPos = m_position; + stripLeadingSpaces(); + int pos = m_position; + m_position = savedPos; + + if ( m_data[pos] == '*' || m_data[pos] == ':'|| isdigit( m_data[pos] )) { + return true; + } + + return false; +} + +ImapSet ImapStreamParser::readSequenceSet() +{ + ImapSet result; + if (! waitForMoreData( m_data.length() == 0 ) ) + throw ImapParserException("Unable to read more data"); + stripLeadingSpaces(); + qint64 value = -1, lower = -1, upper = -1; + Q_FOREVER { + if ( !waitForMoreData( m_data.length() <= m_position ) ) + { + upper = value; + if ( lower < 0 ) + lower = value; + if ( lower >= 0 && upper >= 0 ) + result.add( ImapInterval( lower, upper ) ); + return result; + } + + if ( m_data[m_position] == '*' ) { + value = 0; + } else if ( m_data[m_position] == ':' ) { + lower = value; + } else if ( isdigit( m_data[m_position] ) ) { + bool ok = false; + value = readNumber( &ok ); + Q_ASSERT( ok ); // TODO handle error + --m_position; + } else { + upper = value; + if ( lower < 0 ) + lower = value; + result.add( ImapInterval( lower, upper ) ); + lower = -1; + upper = -1; + value = -1; + if ( m_data[m_position] != ',' ) { + return result; + } + } + ++m_position; + } +} + +bool ImapStreamParser::hasList() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + int savedPos = m_position; + stripLeadingSpaces(); + int pos = m_position; + m_position = savedPos; + if ( m_data[pos] == '(' ) + { + return true; + } + + return false; +} + +void ImapStreamParser::beginList() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + stripLeadingSpaces(); + if ( m_data[m_position] != '(' ) + throw ImapParserException( "Stream not at a beginning of a list" ); + ++m_position; + return; +} + +bool ImapStreamParser::atListEnd() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + int savedPos = m_position; + stripLeadingSpaces(); + int pos = m_position; + m_position = savedPos; + if ( m_data[pos] == ')' ) + { + m_position = pos + 1; + return true; + } + + return false; +} + +QList ImapStreamParser::readParenthesizedList() +{ + QList result; + if (! waitForMoreData( m_data.length() <= m_position ) ) + throw ImapParserException("Unable to read more data"); + + stripLeadingSpaces(); + if ( m_data[m_position] != '(' ) + return result; //no list found + + bool concatToLast = false; + int count = 0; + int sublistbegin = m_position; + int i = m_position + 1; + Q_FOREVER { + if ( !waitForMoreData( m_data.length() <= i ) ) + { + m_position = i; + throw ImapParserException("Unable to read more data"); + } + if ( m_data[i] == '(' ) { + ++count; + if ( count == 1 ) + sublistbegin = i; + ++i; + continue; + } + if ( m_data[i] == ')' ) { + if ( count <= 0 ) { + m_position = i + 1; + return result; + } + if ( count == 1 ) + result.append( m_data.mid( sublistbegin, i - sublistbegin + 1 ) ); + --count; + ++i; + continue; + } + if ( m_data[i] == ' ' ) { + ++i; + continue; + } + if ( m_data[i] == '[' ) { + concatToLast = true; + result.last()+='['; + ++i; + continue; + } + if ( m_data[i] == ']' ) { + concatToLast = false; + result.last()+=']'; + ++i; + continue; + } + if ( count == 0 ) { + m_position = i; + QByteArray ba; + if (hasLiteral()) { + while (!atLiteralEnd()) { + ba+=readLiteralPart(); + } + } else { + ba = readString(); + } + + // We might sometime get some unwanted CRLF, but we're still not at the end + // of the list, would make further string reads fail so eat the CRLFs. + while ( m_data[m_position]=='\r' || m_data[m_position]=='\n' ) { + m_position++; + } + + i = m_position - 1; + if (concatToLast) { + result.last() += ba; + } else { + result.append( ba ); + } + } + ++i; + } + + throw ImapParserException( "Something went very very wrong!" ); +} + +QByteRef ImapStreamParser::readChar() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + { + throw ImapParserException("Unable to read more data"); + } + m_position++; + return m_data[m_position - 1]; +} + +QDateTime ImapStreamParser::readDateTime() +{ + // Syntax: + // date-time = DQUOTE date-day-fixed "-" date-month "-" date-year + // SP time SP zone DQUOTE + // date-day-fixed = (SP DIGIT) / 2DIGIT + // ; Fixed-format version of date-day + // date-month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / + // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" + // date-year = 4DIGIT + // time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + // ; Hours minutes seconds + // zone = ("+" / "-") 4DIGIT + // ; Signed four-digit value of hhmm representing + // ; hours and minutes east of Greenwich (that is, + // ; the amount that the given time differs from + // ; Universal Time). Subtracting the timezone + // ; from the given time will give the UT form. + // ; The Universal Time zone is "+0000". + // Example : "28-May-2006 01:03:35 +0200" + // Position: 0123456789012345678901234567 + // 1 2 + + int savedPos = m_position; + if (! waitForMoreData( m_data.length() == 0 ) ) + throw ImapParserException("Unable to read more data"); + stripLeadingSpaces(); + + bool quoted = false; + if ( m_data[m_position] == '"' ) { + quoted = true; + ++m_position; + + if ( m_data.length() <= m_position + 26 ) { + m_position = savedPos; + return QDateTime(); + } + } else { + if ( m_data.length() < m_position + 26 ) { + m_position = savedPos; + return QDateTime(); + } + } + + bool ok = true; + const int day = ( m_data[m_position] == ' ' ? m_data[m_position + 1] - '0' // single digit day + : m_data.mid( m_position, 2 ).toInt( &ok ) ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + m_position += 3; + const QByteArray shortMonthNames( "janfebmaraprmayjunjulaugsepoctnovdec" ); + int month = shortMonthNames.indexOf( m_data.mid( m_position, 3 ).toLower() ); + if ( month == -1 ) { + m_position = savedPos; + return QDateTime(); + } + month = month / 3 + 1; + m_position += 4; + const int year = m_data.mid( m_position, 4 ).toInt( &ok ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + m_position += 5; + const int hours = m_data.mid( m_position, 2 ).toInt( &ok ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + m_position += 3; + const int minutes = m_data.mid( m_position, 2 ).toInt( &ok ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + m_position += 3; + const int seconds = m_data.mid( m_position, 2 ).toInt( &ok ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + m_position += 4; + const int tzhh = m_data.mid( m_position, 2 ).toInt( &ok ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + m_position += 2; + const int tzmm = m_data.mid( m_position, 2 ).toInt( &ok ); + if ( !ok ) { + m_position = savedPos; + return QDateTime(); + } + int tzsecs = tzhh*60*60 + tzmm*60; + if ( m_data[m_position - 3] == '-' ) + tzsecs = -tzsecs; + const QDate date( year, month, day ); + const QTime time( hours, minutes, seconds ); + QDateTime dateTime; + dateTime = QDateTime( date, time, Qt::UTC ); + if ( !dateTime.isValid() ) { + m_position = savedPos; + return QDateTime(); + } + dateTime = dateTime.addSecs( -tzsecs ); + + m_position += 2; + if ( m_data.length() <= m_position || !quoted ) + return dateTime; + if ( m_data[m_position] == '"' ) + ++m_position; + return dateTime; +} + +bool ImapStreamParser::hasDateTime() +{ + int savedPos = m_position; + QDateTime dateTime = readDateTime(); + m_position = savedPos; + if (dateTime.isNull()) + return false; + else + return true; +} + +QByteArray ImapStreamParser::parseQuotedString() +{ + QByteArray result; + if (! waitForMoreData( m_data.length() == 0 ) ) + throw ImapParserException("Unable to read more data"); + stripLeadingSpaces(); + int end = m_position; + result.clear(); + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + + bool foundSlash = false; + // quoted string + if ( m_data[m_position] == '"' ) { + ++m_position; + int i = m_position; + Q_FOREVER { + if ( !waitForMoreData( m_data.length() <= i ) ) + { + m_position = i; + throw ImapParserException("Unable to read more data"); + } + + if ( foundSlash ) { + foundSlash = false; + if ( m_data[i] == 'r' ) + result += '\r'; + else if ( m_data[i] == 'n' ) + result += '\n'; + else if ( m_data[i] == '\\' ) + result += '\\'; + else if ( m_data[i] == '\"' ) + result += '\"'; + else { + throw ImapParserException("Unexpected '\\' in quoted string"); + } + ++i; + continue; + } + + if ( m_data[i] == '\\' ) { + foundSlash = true; + ++i; + continue; + } + + if ( m_data[i] == '"' ) { + end = i + 1; // skip the '"' + break; + } + + result += m_data[i]; + + ++i; + } + } + + // unquoted string + else { + bool reachedInputEnd = true; + int i = m_position; + Q_FOREVER { + if ( !waitForMoreData( m_data.length() <= i ) ) + { + m_position = i; + throw ImapParserException("Unable to read more data"); + } + if ( m_data[i] == ' ' || m_data[i] == '(' || m_data[i] == ')' || m_data[i] == '[' || m_data[i] == ']' || m_data[i] == '\n' || m_data[i] == '\r' || m_data[i] == '"') { + end = i; + reachedInputEnd = false; + break; + } + if (m_data[i] == '\\') + foundSlash = true; + i++; + } + if ( reachedInputEnd ) //FIXME: how can it get here? + end = m_data.length(); + + result = m_data.mid( m_position, end - m_position ); + + // transform unquoted NIL + if ( result == "NIL" ) + result.clear(); + + // strip quotes + if ( foundSlash ) { + while ( result.contains( "\\\"" ) ) + result.replace( "\\\"", "\"" ); + while ( result.contains( "\\\\" ) ) + result.replace( "\\\\", "\\" ); + } + } + + m_position = end; + return result; +} + +qint64 ImapStreamParser::readNumber( bool * ok ) +{ + qint64 result; + if ( ok ) + *ok = false; + if (! waitForMoreData( m_data.length() == 0 ) ) + throw ImapParserException("Unable to read more data"); + stripLeadingSpaces(); + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + if ( m_position >= m_data.length() ) + throw ImapParserException("Unable to read more data"); + int i = m_position; + Q_FOREVER { + if ( !waitForMoreData( m_data.length() <= i ) ) + { + m_position = i; + throw ImapParserException("Unable to read more data"); + } + if ( !isdigit( m_data.at( i ) ) ) + break; + ++i; + } + const QByteArray tmp = m_data.mid( m_position, i - m_position ); + bool success = false; + result = tmp.toLongLong( &success ); + if ( ok ) + *ok = success; + else if ( !success ) + throw ImapParserException( "Unable to parse number" ); + m_position = i; + return result; +} + +void ImapStreamParser::stripLeadingSpaces() +{ + for ( int i = m_position; i < m_data.length(); ++i ) { + if ( m_data[i] != ' ' ) + { + m_position = i; + return; + } + } + m_position = m_data.length(); +} + +bool ImapStreamParser::waitForMoreData( bool wait) +{ + if ( wait ) { + if ( m_socket->bytesAvailable() > 0 || + m_socket->waitForReadyRead(30000) ) { + m_data.append( m_socket->readAll() ); + } else + { + return false; + } + } + return true; +} + +void ImapStreamParser::setData( const QByteArray &data ) +{ + m_data = data; +} + +QByteArray ImapStreamParser::readRemainingData() +{ + return m_data.mid(m_position); +} + +bool ImapStreamParser::atCommandEnd() +{ + if ( !waitForMoreData( m_position >= m_data.length() ) ) + throw ImapParserException("Unable to read more data"); + int savedPos = m_position; + stripLeadingSpaces(); + if ( m_data[m_position] == '\n' || m_data[m_position] == '\r') { + if ( m_position < m_data.length() && m_data[m_position] == '\r' ) + ++m_position; + if ( m_position < m_data.length() && m_data[m_position] == '\n' ) + ++m_position; + // We'd better empty m_data from time to time before it grows out of control + m_data = m_data.right(m_data.size()-m_position); + m_position = 0; + return true; //command end + } + m_position = savedPos; + return false; //something else +} + +QByteArray ImapStreamParser::readUntilCommandEnd() +{ + QByteArray result; + int i = m_position; + int paranthesisBalance = 0; + bool inQuotedString = false; + Q_FOREVER { + if ( !waitForMoreData( m_data.length() <= i ) ) + { + m_position = i; + throw ImapParserException("Unable to read more data"); + } + if ( !inQuotedString && m_data[i] == '{' ) + { + m_position = i; + hasLiteral(); //init literal size + result.append( m_data.mid( i - 1, m_position - i ) ); + while (!atLiteralEnd()) + { + result.append( readLiteralPart() ); + } + i = m_position; + } + + if ( !inQuotedString && m_data[i] == '(' ) + paranthesisBalance++; + if ( !inQuotedString && m_data[i] == ')' ) + paranthesisBalance--; + result.append( m_data[i]); + + if ( m_data[i] == '"' ) { + if ( m_data[i - 1] != '\\' ) + inQuotedString = !inQuotedString; + } + + if ( ( i == m_data.length() && paranthesisBalance == 0 ) || m_data[i] == '\n' || m_data[i] == '\r') + break; //command end + ++i; + } + m_position = i + 1; + // We'd better empty m_data from time to time before it grows out of control + m_data = m_data.right(m_data.size()-m_position); + m_position = 0; + return result; +} + +void ImapStreamParser::sendContinuationResponse() +{ + QByteArray block = "+ Ready for literal data (expecting " + + QByteArray::number( m_continuationSize ) + " bytes)\r\n"; + m_socket->write(block); + m_socket->waitForBytesWritten(30000); + + QString identifier; + identifier.sprintf( "%p", static_cast( this ) ); + Tracer::self()->connectionOutput( identifier, QString::fromUtf8( block ) ); + +} + +void ImapStreamParser::insertData( const QByteArray& data) +{ + m_data = m_data.insert(m_position, data); +} + +void ImapStreamParser::appendData( const QByteArray& data) +{ + m_data = m_data + data; +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/imapstreamparser.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/imapstreamparser.h --- akonadi-1.1.1/server/src/imapstreamparser.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/imapstreamparser.h 2009-07-28 17:52:13.000000000 +0100 @@ -0,0 +1,251 @@ +/* + Copyright (c) 2006 - 2007 Volker Krause + Copyright (c) 2009 Andras Mantia + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_IMAPSTREAMPARSER_P_H +#define AKONADI_IMAPSTREAMPARSER_P_H + +#include "akonadiprivate_export.h" + +#include "imapset_p.h" + +#include +#include +#include +#include +#include + +#include "exception.h" + +AKONADI_EXCEPTION_MAKE_INSTANCE( ImapParserException ); + +class QIODevice; + +namespace Akonadi { + +/** + Parser for IMAP messages that operates on a local socket stream. +*/ +class AKONADIPRIVATE_EXPORT ImapStreamParser +{ + public: + /** + * Construct the parser. + * @param socket the local socket to work with. + */ + ImapStreamParser( QIODevice* socket); + + /** + * Destructor. + */ + ~ImapStreamParser(); + + /** + * Get a string from the message. If the upcoming data is not a quoted string, unquoted string or a literal, + * the behavior is undefined. Use @ref hasString to be sure a string comes. This call might block. + * @return the next string from the message as an utf8 string + */ + QString readUtf8String(); + + /** + * Same as above, but without decoding it to utf8. + * @return the next string from the message + */ + QByteArray readString(); + + /** + * Get he next IMAP sequence set. If the upcoming data is not an IMAP sequence set, + * the behavior is undefined. Use @ref hasSequenceSet to be sure a sequence set comes. This call might block. + * @return the next IMAP sequence set. + */ + ImapSet readSequenceSet(); + + /** + * Get he next parenthesized list. If the upcoming data is not a parenthesized list, + * the behavior is undefined. Use @ref hasList to be sure a string comes. This call might block. + * @return the next parenthesized list. + */ + QList readParenthesizedList(); + + /** + * Read a single character. This call might block. + * @return the read character + */ + QByteRef readChar(); + + + /** + * Get the next data as a number. This call might block. + * @param ok true if the data found was a number + * @return the number + */ + qint64 readNumber( bool * ok = 0 ); + + /** + * Check if the next data is a string or not. This call might block. + * @return true if a string follows + */ + bool hasString(); + + /** + * Check if the next data is a literal data or not. If a literal is found, the + * internal position pointer is set to the beginning of the literal data. + * This call might block. + * @return true if a literal follows + */ + bool hasLiteral(); + + /** + * Read the next literal sequence. This might or might not be the full data. Example code to read a literal would be: + * @code + * ImapStreamParser parser; + * ... + * if (parser.hasLiteral()) + * { + * while (!parser.atLiteralEnd()) + * { + * QByteArray data = parser.readLiteralPart(); + * // do something with the data + * } + * } + * @endcode + * + * This call might block. + * + * @return part of a literal data + */ + QByteArray readLiteralPart(); + + /** + * Check if the literal data end was reached. See @ref hasLiteral and @ref readLiteralPart . + * @return true if the literal was completely read. + */ + bool atLiteralEnd() const; + + /** + * Get the amount of data that needs to be read for the last literal. If this is called right after hasLiteral, the actual size of the literal data + * is returned. + * @return the remaining literal size + */ + qint64 remainingLiteralSize(); + + /** + * Check if the next data is an IMAP sequence set. This call might block. + * @return true if an IMAP sequence set comes. + */ + bool hasSequenceSet(); + + /** + * Check if the next data is a parenthesized list. This call might block. + * @return true if a parenthesized list comes. + */ + bool hasList(); + + /** + * Begin reading a parenthesized list. This call might block. + * This call will throw an exception if the parser is not at a beginning of a list, + * that is hasList() returns false. + * @see hasList(), atListEnd + */ + void beginList(); + + /** + * Check if the next data is a parenthesized list end. This call might block. + * @return true if a parenthesized list end. In this case the closing parenthesis + * is read from the stream. + */ + bool atListEnd(); + + /** + * Read a date/time. + * @return the date and time or a null QDateTime, if no valid date/time was found + */ + QDateTime readDateTime(); + + /** + * Check if the next element in the data stream is a date or not. + * @return true, if a valid date follows + */ + bool hasDateTime(); + + + /** + * Check if the command end was reached + * @return true if the end of command is reached + */ + bool atCommandEnd(); + + /** + * Return everything that remained from the command. + * @return the remaining command data + */ + QByteArray readUntilCommandEnd(); + + /** + * Return all the data that was read from the socket, but not processed yet. + * @return the remaining unprocessed data + */ + QByteArray readRemainingData(); + + void setData( const QByteArray &data ); + + /** + * Inserts some data back into the parse buffer at the current position. + * @param data data to be inserted + */ + void insertData( const QByteArray &data ); + + /** + * Appends some data to the end of the parse buffer. + * @param data data to be appended + */ + void appendData( const QByteArray &data ); + + /** + * Skips everything until the first character that isn't a space. + */ + void stripLeadingSpaces(); + + private: + QByteArray parseQuotedString(); + + /** + * If the condition is true, wait for more data to be available from the socket. + * If no data comes after a timeout (30000ms), it aborts and returns false. + * @param wait the condition + * @return true if more data is available + */ + bool waitForMoreData( bool wait); + + /** + * Inform the client to send more literal data. + */ + void sendContinuationResponse(); + + QIODevice *m_socket; + QByteArray m_data; + QByteArray m_tag; + int m_position; + qint64 m_literalSize; + qint64 m_continuationSize; +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/intervalcheck.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/intervalcheck.cpp --- akonadi-1.1.1/server/src/intervalcheck.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/intervalcheck.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -19,6 +19,7 @@ #include "intervalcheck.h" #include "storage/datastore.h" +#include "storage/itemretrievalmanager.h" #include #include @@ -45,23 +46,23 @@ void IntervalCheck::doIntervalCheck() { - // cycle over all locations - QList locations = Location::retrieveAll(); - foreach ( Location location, locations ) { + // cycle over all collections + QList collections = Collection::retrieveAll(); + foreach ( Collection collection, collections ) { // determine active cache policy - DataStore::self()->activeCachePolicy( location ); + DataStore::self()->activeCachePolicy( collection ); // check if there is something to expire at all - if ( location.cachePolicyCheckInterval() < 0 || !location.subscribed() ) + if ( collection.cachePolicyCheckInterval() < 0 || !collection.subscribed() ) continue; - QDateTime lastExpectedCheck = QDateTime::currentDateTime().addSecs( location.cachePolicyCheckInterval() * -60 ); - if ( mLastChecks.contains( location.id() ) && mLastChecks.value( location.id() ) > lastExpectedCheck ) + QDateTime lastExpectedCheck = QDateTime::currentDateTime().addSecs( collection.cachePolicyCheckInterval() * -60 ); + if ( mLastChecks.contains( collection.id() ) && mLastChecks.value( collection.id() ) > lastExpectedCheck ) continue; - mLastChecks[ location.id() ] = QDateTime::currentDateTime(); - qDebug() << "interval checking collection " << location.id() << "(" << location.name() << ")"; - DataStore::self()->triggerCollectionSync( location ); + mLastChecks[ collection.id() ] = QDateTime::currentDateTime(); + qDebug() << "interval checking collection " << collection.id() << "(" << collection.name() << ")"; + ItemRetrievalManager::instance()->requestCollectionSync( collection ); } QTimer::singleShot( 60 * 1000, this, SLOT(doIntervalCheck()) ); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/main.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/main.cpp --- akonadi-1.1.1/server/src/main.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/main.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -1,6 +1,5 @@ /*************************************************************************** - * Copyright (C) 2006 by Till Adam * - * adam@kde.org * + * Copyright (C) 2006 by Till Adam * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * @@ -15,7 +14,7 @@ * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/nepomukmanager.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/nepomukmanager.cpp --- akonadi-1.1.1/server/src/nepomukmanager.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/nepomukmanager.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -22,20 +22,17 @@ #include "nepomukmanager.h" -#include "searchqueryiteratorinterface.h" +#include "search/result.h" +#include "search/query.h" #include "entities.h" using namespace Akonadi; -static qint64 uriToItemId( const QString &uri ) +static qint64 uriToItemId( const QUrl &url ) { - if ( uri.isEmpty() ) - return -1; - bool ok = false; - const QUrl url( uri ); const qint64 id = url.queryItemValue( QLatin1String( "item" ) ).toLongLong( &ok ); if ( !ok ) @@ -51,94 +48,63 @@ Q_ASSERT( mInstance == 0 ); mInstance = this; - mSearchInterface = new org::freedesktop::Akonadi::Search( QLatin1String( "org.freedesktop.Akonadi.Search" ), QLatin1String( "/Search" ), - QDBusConnection::sessionBus(), this ); - - if ( !mSearchInterface->isValid() ) { - qWarning() << "Nepomuk QueryServer interface not found!"; + if ( !Nepomuk::Search::QueryServiceClient::serviceAvailable() ) { + qWarning() << "Nepomuk QueryServer interface not available!"; mValid = false; + } else { + reloadSearches(); } } NepomukManager::~NepomukManager() { - stopSearches(); + if ( mValid ) + stopSearches(); } -bool NepomukManager::addSearch( const Location &location ) +bool NepomukManager::addSearch( const Collection &collection ) { - if ( !mSearchInterface->isValid() || !mValid ) + if ( !mValid ) return false; QMutexLocker lock( &mMutex ); - if ( location.remoteId().isEmpty() ) + if ( collection.remoteId().isEmpty() ) return false; // TODO: search statement is stored in remoteId currently, port to attributes! - const QString searchStatement = location.remoteId(); - - const QString queryPath = mSearchInterface->createQuery( searchStatement ); - org::freedesktop::Akonadi::SearchQuery *query = - new org::freedesktop::Akonadi::SearchQuery( QLatin1String( "org.freedesktop.Akonadi.Search" ), - queryPath, QDBusConnection::sessionBus(), 0 ); - - if ( !query->isValid() ) { - qWarning() << "Nepomuk QueryServer: Query could not be instantiated!"; - return false; - } - - connect( query, SIGNAL( hitsChanged( const QStringList& ) ), - this, SLOT( hitsAdded( const QStringList& ) ) ); - connect( query, SIGNAL( hitsRemoved( const QStringList& ) ), - this, SLOT( hitsRemoved( const QStringList& ) ) ); - - mQueryMap.insert( query, location.id() ); - mQueryInvMap.insert( location.id(), query ); // needed for fast lookup in removeSearch() - - // load all hits that are currently available... - const QString iteratorPath = query->allHits(); - - org::freedesktop::Akonadi::SearchQueryIterator *iterator = - new org::freedesktop::Akonadi::SearchQueryIterator( QLatin1String( "org.freedesktop.Akonadi.Search" ), iteratorPath, - QDBusConnection::sessionBus(), 0 ); - - while ( iterator->next() ) { - const QString uri = iterator->currentUri(); - - const qint64 itemId = uriToItemId( uri ); - if ( itemId == -1 ) { - qWarning() << "Nepomuk QueryServer: Retrieved invalid item id from server!"; - continue; - } + const QString searchStatement = collection.remoteId(); - Entity::addToRelation( location.id(), itemId ); - } + Nepomuk::Search::QueryServiceClient *query = new Nepomuk::Search::QueryServiceClient( this ); - iterator->close(); + connect( query, SIGNAL( newEntries( const QList& ) ), + this, SLOT( hitsAdded( const QList& ) ) ); + connect( query, SIGNAL( entriesRemoved( const QList& ) ), + this, SLOT( hitsRemoved( const QList& ) ) ); - // ... and start the change monitoring for future changes - query->start(); + mQueryMap.insert( query, collection.id() ); + mQueryInvMap.insert( collection.id(), query ); // needed for fast lookup in removeSearch() - qDebug( "--------------- added search for loc %lld", location.id() ); + // query with SPARQL statement + query->query( Nepomuk::Search::Query( searchStatement ) ); return true; } -bool NepomukManager::removeSearch( qint64 locationId ) +bool NepomukManager::removeSearch( qint64 collectionId ) { - org::freedesktop::Akonadi::SearchQuery *query = mQueryInvMap.value( locationId ); - if ( !query || !query->isValid() ) { + Nepomuk::Search::QueryServiceClient *query = mQueryInvMap.value( collectionId ); + if ( !query ) { qWarning() << "Nepomuk QueryServer: Query could not be removed!"; + return false; } else { - query->stop(); query->close(); } // cleanup mappings - mQueryInvMap.remove( locationId ); + mQueryInvMap.remove( collectionId ); mQueryMap.remove( query ); - qDebug( "--------------- removed search for loc %lld", locationId ); + query->deleteLater(); return true; } @@ -151,9 +117,9 @@ return; } - const Location::List locations = resource.locations(); - Q_FOREACH ( const Location &location, locations ) { - addSearch( location ); + const Collection::List collections = resource.collections(); + Q_FOREACH ( const Collection &collection, collections ) { + addSearch( collection ); } } @@ -165,57 +131,49 @@ return; } - const Location::List locations = resource.locations(); - Q_FOREACH ( const Location &location, locations ) { - removeSearch( location.id() ); + const Collection::List collections = resource.collections(); + Q_FOREACH ( const Collection &collection, collections ) { + removeSearch( collection.id() ); } } -void NepomukManager::hitsAdded( const QStringList &hits ) +void NepomukManager::hitsAdded( const QList& entries ) { - qDebug( "--------------- hits added 1" ); - - org::freedesktop::Akonadi::SearchQuery *query = qobject_cast( sender() ); + Nepomuk::Search::QueryServiceClient *query = qobject_cast( sender() ); if ( !query ) { qWarning() << "Nepomuk QueryServer: Got signal from non-existing search query!"; return; } - qDebug( "--------------- hits added 2" ); - mMutex.lock(); - qint64 locationId = mQueryMap.value( query ); + qint64 collectionId = mQueryMap.value( query ); mMutex.unlock(); - Q_FOREACH( const QString &uri, hits ) { - const qint64 itemId = uriToItemId( uri ); + Q_FOREACH( const Nepomuk::Search::Result &result, entries ) { + const qint64 itemId = uriToItemId( result.resourceUri() ); if ( itemId == -1 ) { qWarning() << "Nepomuk QueryServer: Retrieved invalid item id from server!"; continue; } - Entity::addToRelation( locationId, itemId ); + Entity::addToRelation( collectionId, itemId ); } } -void NepomukManager::hitsRemoved( const QStringList &hits ) +void NepomukManager::hitsRemoved( const QList &entries ) { - qDebug( "--------------- hits removed 1" ); - - org::freedesktop::Akonadi::SearchQuery *query = qobject_cast( sender() ); + Nepomuk::Search::QueryServiceClient *query = qobject_cast( sender() ); if ( !query ) { qWarning() << "Nepomuk QueryServer: Got signal from non-existing search query!"; return; } - qDebug( "--------------- hits removed 2" ); - mMutex.lock(); - qint64 locationId = mQueryMap.value( query ); + qint64 collectionId = mQueryMap.value( query ); mMutex.unlock(); - Q_FOREACH( const QString &uri, hits ) { + Q_FOREACH( const QUrl &uri, entries ) { const qint64 itemId = uriToItemId( uri ); if ( itemId == -1 ) { @@ -223,7 +181,7 @@ continue; } - Entity::removeFromRelation( locationId, itemId ); + Entity::removeFromRelation( collectionId, itemId ); } } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/nepomukmanager.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/nepomukmanager.h --- akonadi-1.1.1/server/src/nepomukmanager.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/nepomukmanager.h 2009-07-28 17:52:13.000000000 +0100 @@ -26,8 +26,7 @@ #include "abstractsearchmanager.h" -#include "searchinterface.h" -#include "searchqueryinterface.h" +#include "search/queryserviceclient.h" namespace Akonadi { @@ -39,25 +38,23 @@ NepomukManager( QObject* parent = 0 ); ~NepomukManager(); - bool addSearch( const Location &location ); - bool removeSearch( qint64 location ); + bool addSearch( const Collection &collection ); + bool removeSearch( qint64 collection ); private: void reloadSearches(); void stopSearches(); private Q_SLOTS: - void hitsAdded( const QStringList &hits ); - void hitsRemoved( const QStringList &hits ); + void hitsAdded( const QList& entries ); + void hitsRemoved( const QList &entries ); private: bool mValid; QMutex mMutex; - QHash mQueryMap; - QHash mQueryInvMap; - - org::freedesktop::Akonadi::Search *mSearchInterface; + QHash mQueryMap; + QHash mQueryInvMap; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/resourcemanager.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/resourcemanager.cpp --- akonadi-1.1.1/server/src/resourcemanager.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/resourcemanager.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -20,6 +20,7 @@ #include "resourcemanager.h" #include "tracer.h" #include "storage/datastore.h" +#include "resourcemanageradaptor.h" #include @@ -30,47 +31,44 @@ Akonadi::ResourceManager::ResourceManager(QObject * parent) : QObject( parent ) { - mManager = new org::freedesktop::Akonadi::AgentManager( QLatin1String("org.freedesktop.Akonadi.Control"), - QLatin1String("/AgentManager"), QDBusConnection::sessionBus(), this ); - - connect( mManager, SIGNAL(agentInstanceAdded(const QString&)), - SLOT(resourceAdded(const QString&)) ); - connect( mManager, SIGNAL(agentInstanceRemoved(const QString&)), - SLOT(resourceRemoved(const QString& )) ); + new ResourceManagerAdaptor( this ); + QDBusConnection::sessionBus().registerObject( QLatin1String("/ResourceManager"), this ); } -void Akonadi::ResourceManager::resourceAdded(const QString & name) +bool Akonadi::ResourceManager::addResourceInstance(const QString & name) { DataStore *db = DataStore::self(); Resource resource = Resource::retrieveByName( name ); if ( resource.isValid() ) - return; // resource already exists + return false; // resource already exists // create the resource resource.setName( name ); if ( !resource.insert() ) { Tracer::self()->error( "ResourceManager", QString::fromLatin1("Could not create resource '%1'.").arg(name) ); delete db; - return; + return false; } resource = Resource::retrieveByName( name ); + return true; } -void Akonadi::ResourceManager::resourceRemoved(const QString & name) +bool Akonadi::ResourceManager::removeResourceInstance(const QString & name) { DataStore *db = DataStore::self(); // remove items and collections Resource resource = Resource::retrieveByName( name ); if ( resource.isValid() ) { - QList locations = resource.locations(); - foreach ( Location location, locations ) - db->cleanupLocation( location ); + QList collections = resource.collections(); + foreach ( Collection collection, collections ) + db->cleanupCollection( collection ); // remove resource resource.remove(); } + return true; } ResourceManager * Akonadi::ResourceManager::self() diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/resourcemanager.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/resourcemanager.h --- akonadi-1.1.1/server/src/resourcemanager.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/resourcemanager.h 2009-07-28 17:52:13.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -40,9 +40,9 @@ private: ResourceManager( QObject *parent = 0 ); - private Q_SLOTS: - void resourceAdded( const QString &name ); - void resourceRemoved( const QString &name ); + public Q_SLOTS: + bool addResourceInstance( const QString &name ); + bool removeResourceInstance( const QString &name ); private: static ResourceManager* mSelf; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/dbusoperators.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/dbusoperators.cpp --- akonadi-1.1.1/server/src/search/dbusoperators.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/dbusoperators.cpp 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,345 @@ +/* + Copyright (c) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "dbusoperators.h" + +#include + + +Q_DECLARE_METATYPE(Nepomuk::Search::Result) +Q_DECLARE_METATYPE(Nepomuk::Search::Term) +Q_DECLARE_METATYPE(Nepomuk::Search::Query) +Q_DECLARE_METATYPE(Soprano::Node) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) + + +void Nepomuk::Search::registerDBusTypes() +{ + qDBusRegisterMetaType(); + qDBusRegisterMetaType >(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); +} + + +QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Result& result ) +{ + // + // Signature: (sda{s(isss)}) + // + + arg.beginStructure(); + + arg << QString::fromAscii( result.resourceUri().toEncoded() ) << result.score(); + + arg.beginMap( QVariant::String, qMetaTypeId() ); + + QHash rp = result.requestProperties(); + for ( QHash::const_iterator it = rp.constBegin(); it != rp.constEnd(); ++it ) { + arg.beginMapEntry(); + arg << QString::fromAscii( it.key().toEncoded() ) << it.value(); + arg.endMapEntry(); + } + + arg.endMap(); + + arg.endStructure(); + + return arg; +} + + +const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Result& result ) +{ + // + // Signature: (sda{s(isss)}) + // + + arg.beginStructure(); + QString uri; + double score = 0.0; + + arg >> uri >> score; + result = Nepomuk::Search::Result( QUrl::fromEncoded( uri.toAscii() ), score ); + + arg.beginMap(); + while ( !arg.atEnd() ) { + QString rs; + Soprano::Node node; + arg.beginMapEntry(); + arg >> rs >> node; + arg.endMapEntry(); + result.addRequestProperty( QUrl::fromEncoded( rs.toAscii() ), node ); + } + arg.endMap(); + + arg.endStructure(); + + return arg; +} + + +QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Term& term ) +{ + // + // Signature: (ii(isss)sss) + // i -> type + // i -> comparator type + // (isss) -> Soprano::LiteralValue encoded as a Soprano::Node for simplicity + // s -> resource + // s -> field + // s -> property + // + + arg.beginStructure(); + arg << ( int )term.type() + << ( int )term.comparator() + << Soprano::Node( term.value() ) + << QString::fromAscii( term.resource().toEncoded() ) + << term.field() + << QString::fromAscii( term.property().toEncoded() ); + arg.endStructure(); + + return arg; +} + + +const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Term& term ) +{ + // + // Signature: (ii(isss)sss) + // i -> type + // i -> comparator type + // (isss) -> Soprano::LiteralValue encoded as a Soprano::Node for simplicity + // s -> resource + // s -> field + // s -> property + // + + arg.beginStructure(); + int type = Nepomuk::Search::Term::InvalidTerm; + int comparator = Nepomuk::Search::Term::Equal; + Soprano::Node valueNode; + QString resource, field, property; + arg >> type + >> comparator + >> valueNode + >> resource + >> field + >> property; + term.setType( Nepomuk::Search::Term::Type( type ) ); + term.setComparator( Nepomuk::Search::Term::Comparator( comparator ) ); + if ( valueNode.isLiteral() ) + term.setValue( valueNode.literal() ); + if ( !resource.isEmpty() ) + term.setResource( QUrl::fromEncoded( resource.toAscii() ) ); + if ( !field.isEmpty() ) + term.setField( field ); + if ( !property.isEmpty() ) + term.setProperty( QUrl::fromEncoded( property.toAscii() ) ); + arg.endStructure(); + + return arg; +} + + +// streaming a Query object is a bit tricky as it is a set of nested Term objects +// DBus does not allow arbitrary nesting of objects, thus we use a little trick: +// We store all used Term objects in a list and use integer indices pointing into +// this list to describe the nesting within the Term objects. This also means that +// a Term's subTerm list is replaced with a list of indices +namespace { + /** + * Build term relations for the last term in the list + */ + void buildTermRelations( QList& terms, QHash >& termRelations ) { + QList subTerms = terms.last().subTerms(); + int termIndex = terms.count()-1; + for ( int i = 0; i < subTerms.count(); ++i ) { + terms.append( subTerms[i] ); + termRelations[termIndex].append( terms.count()-1 ); + buildTermRelations( terms, termRelations ); + } + } +} + +QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Query& query ) +{ + // + // Signature: (isa(ii(isss)sss)a{iai}ia{sb}) + // i -> type + // s -> sparql query + // a(ii(isss)sss) -> array of terms (first is root term) + // a{iai} -> hash of term relations + // i -> limit + // a{sb} -> request properties + // + + arg.beginStructure(); + + arg << ( int )query.type() << query.sparqlQuery(); + + QList terms; + QHash > termRelations; + if ( query.type() == Nepomuk::Search::Query::PlainQuery ) { + terms.append( query.term() ); + buildTermRelations( terms, termRelations ); + } + arg << terms; + + arg.beginMap( QVariant::Int, qMetaTypeId >() ); + for( QHash >::const_iterator it = termRelations.constBegin(); + it != termRelations.constEnd(); ++it ) { + arg.beginMapEntry(); + arg << it.key() << it.value(); + arg.endMapEntry(); + } + arg.endMap(); + arg << query.limit(); + + arg.beginMap( QVariant::String, QVariant::Bool ); + QList requestProperties = query.requestProperties(); + foreach( const Nepomuk::Search::Query::RequestProperty& rp, requestProperties ) { + arg.beginMapEntry(); + arg << QString::fromAscii( rp.first.toEncoded() ) << rp.second; + arg.endMapEntry(); + } + arg.endMap(); + + arg.endStructure(); + + return arg; +} + + +namespace { + Nepomuk::Search::Term rebuildTermFromTermList( const QList& terms, + const QHash >& termRelations, + int index = 0 ) { + Nepomuk::Search::Term root = terms[index]; + foreach( int i, termRelations[index] ) { + root.addSubTerm( rebuildTermFromTermList( terms, termRelations, i ) ); + } + return root; + } +} + +const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Query& query ) +{ + // + // Signature: (isa(ii(isss)sss)a{iai}ia{sb}) + // i -> type + // s -> sparql query + // a(ii(isss)sss) -> array of terms (first is root term) + // a{iai} -> hash of term relations + // i -> limit + // a{sb} -> request properties + // + + arg.beginStructure(); + + int type = Nepomuk::Search::Query::InvalidQuery; + QString sparqlQuery; + QList terms; + QHash > termRelations; + int limit = 0; + + arg >> type + >> sparqlQuery + >> terms; + + arg.beginMap(); + while ( !arg.atEnd() ) { + int termIndex = 0; + QList indices; + arg.beginMapEntry(); + arg >> termIndex >> indices; + arg.endMapEntry(); + termRelations.insert( termIndex, indices ); + } + arg.endMap(); + + arg >> limit; + + arg.beginMap(); + while ( !arg.atEnd() ) { + QString prop; + bool optional = true; + arg.beginMapEntry(); + arg >> prop >> optional; + arg.endMapEntry(); + query.addRequestProperty( QUrl::fromEncoded( prop.toAscii() ), optional ); + } + arg.endMap(); + + arg.endStructure(); + + if ( Nepomuk::Search::Query::Type( type ) == Nepomuk::Search::Query::PlainQuery ) { + query.setTerm( rebuildTermFromTermList( terms, termRelations ) ); + } + else { + query.setSparqlQuery( sparqlQuery ); + } + query.setLimit( limit ); + + return arg; +} + + +QDBusArgument& operator<<( QDBusArgument& arg, const Soprano::Node& node ) +{ + arg.beginStructure(); + arg << ( int )node.type(); + if ( node.type() == Soprano::Node::ResourceNode ) { + arg << QString::fromAscii( node.uri().toEncoded() ); + } + else { + arg << node.toString(); + } + arg << node.language() << node.dataType().toString(); + arg.endStructure(); + return arg; +} + + +const QDBusArgument& operator>>( const QDBusArgument& arg, Soprano::Node& node ) +{ + // + // Signature: (isss) + // + arg.beginStructure(); + int type; + QString value, language, dataTypeUri; + arg >> type >> value >> language >> dataTypeUri; + if ( type == Soprano::Node::LiteralNode ) { + node = Soprano::Node( Soprano::LiteralValue::fromString( value, dataTypeUri ), language ); + } + else if ( type == Soprano::Node::ResourceNode ) { + node = Soprano::Node( QUrl::fromEncoded( value.toAscii() ) ); + } + else if ( type == Soprano::Node::BlankNode ) { + node = Soprano::Node( value ); + } + else { + node = Soprano::Node(); + } + arg.endStructure(); + return arg; +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/dbusoperators.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/dbusoperators.h --- akonadi-1.1.1/server/src/search/dbusoperators.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/dbusoperators.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,46 @@ +/* + Copyright (c) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _NEPOMUK_SEARCH_DBUS_OPERATORS_H_ +#define _NEPOMUK_SEARCH_DBUS_OPERATORS_H_ + +#include + +#include "result.h" +#include "query.h" +#include "term.h" + +namespace Nepomuk { + namespace Search { + void registerDBusTypes(); + } +} + +QDBusArgument& operator<<( QDBusArgument& arg, const Soprano::Node& ); +const QDBusArgument& operator>>( const QDBusArgument& arg, Soprano::Node& ); + +QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Term& term ); +const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Term& ); + +QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Query& query ); +const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Query& ); + +QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Result& ); +const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Result& ); + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/org.kde.nepomuk.QueryService.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/org.kde.nepomuk.QueryService.xml --- akonadi-1.1.1/server/src/search/org.kde.nepomuk.QueryService.xml 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/org.kde.nepomuk.QueryService.xml 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/org.kde.nepomuk.Query.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/org.kde.nepomuk.Query.xml --- akonadi-1.1.1/server/src/search/org.kde.nepomuk.Query.xml 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/org.kde.nepomuk.Query.xml 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/query.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/query.cpp --- akonadi-1.1.1/server/src/search/query.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/query.cpp 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,195 @@ +/* + This file is part of the Nepomuk KDE project. + Copyright (C) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "query.h" +#include "term.h" + +#include +#include + + +class Nepomuk::Search::Query::Private : public QSharedData +{ +public: + Private() + : type( InvalidQuery ), + limit( 0 ) { + } + + Type type; + Term term; + QString sparqlQuery; + int limit; + + QList requestProperties; +}; + + +Nepomuk::Search::Query::Query() + : d( new Private() ) +{ +} + + +Nepomuk::Search::Query::Query( const Query& other ) +{ + d = other.d; +} + + +Nepomuk::Search::Query::Query( const Term& term ) + : d ( new Private() ) +{ + d->type = PlainQuery; + d->term = term; +} + + +Nepomuk::Search::Query::Query( const QString& sparqlQuery ) + : d ( new Private() ) +{ + d->type = SPARQLQuery; + d->sparqlQuery = sparqlQuery; +} + + +Nepomuk::Search::Query::~Query() +{ +} + + +Nepomuk::Search::Query& Nepomuk::Search::Query::operator=( const Query& other ) +{ + d = other.d; + return *this; +} + + +Nepomuk::Search::Query::Type Nepomuk::Search::Query::type() const +{ + return d->type; +} + + +Nepomuk::Search::Term Nepomuk::Search::Query::term() const +{ + return d->term; +} + + +int Nepomuk::Search::Query::limit() const +{ + return d->limit; +} + + +QString Nepomuk::Search::Query::sparqlQuery() const +{ + return d->sparqlQuery; +} + + +void Nepomuk::Search::Query::setTerm( const Term& term ) +{ + d->term = term; + d->type = PlainQuery; +} + + +void Nepomuk::Search::Query::setLimit( int limit ) +{ + d->limit = limit; +} + + +void Nepomuk::Search::Query::setSparqlQuery( const QString& qs ) +{ + d->sparqlQuery = qs; + d->term = Term(); + d->type = SPARQLQuery; +} + + +void Nepomuk::Search::Query::addRequestProperty( const QUrl& property, bool optional ) +{ + d->requestProperties.append( qMakePair( property, optional ) ); +} + + +void Nepomuk::Search::Query::clearRequestProperties() +{ + d->requestProperties.clear(); +} + + +QList Nepomuk::Search::Query::requestProperties() const +{ + return d->requestProperties; +} + + +namespace { + bool compareRequestProperties( const QList& rp1, const QList& rp2 ) { + // brute force + foreach( const Nepomuk::Search::Query::RequestProperty& rp, rp1 ) { + if ( !rp2.contains( rp ) ) { + return false; + } + } + foreach( const Nepomuk::Search::Query::RequestProperty& rp, rp2 ) { + if ( !rp1.contains( rp ) ) { + return false; + } + } + return true; + } +} + +bool Nepomuk::Search::Query::operator==( const Query& other ) const +{ + if ( d->type == other.d->type && + d->limit == other.d->limit ) { + if ( d->type == SPARQLQuery ) { + return( d->sparqlQuery == other.d->sparqlQuery && + compareRequestProperties( d->requestProperties, other.d->requestProperties ) ); + } + else { + return( d->term == other.d->term && + compareRequestProperties( d->requestProperties, other.d->requestProperties ) ); + } + } + + return false; +} + + +QDebug operator<<( QDebug dbg, const Nepomuk::Search::Query& query ) +{ + dbg << "(Query" << query.term() << query.limit() << ")"; + return dbg; +} + + +uint Nepomuk::Search::qHash( const Nepomuk::Search::Query& query ) +{ + if ( query.type() == Nepomuk::Search::Query::SPARQLQuery ) + return qHash( query.sparqlQuery() ); + else + return qHash( query.term() ); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/query.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/query.h --- akonadi-1.1.1/server/src/search/query.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/query.h 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,120 @@ +/* + This file is part of the Nepomuk KDE project. + Copyright (C) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef _NEPOMUK_SEARCH_QUERY_H_ +#define _NEPOMUK_SEARCH_QUERY_H_ + +#include +#include +#include +#include + +class QUrl; + +namespace Nepomuk { + namespace Search { + + class Term; + + /** + * \class Query query.h nepomuk/query.h + * + * \brief A Nepomuk desktop query. + * + * A query can either be based on Term or a more complex + * SPARQL query. + * + * \author Sebastian Trueg + */ + class Query + { + public: + enum Type { + InvalidQuery, + PlainQuery, + SPARQLQuery + }; + + /** + * Create an empty invalid query object. + */ + Query(); + + /** + * Create a query of type PlainQuery based on + * \a term. + */ + Query( const Term& term ); + + /** + * Create a SPARQL query. The query has to have one select variable called "?r" + */ + explicit Query( const QString& sparqlQuery ); + + /** + * Copy constructor. + */ + Query( const Query& ); + + /** + * Destructor + */ + ~Query(); + + /** + * Assignment operator + */ + Query& operator=( const Query& ); + + Type type() const; + Term term() const; + QString sparqlQuery() const; + int limit() const; + + void setTerm( const Term& ); + void setSparqlQuery( const QString& ); + void setLimit( int ); + + /** + * Add a property that should be reported with each search result. + * \param property The requested property. + * \param optional If \p true the property is optional, meaning it can + * be empty ins earch results. + */ + void addRequestProperty( const QUrl& property, bool optional = true ); + void clearRequestProperties(); + + typedef QPair RequestProperty; + + QList requestProperties() const; + + bool operator==( const Query& ) const; + + private: + class Private; + QSharedDataPointer d; + }; + + uint qHash( const Nepomuk::Search::Query& ); + } +} + +QDebug operator<<( QDebug, const Nepomuk::Search::Query& ); + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/querymetatype.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/querymetatype.h --- akonadi-1.1.1/server/src/search/querymetatype.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/querymetatype.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,31 @@ +/* + Copyright (C) 2008-2009 by Sebastian Trueg + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This file looks very weird but is the only way to get the Query metatype + * into the service interface when generated via qdbusxml2cpp. + */ + +#ifndef _NEPOMUK_QUERY_META_TYPE_H_ +#define _NEPOMUK_QUERY_META_TYPE_H_ + +#include "query.h" + +Q_DECLARE_METATYPE(Nepomuk::Search::Query) + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/queryserviceclient.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/queryserviceclient.cpp --- akonadi-1.1.1/server/src/search/queryserviceclient.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/queryserviceclient.cpp 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,228 @@ +/* + Copyright (c) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "queryserviceclient.h" +#include "dbusoperators.h" +#include "result.h" +#include "query.h" +#include "queryserviceinterface.h" +#include "queryinterface.h" + +#include +#include +#include +#include + +#include +#include + +namespace { + /** + * Each thread needs its own QDBusConnection. We do it the very easy + * way and just create a new connection for each client + * Why does Qt not handle this automatically? + */ + class QDBusConnectionPerThreadHelper + { + public: + QDBusConnectionPerThreadHelper() + : m_counter( 0 ) { + } + + QDBusConnection newConnection() { + QMutexLocker lock( &m_mutex ); + return QDBusConnection::connectToBus( QDBusConnection::SessionBus, + QString::fromLatin1("NepomukQueryServiceConnection%1").arg(++m_counter) ); + } + + private: + int m_counter; + QMutex m_mutex; + }; + + Q_GLOBAL_STATIC(QDBusConnectionPerThreadHelper, s_globalDBusConnectionPerThreadHelper) +} + + +class Nepomuk::Search::QueryServiceClient::Private +{ +public: + Private() + : queryServiceInterface( 0 ), + queryInterface( 0 ), + dbusConnection( s_globalDBusConnectionPerThreadHelper()->newConnection() ), + loop( 0 ) { + } + + void _k_entriesRemoved( const QStringList& ); + void _k_finishedListing(); + bool handleQueryReply( QDBusReply reply ); + + org::kde::nepomuk::QueryService* queryServiceInterface; + org::kde::nepomuk::Query* queryInterface; + + QueryServiceClient* q; + + QDBusConnection dbusConnection; + + QEventLoop* loop; +}; + + +void Nepomuk::Search::QueryServiceClient::Private::_k_entriesRemoved( const QStringList& uris ) +{ + QList ul; + foreach( const QString& s, uris ) { + ul.append( QUrl( s ) ); + } + emit q->entriesRemoved( ul ); +} + + +void Nepomuk::Search::QueryServiceClient::Private::_k_finishedListing() +{ + emit q->finishedListing(); + if( loop ) { + q->close(); + } +} + + +bool Nepomuk::Search::QueryServiceClient::Private::handleQueryReply( QDBusReply r ) +{ + if ( r.isValid() ) { + queryInterface = new org::kde::nepomuk::Query( queryServiceInterface->service(), + r.value().path(), + dbusConnection ); + connect( queryInterface, SIGNAL( newEntries( QList ) ), + q, SIGNAL( newEntries( QList ) ) ); + connect( queryInterface, SIGNAL( entriesRemoved( QStringList ) ), + q, SLOT( _k_entriesRemoved( QStringList ) ) ); + connect( queryInterface, SIGNAL( finishedListing() ), + q, SLOT( _k_finishedListing() ) ); + // run the listing async in case the event loop below is the only one we have + // and we need it to handle the signals and list returns results immediately + QTimer::singleShot( 0, queryInterface, SLOT(list()) ); + return true; + } + else { + qDebug() << "Query failed:" << r.error().message(); + return false; + } +} + + +Nepomuk::Search::QueryServiceClient::QueryServiceClient( QObject* parent ) + : QObject( parent ), + d( new Private() ) +{ + d->q = this; + + Nepomuk::Search::registerDBusTypes(); + + // we use our own connection to be thread-safe + d->queryServiceInterface = new org::kde::nepomuk::QueryService( QLatin1String("org.kde.nepomuk.services.nepomukqueryservice"), + QLatin1String("/nepomukqueryservice"), + d->dbusConnection ); +} + + +Nepomuk::Search::QueryServiceClient::~QueryServiceClient() +{ + close(); + delete d; +} + + +bool Nepomuk::Search::QueryServiceClient::query( const QString& query ) +{ + close(); + + if ( d->queryServiceInterface->isValid() ) { + return d->handleQueryReply( d->queryServiceInterface->query( query, QStringList() ) ); + } + else { + qDebug() << "Could not contact query service."; + return false; + } +} + + +bool Nepomuk::Search::QueryServiceClient::query( const Query& query ) +{ + close(); + + if ( d->queryServiceInterface->isValid() ) { + return d->handleQueryReply( d->queryServiceInterface->query( query ) ); + } + else { + qDebug() << "Could not contact query service."; + return false; + } +} + + +bool Nepomuk::Search::QueryServiceClient::blockingQuery( const QString& q ) +{ + if( query( q ) ) { + QEventLoop loop; + d->loop = &loop; + loop.exec(); + d->loop = 0; + return true; + } + else { + return false; + } +} + + +bool Nepomuk::Search::QueryServiceClient::blockingQuery( const Query& q ) +{ + if( query( q ) ) { + QEventLoop loop; + d->loop = &loop; + loop.exec(); + d->loop = 0; + return true; + } + else { + return false; + } +} + + +void Nepomuk::Search::QueryServiceClient::close() +{ + if ( d->queryInterface ) { + qDebug(); + d->queryInterface->close(); + delete d->queryInterface; + d->queryInterface = 0; + if( d->loop ) + d->loop->exit(); + } +} + + +bool Nepomuk::Search::QueryServiceClient::serviceAvailable() +{ + return QDBusConnection::sessionBus().interface()->isServiceRegistered( QLatin1String("org.kde.nepomuk.services.nepomukqueryservice") ); +} + +#include "queryserviceclient.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/queryserviceclient.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/queryserviceclient.h --- akonadi-1.1.1/server/src/search/queryserviceclient.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/queryserviceclient.h 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,159 @@ +/* + Copyright (c) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _NEPOMUK_QUERY_SERVICE_CLIENT_H_ +#define _NEPOMUK_QUERY_SERVICE_CLIENT_H_ + +#include + +class QUrl; + +namespace Nepomuk { + namespace Search { + + class Result; + class Query; + + /** + * \class QueryServiceClient queryserviceclient.h Nepomuk/Search/QueryServiceClient + * + * \brief Convenience frontend to the %Nepomuk Query DBus Service + * + * The QueryServiceClient provides an easy way to access the %Nepomuk Query Service + * without having to deal with any communication details. By default it monitors + * queries for changes. + * + * Usage is simple: Create an instance of the client for each search you want to + * track. Once instance may also be reused for subsequent queries if further updates + * of the persistent query are not necessary. + * + * \author Sebastian Trueg + */ + class QueryServiceClient : public QObject + { + Q_OBJECT + + public: + /** + * Create a new QueryServiceClient instance. + */ + QueryServiceClient( QObject* parent = 0 ); + + /** + * Desctructor. Closes the query. + */ + ~QueryServiceClient(); + + /** + * Check if the service is running. + * \return \p true if the Nepomuk query service is running and could + * be contacted via DBus, \p false otherwise + */ + static bool serviceAvailable(); + + public Q_SLOTS: + /** + * Start a query using the Nepomuk user query language. + * + * Results will be reported via newEntries. All results + * have been reported once finishedListing has been emitted. + * + * \return \p true if the query service was found and the query + * was started. \p false otherwise. + * + * \sa QueryParser + */ + bool query( const QString& query ); + + /** + * Start a query. + * + * Results will be reported via newEntries. All results + * have been reported once finishedListing has been emitted. + * + * \return \p true if the query service was found and the query + * was started. \p false otherwise. + */ + bool query( const Query& query ); + + /** + * Start a query using the Nepomuk user query language. + * + * Results will be reported as with query(const QString&) + * but a local event loop will be started to block the method + * call until all results have been listed. + * + * The client will be closed after the initial listing. Thus, + * changes to results will not be reported as it is the case + * with the non-blocking methods. + * + * \return \p true if the query service was found and the query + * was started. \p false otherwise. + * + * \sa query(const QString&), close() + */ + bool blockingQuery( const QString& query ); + + /** + * \overload + * + * \sa query(const Query&) + */ + bool blockingQuery( const Query& query ); + + /** + * Close the client, thus stop to monitor the query + * for changes. Without closing the client it will continue + * signalling changes to the results. + * + * This will also make any blockingQuery return immediately. + */ + void close(); + + Q_SIGNALS: + /** + * Emitted for new search results. This signal is emitted both + * for the initial listing and for changes to the search. + */ + void newEntries( const QList& entries ); + + /** + * Emitted if the search results changed when monitoring a query. + * \param entries A list of resource URIs identifying the resources + * that dropped out of the query results. + */ + void entriesRemoved( const QList& entries ); + + /** + * Emitted when the initial listing has been finished, ie. if all + * results have been reported via newEntries. If no further updates + * are necessary the client should be closed now. + */ + void finishedListing(); + + private: + class Private; + Private* const d; + + Q_PRIVATE_SLOT( d, void _k_entriesRemoved( const QStringList& ) ) + Q_PRIVATE_SLOT( d, void _k_finishedListing() ) + }; + } +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/result.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/result.cpp --- akonadi-1.1.1/server/src/search/result.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/result.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,139 @@ +/* + This file is part of the Nepomuk KDE project. + Copyright (C) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "result.h" + +#include +#include + +#include "soprano/node.h" // for qHash( QUrl() ) + +class Nepomuk::Search::Result::Private : public QSharedData +{ +public: + QUrl resource; + double score; + QHash requestProperties; +}; + + +Nepomuk::Search::Result::Result() + : d( new Private() ) +{ +} + + +Nepomuk::Search::Result::Result( const QUrl& uri, double score ) + : d( new Private() ) +{ + d->resource = uri; + d->score = score; +} + + +Nepomuk::Search::Result::Result( const Result& other ) +{ + d = other.d; +} + + +Nepomuk::Search::Result::~Result() +{ +} + + +Nepomuk::Search::Result& Nepomuk::Search::Result::operator=( const Result& other ) +{ + d = other.d; + return *this; +} + + +double Nepomuk::Search::Result::score() const +{ + return d->score; +} + + +QUrl Nepomuk::Search::Result::resourceUri() const +{ + return d->resource; +} + + +void Nepomuk::Search::Result::setScore( double score ) +{ + d->score = score; +} + + +void Nepomuk::Search::Result::addRequestProperty( const QUrl& property, const Soprano::Node& value ) +{ + d->requestProperties[property] = value; +} + + +Soprano::Node Nepomuk::Search::Result::operator[]( const QUrl& property ) const +{ + return requestProperty( property ); +} + + +Soprano::Node Nepomuk::Search::Result::requestProperty( const QUrl& property ) const +{ + QHash::const_iterator it = d->requestProperties.find( property ); + if ( it != d->requestProperties.end() ) { + return *it; + } + else { + return Soprano::Node(); + } +} + + +QHash Nepomuk::Search::Result::requestProperties() const +{ + return d->requestProperties; +} + + +bool Nepomuk::Search::Result::operator==( const Result& other ) const +{ + if ( d->resource != other.d->resource || + d->score != other.d->score ) { + return false; + } + for ( QHash::const_iterator it = d->requestProperties.constBegin(); + it != d->requestProperties.constEnd(); ++it ) { + QHash::const_iterator it2 = other.d->requestProperties.constFind( it.key() ); + if ( it2 == other.d->requestProperties.constEnd() || + it2.value() != it.value() ) { + return false; + } + } + for ( QHash::const_iterator it = other.d->requestProperties.constBegin(); + it != other.d->requestProperties.constEnd(); ++it ) { + QHash::const_iterator it2 = d->requestProperties.constFind( it.key() ); + if ( it2 == d->requestProperties.constEnd() || + it2.value() != it.value() ) { + return false; + } + } + return true; +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/result.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/result.h --- akonadi-1.1.1/server/src/search/result.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/result.h 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,70 @@ +/* + This file is part of the Nepomuk KDE project. + Copyright (C) 2008 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef _NEPOMUK_SEARCH_RESULT_H_ +#define _NEPOMUK_SEARCH_RESULT_H_ + +#include +#include +#include +#include + +#include "soprano/statement.h" + +namespace Nepomuk { + namespace Search { + /** + * \brief A single search result. + * + * A search returns a set of Result object. + * + * \author Sebastian Trueg + */ + class Result + { + public: + Result(); + Result( const QUrl& uri, double score = 0.0 ); + Result( const Result& ); + ~Result(); + + Result& operator=( const Result& ); + + double score() const; + QUrl resourceUri() const; + + void setScore( double score ); + + void addRequestProperty( const QUrl& property, const Soprano::Node& value ); + + QHash requestProperties() const; + + Soprano::Node operator[]( const QUrl& property ) const; + Soprano::Node requestProperty( const QUrl& property ) const; + + bool operator==( const Result& ) const; + + private: + class Private; + QSharedDataPointer d; + }; + } +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/search/term.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/search/term.cpp --- akonadi-1.1.1/server/src/search/term.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/search/term.cpp 2009-07-28 17:52:08.000000000 +0100 @@ -0,0 +1,363 @@ +/* + This file is part of the Nepomuk KDE project. + Copyright (C) 2007 Sebastian Trueg + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include "term.h" + +#include +#include +#include +#include + + +class Nepomuk::Search::Term::Private : public QSharedData +{ +public: + Private( Type t = InvalidTerm, Comparator c = Equal ) + : type( t ), + comparator( c ) { + } + + Type type; + Comparator comparator; + Soprano::LiteralValue value; + QUrl resource; + QString field; + QUrl property; + QList subTerms; +}; + + +Nepomuk::Search::Term::Term() + : d( new Private() ) +{ +} + + +Nepomuk::Search::Term::Term( const Term& other ) +{ + d = other.d; +} + + +Nepomuk::Search::Term::Term( const Soprano::LiteralValue& value ) + : d( new Private( LiteralTerm ) ) +{ + d->value = value; +} + + +Nepomuk::Search::Term::Term( const QUrl& value ) + : d( new Private( ResourceTerm ) ) +{ + d->resource = value; +} + + +Nepomuk::Search::Term::Term( const QString& field, const Soprano::LiteralValue& value, Comparator c ) + : d( new Private( ComparisonTerm, c ) ) +{ + d->field = field; + d->subTerms.append( Term( value ) ); +} + + +Nepomuk::Search::Term::Term( const QUrl& field, const Soprano::LiteralValue& value, Comparator c ) + : d( new Private( ComparisonTerm, c ) ) +{ + d->property = field; + d->subTerms.append( Term( value ) ); +} + + +Nepomuk::Search::Term::Term( const QUrl& field, const QUrl& resource ) + : d( new Private( ComparisonTerm ) ) +{ + d->property = field; + d->subTerms.append( Term( resource ) ); +} + + +Nepomuk::Search::Term::~Term() +{ +} + + +Nepomuk::Search::Term& Nepomuk::Search::Term::operator=( const Term& other ) +{ + d = other.d; + return *this; +} + + +Nepomuk::Search::Term& Nepomuk::Search::Term::operator=( const Soprano::LiteralValue& literal ) +{ + d->value = literal; + d->type = LiteralTerm; + d->subTerms.clear(); + d->field.clear(); + return *this; +} + + +bool Nepomuk::Search::Term::isValid() const +{ + switch ( d->type ) { + case InvalidTerm: + return false; + + case LiteralTerm: + return d->value.isValid() && d->subTerms.isEmpty(); + + case ResourceTerm: + return d->resource.isValid() && d->subTerms.isEmpty(); + + case AndTerm: + case OrTerm: + return !d->subTerms.isEmpty(); + + case ComparisonTerm: + return ( !d->field.isEmpty() || !d->property.isEmpty() ) && ( d->subTerms.count() == 1 ); + } + // make gcc happy + return false; +} + + +Nepomuk::Search::Term::Type Nepomuk::Search::Term::type() const +{ + return d->type; +} + + +Soprano::LiteralValue Nepomuk::Search::Term::value() const +{ + return d->value; +} + + +QUrl Nepomuk::Search::Term::resource() const +{ + return d->resource; +} + + +Nepomuk::Search::Term::Comparator Nepomuk::Search::Term::comparator() const +{ + return d->comparator; +} + + +QString Nepomuk::Search::Term::field() const +{ + return d->field; +} + + +QUrl Nepomuk::Search::Term::property() const +{ + return d->property; +} + + +QList Nepomuk::Search::Term::subTerms() const +{ + return d->subTerms; +} + + +void Nepomuk::Search::Term::setType( Type type ) +{ + d->type = type; +} + + +void Nepomuk::Search::Term::setValue( const Soprano::LiteralValue& v ) +{ + d->value = v; + d->resource = QUrl(); +} + + +void Nepomuk::Search::Term::setResource( const QUrl& res ) +{ + d->resource = res; + d->value = Soprano::LiteralValue(); +} + + +void Nepomuk::Search::Term::setComparator( Comparator c ) +{ + d->comparator = c; +} + + +void Nepomuk::Search::Term::setField( const QString& f ) +{ + d->field = f; + d->property = QUrl(); +} + + +void Nepomuk::Search::Term::setSubTerms( const QList& terms ) +{ + d->subTerms = terms; +} + + +void Nepomuk::Search::Term::setProperty( const QUrl& p ) +{ + d->property = p; + d->field.clear(); +} + + +void Nepomuk::Search::Term::addSubTerm( const Term& term ) +{ + d->subTerms.append( term ); +} + + +namespace { + bool compareLists( const QList& t1, const QList& t2 ) { + // brute-force + foreach( const Nepomuk::Search::Term& t, t1 ) { + if ( !t2.contains( t ) ) { + return false; + } + } + foreach( const Nepomuk::Search::Term& t, t2 ) { + if ( !t1.contains( t ) ) { + return false; + } + } + return true; + } +} + +bool Nepomuk::Search::Term::operator==( const Term& other ) const +{ + if ( d->type == other.d->type ) { + if ( d->type == ComparisonTerm ) { + return ( d->comparator == other.d->comparator && + compareLists( d->subTerms, other.d->subTerms ) ); + } + else { + return d->value == other.d->value && + d->resource == other.d->resource && + d->field == other.d->field && + d->property == other.d->property && + compareLists( d->subTerms, other.d->subTerms ); + } + } + + return false; +} + + +QDebug operator<<( QDebug dbg, const Nepomuk::Search::Term& term ) +{ + if ( term.isValid() ) { + dbg << "(Term"; + switch( term.type() ) { + case Nepomuk::Search::Term::LiteralTerm: + dbg << "literal" << term.value(); + break; + case Nepomuk::Search::Term::ResourceTerm: + dbg << "resource" << term.resource(); + break; + case Nepomuk::Search::Term::AndTerm: + dbg << "and"; + break; + case Nepomuk::Search::Term::OrTerm: + dbg << "or"; + break; + case Nepomuk::Search::Term::ComparisonTerm: + dbg << "compare"; + switch( term.comparator() ) { + case Nepomuk::Search::Term::Contains: + dbg << ":"; + break; + case Nepomuk::Search::Term::Equal: + dbg << "="; + break; + case Nepomuk::Search::Term::Greater: + dbg << ">"; + break; + case Nepomuk::Search::Term::Smaller: + dbg << "<"; + break; + case Nepomuk::Search::Term::GreaterOrEqual: + dbg << ">="; + break; + case Nepomuk::Search::Term::SmallerOrEqual: + dbg << "<="; + break; + } + default: + break; + } + if ( term.type() == Nepomuk::Search::Term::ComparisonTerm ) { + if ( term.property().isValid() ) { + dbg << "Property" << term.property(); + } + else { + dbg << "Field:" << term.field(); + } + dbg << term.subTerms().first(); + } + if ( term.type() == Nepomuk::Search::Term::AndTerm || + term.type() == Nepomuk::Search::Term::OrTerm ) { + dbg << "Subterms: ["; + foreach( const Nepomuk::Search::Term &t, term.subTerms() ) { + dbg << t; + } + dbg << "]"; + } + dbg << ")"; + } + + return dbg; +} + + +uint Nepomuk::Search::qHash( const Nepomuk::Search::Term& term ) +{ + switch( term.type() ) { + case Nepomuk::Search::Term::LiteralTerm: + return qHash( term.value().toString() ); + + case Nepomuk::Search::Term::ComparisonTerm: + return( qHash( term.property().isValid() ? term.property().toString() : term.field() )<<16 | + qHash( term.subTerms().first() )<<8 | + ( uint )term.comparator() ); + + case Nepomuk::Search::Term::AndTerm: + case Nepomuk::Search::Term::OrTerm: { + uint h = ( uint )term.type(); + QList subTerms = term.subTerms(); + for ( int i = 0; i < subTerms.count(); ++i ) { + h |= ( qHash( subTerms[i] )< + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef _NEPOMUK_SEARCH_TERM_H_ +#define _NEPOMUK_SEARCH_TERM_H_ + +#include +#include +#include + +#include "soprano/literalvalue.h" + +namespace Nepomuk { + namespace Search { + /** + * \class Term term.h nepomuk/term.h + * + * \brief A Query constist of Terms. + * + * Queries are build from Term instances. A Term can have one of multiple + * types and subterms. See Term::Type for details on the different Term types. + * + * \author Sebastian Trueg + */ + class Term + { + public: + /** + * Each search term has a type. + */ + enum Type { + InvalidTerm, /**< An invalid term does not do anything */ + /** + * A literal term is the simplest form of Term. It matches all resource + * that contain the value. + * + * It is also used to specify literal values in comparison Terms such + * as ContainsTerm or EqualityTerm. + */ + LiteralTerm, + + /** + * A resource term matches one resource by URI. + * + * It is also used to specify a resource in EqualityTerm Terms. + */ + ResourceTerm, + + /** + * Match all resources that match all sub terms. + */ + AndTerm, + + /** + * Match all resources that match one of the sub terms. + */ + OrTerm, + + /** + * A comparison. The comparison operator needs to be specified in addition. + * For specifying the %property the same applies as for ContainsTerm. + * + * A single subterm specifies the resource or value to match (resource terms + * can only be matched via Equal or Contains. + */ + ComparisonTerm + }; + + enum Comparator { + Contains, + Equal, + Greater, + Smaller, + GreaterOrEqual, + SmallerOrEqual + }; + + /** + * Constructs an invalid term. + */ + Term(); + + /** + * Copy constructor. + */ + Term( const Term& other ); + + /** + * Construct a literal term. + */ + Term( const Soprano::LiteralValue& value ); + + /** + * Construct a resource term. + */ + Term( const QUrl& resource ); + + /** + * Construct a Contains ComparisonTerm term. + * \param field A string that will be matched against a field label + * \param value A value that will be matched against the field value. Unsupported + * types are converted to string. + * \param comparator The Comparator to use + */ + Term( const QString& field, const Soprano::LiteralValue& value, Comparator c = Contains ); + + /** + * Construct a Contains ComparisonTerm term. + * \param field The exact field to match + * \param value A value that will be matched against the field value. Unsupported + * types are converted to string. + * \param comparator The Comparator to use + */ + Term( const QUrl& field, const Soprano::LiteralValue& value, Comparator c = Contains ); + + /** + * Construct an EqualityTerm term. + * \param field The exact field to match + * \param value The resource that should be matched. + */ + Term( const QUrl& field, const QUrl& resource ); + + /** + * Destructor + */ + ~Term(); + + /** + * Assign another term to this one. + */ + Term& operator=( const Term& other ); + + /** + * Make the term a literal term. + */ + Term& operator=( const Soprano::LiteralValue& other ); + + /** + * \return \p true if the Term is valid. + */ + bool isValid() const; + + /** + * \return the Term type. + * + * \sa setType + */ + Type type() const; + + /** + * The literal value of a LiteralTerm. + * + * \sa setValue + */ + Soprano::LiteralValue value() const; + + /** + * The resource of a ResourceTerm + * + * \sa setResource + */ + QUrl resource() const; + + /** + * The Comparator used by ComparisonTerm Terms. + * + * \sa setComparator + */ + Comparator comparator() const; + + /** + * A property name used for ComparisonTerm Terms. Will be matched against + * the rdfs:label to find the corresponding property. + * + * \sa setField, property, setProperty + */ + QString field() const; + + /** + * A property used for ComparisonTerm Terms. + * + * \sa setProperty, field, setField + */ + QUrl property() const; + + /** + * The sub terms used by AndTerm, OrTerm, and ComparisonTerm. + * + * \sa setSubTerms, addSubTerm + */ + QList subTerms() const; + + /** + * Set the type of the Term + */ + void setType( Type ); + + /** + * Set the value of a LiteralTerm + */ + void setValue( const Soprano::LiteralValue& ); + + /** + * Set the resource of a ResourceTerm + */ + void setResource( const QUrl& ); + + /** + * Defaults to Equal + */ + void setComparator( Comparator ); + + /** + * Set the property label in case the exact + * property is not known. Will be mached against + * the property's rdfs:label. + * + * \sa field, setProperty, property + */ + void setField( const QString& ); + + /** + * Set the property for ComparisonTerm + * Terms. + * + * If the exact property is not known use setField. + * + * \sa property + */ + void setProperty( const QUrl& ); + + /** + * Set the subterms used by AndTerm, OrTerm, and ComparisonTerm. + * + * \sa addSubTerm + */ + void setSubTerms( const QList& ); + + /** + * Add a subterm used by AndTerm, OrTerm, and ComparisonTerm. + * + * \sa setSubTerm + */ + void addSubTerm( const Term& ); + + /** + * Comparison operator. + */ + bool operator==( const Term& ) const; + + private: + class Private; + QSharedDataPointer d; + }; + + uint qHash( const Nepomuk::Search::Term& ); + } +} + +QDebug operator<<( QDebug, const Nepomuk::Search::Term& ); + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/akonadidb.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/akonadidb.xml --- akonadi-1.1.1/server/src/storage/akonadidb.xml 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/akonadidb.xml 2009-07-28 17:52:07.000000000 +0100 @@ -67,21 +67,21 @@ Contains the schema version of the database. - +
- +
- +
- + @@ -90,9 +90,9 @@ - - - + + +
@@ -110,8 +110,8 @@ - - + + create/modified time @@ -123,7 +123,7 @@ Indicates that this item has unsaved changes. - +
@@ -146,26 +146,27 @@ - + + - +
- + - +
- - Specifies allowed MimeType for a Location + + Specifies allowed MimeType for a Collection - + Used to associate items with search folders. diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/collectionqueryhelper.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/collectionqueryhelper.cpp --- akonadi-1.1.1/server/src/storage/collectionqueryhelper.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/collectionqueryhelper.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,54 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "collectionqueryhelper.h" + +#include "akonadiconnection.h" +#include "entities.h" +#include "storage/querybuilder.h" +#include "libs/imapset_p.h" +#include "handler/scope.h" +#include "handler.h" +#include "queryhelper.h" + +using namespace Akonadi; + +void CollectionQueryHelper::remoteIdToQuery(const QStringList& rids, AkonadiConnection* connection, QueryBuilder& qb) +{ + if ( rids.size() == 1 ) + qb.addValueCondition( Collection::remoteIdFullColumnName(), Query::Equals, rids.first() ); + else + qb.addValueCondition( Collection::remoteIdFullColumnName(), Query::In, rids ); + + if ( connection->resourceContext().isValid() ) { + qb.addValueCondition( Collection::resourceIdFullColumnName(), Query::Equals, connection->resourceContext().id() ); + } +} + +void CollectionQueryHelper::scopeToQuery(const Scope& scope, AkonadiConnection* connection, QueryBuilder& qb) +{ + if ( scope.scope() == Scope::None || scope.scope() == Scope::Uid ) { + QueryHelper::setToQuery( scope.uidSet(), Collection::idFullColumnName(), qb ); + } else if ( scope.scope() == Scope::Rid ) { + if ( connection->selectedCollectionId() <= 0 && !connection->resourceContext().isValid() ) + throw HandlerException( "Operations based on remote identifiers require a resource or collection context" ); + CollectionQueryHelper::remoteIdToQuery( scope.ridSet(), connection, qb ); + } else + throw HandlerException( "WTF?" ); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/collectionqueryhelper.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/collectionqueryhelper.h --- akonadi-1.1.1/server/src/storage/collectionqueryhelper.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/collectionqueryhelper.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,53 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_COLLECTIONQUERYHELPER_H +#define AKONADI_COLLECTIONQUERYHELPER_H + +#include "entities.h" +#include "handler/scope.h" + +namespace Akonadi { + +class AkonadiConnection; +class ImapSet; +class QueryBuilder; +class Scope; + +/** + Helper methods to generate WHERE clauses for collection queries based on a Scope object. +*/ +namespace CollectionQueryHelper +{ + /** + Add conditions to @p qb for the given remote identifier @p rid. + The rid context is taken from @p connection. + */ + void remoteIdToQuery( const QStringList &rids, AkonadiConnection* connection, QueryBuilder &qb ); + + /** + Add conditions to @p qb for the given collection operation scope @p scope. + The rid context is taken from @p connection, if none is specified an exception is thrown. + */ + void scopeToQuery( const Scope &scope, AkonadiConnection* connection, QueryBuilder &qb ); +} + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/create-unittest-values.sql /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/create-unittest-values.sql --- akonadi-1.1.1/server/src/storage/create-unittest-values.sql 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/create-unittest-values.sql 1970-01-01 01:00:00.000000000 +0100 @@ -1,91 +0,0 @@ -DELETE FROM PimItemFlagRelation; -DELETE FROM PimItemTable; -DELETE FROM PartTable; -DELETE FROM LocationMimeTypeRelation; -DELETE FROM LocationTable WHERE name != "Search" or parentId != 0; -DELETE FROM ResourceTable WHERE name != "akonadi_search_resource"; -DELETE FROM LocationPimItemRelation; - -INSERT INTO ResourceTable (id, name) VALUES(2, 'akonadi_dummy_resource_1'); -INSERT INTO ResourceTable (id, name) VALUES(3, 'akonadi_dummy_resource_2'); -INSERT INTO ResourceTable (id, name) VALUES(4, 'akonadi_dummy_resource_3'); - -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (10, 6, 'foo', 2); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (2, 10, 'bar', 2); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (3, 2, 'bla', 2); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (4, 10, 'bla', 2); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (5, 7, 'foo2', 3); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (6, 0, 'res1', 2); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (7, 0, 'res2', 3); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (8, 0, 'res3', 4); -INSERT INTO LocationTable (id, parentId, name, resourceId) VALUES (9, 7, 'space folder', 3); - -INSERT INTO LocationTable (id, parentId, name, remoteId, resourceId) VALUES(11, 1, 'kde-core-devel', 'kde-core-devel@kde.org', 1); -INSERT INTO LocationTable (id, parentId, name, remoteId, resourceId) VALUES(12, 1, 'all', 'MIMETYPE message/rfc822', 1); -INSERT INTO LocationTable (id, parentId, name, remoteId, resourceId) VALUES(13, 1, 'Test ?er', '"Test ?er"', 1); - -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 10, 3); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 10, 4); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 10, 2); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 2, 5); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 10, 1); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 3, 5); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 4, 5); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 8, 5); -INSERT INTO LocationMimeTypeRelation ( Location_id, MimeType_id) VALUES( 8, 1); - -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (1, 'A', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (2, 'B', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (3, 'C', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (4, 'D', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (5, 'E', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (6, 'F', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (7, 'G', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (8, 'H', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (9, 'I', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (10, 'J', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (11, 'K', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (12, 'L', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (13, 'M', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (14, 'N', 10, 1); -INSERT INTO PimItemTable (id, remoteId, locationId, mimeTypeId) VALUES (15, 'O', 10, 1); - -INSERT INTO PimItemFlagRelation (Flag_id, PimItem_id) VALUES (5, 1); -INSERT INTO PimItemFlagRelation (Flag_id, PimItem_id) VALUES (8, 1); -INSERT INTO PimItemFlagRelation (Flag_id, PimItem_id) VALUES (7, 1); -INSERT INTO PimItemFlagRelation (Flag_id, PimItem_id) VALUES (5, 2); - -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (1, 'PLD:RFC822', 'testmailbody', 12, 1); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (2, 'ATR:HEAD', 'From: ', 21, 1); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (3, 'PLD:RFC822', 'testmailbody1', 13, 2); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (4, 'ATR:HEAD', 'From: ', 22, 2); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (5, 'PLD:RFC822', 'testmailbody2', 13, 3); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (6, 'ATR:HEAD', 'From: ', 22, 3); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (7, 'PLD:RFC822', 'testmailbody3', 13, 4); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (8, 'ATR:HEAD', 'From: ', 22, 4); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (9, 'PLD:RFC822', 'testmailbody4', 13, 5); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (10, 'ATR:HEAD', 'From: ', 22, 5); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (11, 'PLD:RFC822', 'testmailbody5', 13, 6); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (12, 'ATR:HEAD', 'From: ', 22, 6); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (13, 'PLD:RFC822', 'testmailbody6', 13, 7); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (14, 'ATR:HEAD', 'From: ', 22, 7); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (15, 'PLD:RFC822', 'testmailbody7', 13, 8); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (16, 'ATR:HEAD', 'From: ', 22, 8); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (17, 'PLD:RFC822', 'testmailbody8', 13, 9); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (18, 'ATR:HEAD', 'From: ', 22, 9); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (19, 'PLD:RFC822', 'testmailbody9', 13, 10); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (20, 'ATR:HEAD', 'From: ', 22, 10); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (21, 'PLD:RFC822', 'testmailbody10', 14, 11); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (22, 'ATR:HEAD', 'From: ', 23, 11); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (23, 'PLD:RFC822', 'testmailbody11', 14, 12); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (24, 'ATR:HEAD', 'From: ', 23, 12); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (25, 'PLD:RFC822', 'testmailbody12', 14, 13); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (26, 'ATR:HEAD', 'From: ', 23, 13); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (27, 'PLD:RFC822', 'testmailbody13', 14, 14); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (28, 'ATR:HEAD', 'From: ', 23, 14); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (29, 'PLD:RFC822', 'testmailbody14', 14, 15); -INSERT INTO PartTable (id, name, data, datasize, pimItemId) VALUES (30, 'ATR:HEAD', 'From: ', 23, 15); - -INSERT INTO LocationPimItemRelation (Location_id, PimItem_id) VALUES (11, 1); -INSERT INTO LocationPimItemRelation (Location_id, PimItem_id) VALUES (11, 3); -INSERT INTO LocationPimItemRelation (Location_id, PimItem_id) VALUES (11, 5); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/datastore.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/datastore.cpp --- akonadi-1.1.1/server/src/storage/datastore.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/datastore.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -21,7 +21,6 @@ #include "datastore.h" #include "agentmanagerinterface.h" -#include "resourceinterface.h" #include "dbconfig.h" #include "dbinitializer.h" #include "dbupdater.h" @@ -30,7 +29,10 @@ #include "selectquerybuilder.h" #include "handlerhelper.h" #include "countquerybuilder.h" -#include "../../libs/xdgbasedirs_p.h" +#include "xdgbasedirs_p.h" +#include "akdebug.h" +#include "parthelper.h" +#include "libs/protocol_p.h" #include #include @@ -50,10 +52,6 @@ using namespace Akonadi; -QList DataStore::mPendingItemDeliveries; -QMutex DataStore::mPendingItemDeliveriesMutex; -QWaitCondition DataStore::mPendingItemDeliveriesCondition; - /*************************************************************************** * DataStore * ***************************************************************************/ @@ -117,7 +115,7 @@ { DbInitializer initializer( m_database, QLatin1String(":akonadidb.xml") ); if (! initializer.run() ) { - qWarning() << initializer.errorMsg(); + akError() << initializer.errorMsg(); return false; } @@ -129,7 +127,7 @@ MimeType::enableCache( true ); Flag::enableCache( true ); Resource::enableCache( true ); - Location::enableCache( true ); + Collection::enableCache( true ); return true; } @@ -171,12 +169,12 @@ return false; } - mNotificationCollector->itemChanged( item ); + mNotificationCollector->itemChanged( item, QSet() << "FLAGS" ); return true; } bool DataStore::appendItemFlags( const PimItem &item, const QList &flags, - bool checkIfExists, const Location &loc ) + bool checkIfExists, const Collection &col ) { if ( !item.isValid() ) return false; @@ -190,12 +188,12 @@ } } - mNotificationCollector->itemChanged( item, loc ); + mNotificationCollector->itemChanged( item, QSet() << "FLAGS", col ); return true; } bool DataStore::appendItemFlags( const PimItem &item, const QList &flags, - bool checkIfExists, const Location &loc ) + bool checkIfExists, const Collection &col ) { Flag::List list; foreach ( const QByteArray &f, flags ) { @@ -207,7 +205,7 @@ } list << flag; } - return appendItemFlags( item, list, checkIfExists, loc ); + return appendItemFlags( item, list, checkIfExists, col ); } bool DataStore::removeItemFlags( const PimItem &item, const QList &flags ) @@ -217,7 +215,7 @@ return false; } - mNotificationCollector->itemChanged( item ); + mNotificationCollector->itemChanged( item, QSet() << "FLAGS" ); return true; } @@ -226,79 +224,113 @@ bool DataStore::removeItemParts( const PimItem &item, const QList &parts ) { Part::List existingParts = item.parts(); - foreach ( Part part, existingParts ) - if( parts.contains( part.name().toLatin1() ) ) - part.remove(); + foreach ( Part part, existingParts ) { + if( parts.contains( part.name().toLatin1() ) ) { + if ( !PartHelper::remove(&part) ) + return false; + } + } - mNotificationCollector->itemChanged( item ); + mNotificationCollector->itemChanged( item, parts.toSet() ); return true; } -/* --- Location ------------------------------------------------------ */ -bool DataStore::appendLocation( Location &location ) +/* --- Collection ------------------------------------------------------ */ +bool DataStore::appendCollection( Collection &collection ) { // no need to check for already existing collection with the same name, // a unique index on parent + name prevents that in the database - if ( !location.insert() ) + if ( !collection.insert() ) return false; - mNotificationCollector->collectionAdded( location ); + mNotificationCollector->collectionAdded( collection ); return true; } -bool Akonadi::DataStore::cleanupLocation(Location & location) +bool Akonadi::DataStore::cleanupCollection(Collection &collection) { // delete the content - PimItem::List items = location.items(); + PimItem::List items = collection.items(); foreach ( PimItem item, items ) cleanupPimItem( item ); - // delete location mimetypes - removeMimeTypesForLocation( location.id() ); - Location::clearPimItems( location.id() ); + // delete collection mimetypes + collection.clearMimeTypes(); + Collection::clearPimItems( collection.id() ); // delete attributes - foreach ( LocationAttribute attr, location.attributes() ) + foreach ( CollectionAttribute attr, collection.attributes() ) if ( !attr.remove() ) return false; - // delete the location itself - mNotificationCollector->collectionRemoved( location ); - return location.remove(); + // delete the collection itself + mNotificationCollector->collectionRemoved( collection ); + return collection.remove(); +} + +static bool recursiveSetResourceId( const Collection & collection, qint64 resourceId ) +{ + QueryBuilder qb( QueryBuilder::Update ); + qb.addTable( Collection::tableName() ); + qb.addValueCondition( Collection::parentIdColumn(), Query::Equals, collection.id() ); + qb.updateColumnValue( Collection::resourceIdColumn(), resourceId ); + if ( !qb.exec() ) + return false; + foreach ( const Collection &col, collection.children() ) { + if ( !recursiveSetResourceId( col, resourceId ) ) + return false; + } + return true; } -bool Akonadi::DataStore::renameLocation( Location & location, qint64 newParent, const QByteArray & newName) +bool Akonadi::DataStore::renameCollection( Collection & collection, qint64 newParent, const QByteArray & newName) { - if ( location.name() == newName && location.parentId() == newParent ) + if ( collection.name() == newName && collection.parentId() == newParent ) return true; if ( !m_dbOpened ) return false; - if ( newParent > 0 ) { - Location parent = Location::retrieveById( newParent ); + int resourceId = collection.resourceId(); + if ( newParent > 0 && collection.parentId() != newParent ) { + Collection parent = Collection::retrieveById( newParent ); + resourceId = parent.resourceId(); if ( !parent.isValid() ) return false; + + forever { + if ( parent.id() == collection.id() ) + return false; // target is child of source + if ( parent.parentId() == 0 ) + break; + parent = parent.parent(); + } } - SelectQueryBuilder qb; - qb.addValueCondition( Location::parentIdColumn(), Query::Equals, newParent ); - qb.addValueCondition( Location::nameColumn(), Query::Equals, newName ); + SelectQueryBuilder qb; + qb.addValueCondition( Collection::parentIdColumn(), Query::Equals, newParent ); + qb.addValueCondition( Collection::nameColumn(), Query::Equals, newName ); if ( !qb.exec() || qb.result().count() > 0 ) return false; - location.setName( newName ); - location.setParentId( newParent ); + collection.setName( newName ); + collection.setParentId( newParent ); + if ( collection.resourceId() != resourceId ) { + collection.setResourceId( resourceId ); + if ( !recursiveSetResourceId( collection, resourceId ) ) + return false; + } - if ( !location.update() ) + if ( !collection.update() ) return false; - mNotificationCollector->collectionChanged( location ); + QList changes = QList() << "PARENT" << AKONADI_PARAM_NAME; + mNotificationCollector->collectionChanged( collection, changes ); return true; } -bool DataStore::appendMimeTypeForLocation( qint64 locationId, const QStringList & mimeTypes ) +bool DataStore::appendMimeTypeForCollection( qint64 collectionId, const QStringList & mimeTypes ) { if ( mimeTypes.isEmpty() ) return true; @@ -311,7 +343,7 @@ foreach ( const MimeType &mt, qb.result() ) { // unique index on n:m relation prevents duplicates, ie. this will fail // if this mimetype is already set - if ( !Location::addMimeType( locationId, mt.id() ) ) + if ( !Collection::addMimeType( collectionId, mt.id() ) ) return false; missingMimeTypes.removeAll( mt.name() ); } @@ -321,41 +353,36 @@ qint64 mimeTypeId; if ( !appendMimeType( mtName, &mimeTypeId ) ) return false; - if ( !Location::addMimeType( locationId, mimeTypeId ) ) + if ( !Collection::addMimeType( collectionId, mimeTypeId ) ) return false; } return true; } -bool Akonadi::DataStore::removeMimeTypesForLocation(qint64 locationId) -{ - return Location::clearMimeTypes( locationId ); -} - -void DataStore::activeCachePolicy(Location & loc) +void DataStore::activeCachePolicy(Collection & col) { - if ( !loc.cachePolicyInherit() ) + if ( !col.cachePolicyInherit() ) return; - Location parent = loc.parent(); + Collection parent = col.parent(); while ( parent.isValid() ) { if ( !parent.cachePolicyInherit() ) { - loc.setCachePolicyCheckInterval( parent.cachePolicyCheckInterval() ); - loc.setCachePolicyCacheTimeout( parent.cachePolicyCacheTimeout() ); - loc.setCachePolicySyncOnDemand( parent.cachePolicySyncOnDemand() ); - loc.setCachePolicyLocalParts( parent.cachePolicyLocalParts() ); + col.setCachePolicyCheckInterval( parent.cachePolicyCheckInterval() ); + col.setCachePolicyCacheTimeout( parent.cachePolicyCacheTimeout() ); + col.setCachePolicySyncOnDemand( parent.cachePolicySyncOnDemand() ); + col.setCachePolicyLocalParts( parent.cachePolicyLocalParts() ); return; } parent = parent.parent(); } // ### system default - loc.setCachePolicyCheckInterval( -1 ); - loc.setCachePolicyCacheTimeout( -1 ); - loc.setCachePolicySyncOnDemand( false ); - loc.setCachePolicyLocalParts( QLatin1String("ALL") ); + col.setCachePolicyCheckInterval( -1 ); + col.setCachePolicyCacheTimeout( -1 ); + col.setCachePolicySyncOnDemand( false ); + col.setCachePolicyLocalParts( QLatin1String("ALL") ); } /* --- MimeType ------------------------------------------------------ */ @@ -374,15 +401,15 @@ /* --- PimItem ------------------------------------------------------- */ -bool DataStore::appendPimItem( const QList & parts, +bool DataStore::appendPimItem( QList & parts, const MimeType & mimetype, - const Location & location, + const Collection & collection, const QDateTime & dateTime, - const QByteArray & remote_id, + const QString & remote_id, PimItem &pimItem ) { pimItem.setMimeTypeId( mimetype.id() ); - pimItem.setLocationId( location.id() ); + pimItem.setCollectionId( collection.id() ); if ( dateTime.isValid() ) pimItem.setDatetime( dateTime ); if ( remote_id.isEmpty() ) { @@ -400,72 +427,22 @@ // insert every part if ( !parts.isEmpty() ) { - foreach( Part part, parts ) { - part.setPimItemId( pimItem.id() ); - part.setDatasize( part.data().size() ); + //don't use foreach, the caller depends on knowing the part has changed, see the Append handler + for(QList::iterator it = parts.begin(); it != parts.end(); ++it ) { + + (*it).setPimItemId( pimItem.id() ); + (*it).setDatasize( (*it).data().size() ); - if( !part.insert() ) + qDebug() << "Insert from DataStore::appendPimItem"; + if( !PartHelper::insert(&(*it)) ) return false; } } - mNotificationCollector->itemAdded( pimItem, location, mimetype.name() ); + mNotificationCollector->itemAdded( pimItem, collection, mimetype.name() ); return true; } -bool Akonadi::DataStore::updatePimItem(PimItem & pimItem) -{ - pimItem.setAtime( QDateTime::currentDateTime() ); - if ( mSessionId != pimItem.location().resource().name().toLatin1() ) - pimItem.setDirty( true ); - if ( !pimItem.update() ) - return false; - - mNotificationCollector->itemChanged( pimItem ); - return true; -} - -bool Akonadi::DataStore::updatePimItem(PimItem & pimItem, const Location & destination) -{ - if ( !pimItem.isValid() || !destination.isValid() ) - return false; - if ( pimItem.locationId() == destination.id() ) - return true; - - Location source = pimItem.location(); - if ( !source.isValid() ) - return false; - mNotificationCollector->collectionChanged( source ); - - pimItem.setLocationId( destination.id() ); - pimItem.setAtime( QDateTime::currentDateTime() ); - if ( mSessionId != pimItem.location().resource().name().toLatin1() ) - pimItem.setDirty( true ); - if ( !pimItem.update() ) - return false; - - mNotificationCollector->collectionChanged( destination ); - mNotificationCollector->itemMoved( pimItem, source, destination ); - return true; -} - -bool DataStore::updatePimItem(PimItem & pimItem, const QString & remoteId) -{ - if ( !pimItem.isValid() ) - return false; - if ( pimItem.remoteId() == remoteId.toLatin1() ) - return true; - - pimItem.setRemoteId( remoteId.toLatin1() ); - pimItem.setAtime( QDateTime::currentDateTime() ); - if ( !pimItem.update() ) - return false; - mNotificationCollector->itemChanged( pimItem ); - return true; -} - - - bool DataStore::cleanupPimItem( const PimItem &item ) { if ( !item.isValid() ) @@ -476,20 +453,20 @@ if ( !item.clearFlags() ) return false; - if ( !Part::remove( Part::pimItemIdColumn(), item.id() ) ) + if ( !PartHelper::remove( Part::pimItemIdColumn(), item.id() ) ) return false; if ( !PimItem::remove( PimItem::idColumn(), item.id() ) ) return false; - if ( !Entity::clearRelation( item.id(), Entity::Right ) ) + if ( !Entity::clearRelation( item.id(), Entity::Right ) ) return false; return true; } -bool DataStore::cleanupPimItems( const Location &location ) +bool DataStore::cleanupPimItems( const Collection &collection ) { - if ( !m_dbOpened || !location.isValid() ) + if ( !m_dbOpened || !collection.isValid() ) return false; QueryBuilder qb( QueryBuilder::Select ); @@ -498,7 +475,7 @@ qb.addTable( PimItem::tableName() ); qb.addColumn( PimItemFlagRelation::leftFullColumnName() ); qb.addValueCondition( Flag::nameFullColumnName(), Query::Equals, QLatin1String("\\Deleted") ); - qb.addValueCondition( PimItem::locationIdFullColumnName(), Query::Equals, location.id() ); + qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, collection.id() ); qb.addColumnCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, Flag::idFullColumnName() ); if ( !qb.exec() ) @@ -519,72 +496,6 @@ return ok; } -void Akonadi::DataStore::retrieveDataFromResource( qint64 uid, const QByteArray& remote_id, const QByteArray& mimeType, - const QString &resource, const QStringList &parts ) -{ - // TODO: error handling - qDebug() << "retrieveDataFromResource()" << uid; - - // check if that item is already being fetched by someone else - mPendingItemDeliveriesMutex.lock(); - if ( mPendingItemDeliveries.contains( uid ) ) { - qDebug() << "requestItemDelivery(): item already requested by other thread - waiting" << uid; - mPendingItemDeliveriesCondition.wait( &mPendingItemDeliveriesMutex ); - qDebug() << "requestItemDelivery(): continuing"; - forever { - if ( !mPendingItemDeliveries.contains( uid ) ) { - mPendingItemDeliveriesMutex.unlock(); - break; - } - qDebug() << "requestItemDelivery(): item still requested by other thread - waiting again" << uid; - mPendingItemDeliveriesCondition.wait( &mPendingItemDeliveriesMutex ); - } - } else { - qDebug() << "requestItemDelivery(): blocking uid" << uid; - mPendingItemDeliveries << uid; - mPendingItemDeliveriesMutex.unlock(); - - qDebug() << "requestItemDelivery(): requested parts:" << parts; - - // call the resource - org::freedesktop::Akonadi::Resource *interface = resourceInterface( resource ); - if ( interface ) - interface->requestItemDelivery( uid, QString::fromUtf8(remote_id), QString::fromUtf8(mimeType), parts ); - - mPendingItemDeliveriesMutex.lock(); - qDebug() << "requestItemDelivery(): freeing uid" << uid; - mPendingItemDeliveries.removeAll( uid ); - mPendingItemDeliveriesCondition.wakeAll(); - mPendingItemDeliveriesMutex.unlock(); - } -} - -void DataStore::triggerCollectionSync( const Location &location ) -{ - org::freedesktop::Akonadi::Resource *interface = resourceInterface( location.resource().name() ); - if ( interface ) - interface->synchronizeCollection( location.id() ); -} - -QList DataStore::listPimItems( const Location & location, const Flag &flag ) -{ - if ( !m_dbOpened ) - return QList(); - - SelectQueryBuilder qb; - qb.addTable( PimItemFlagRelation::tableName() ); - qb.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, PimItemFlagRelation::leftFullColumnName() ); - qb.addValueCondition( PimItemFlagRelation::rightFullColumnName(), Query::Equals, flag.id() ); - - if ( location.isValid() ) - qb.addValueCondition( PimItem::locationIdFullColumnName(), Query::Equals, location.id() ); - - if ( !qb.exec() ) - return QList(); - - return qb.result(); -} - qint64 DataStore::highestPimItemId() const { if ( !m_dbOpened ) @@ -607,45 +518,45 @@ } -bool DataStore::addCollectionAttribute(const Location & loc, const QByteArray & key, const QByteArray & value) +bool DataStore::addCollectionAttribute(const Collection & col, const QByteArray & key, const QByteArray & value) { - SelectQueryBuilder qb; - qb.addValueCondition( LocationAttribute::locationIdColumn(), Query::Equals, loc.id() ); - qb.addValueCondition( LocationAttribute::typeColumn(), Query::Equals, key ); + SelectQueryBuilder qb; + qb.addValueCondition( CollectionAttribute::collectionIdColumn(), Query::Equals, col.id() ); + qb.addValueCondition( CollectionAttribute::typeColumn(), Query::Equals, key ); if ( !qb.exec() ) return false; if ( qb.result().count() > 0 ) { - qDebug() << "Attribute" << key << "already exists for location" << loc.id(); + qDebug() << "Attribute" << key << "already exists for collection" << col.id(); return false; } - LocationAttribute attr; - attr.setLocationId( loc.id() ); + CollectionAttribute attr; + attr.setCollectionId( col.id() ); attr.setType( key ); attr.setValue( value ); if ( !attr.insert() ) return false; - mNotificationCollector->collectionChanged( loc ); + mNotificationCollector->collectionChanged( col, QList() << key ); return true; } -bool Akonadi::DataStore::removeCollectionAttribute(const Location & loc, const QByteArray & key) +bool Akonadi::DataStore::removeCollectionAttribute(const Collection & col, const QByteArray & key) { - SelectQueryBuilder qb; - qb.addValueCondition( LocationAttribute::locationIdColumn(), Query::Equals, loc.id() ); - qb.addValueCondition( LocationAttribute::typeColumn(), Query::Equals, key ); + SelectQueryBuilder qb; + qb.addValueCondition( CollectionAttribute::collectionIdColumn(), Query::Equals, col.id() ); + qb.addValueCondition( CollectionAttribute::typeColumn(), Query::Equals, key ); if ( !qb.exec() ) return false; - foreach ( LocationAttribute attr, qb.result() ) { + foreach ( CollectionAttribute attr, qb.result() ) { if ( !attr.remove() ) return false; } - mNotificationCollector->collectionChanged( loc ); + mNotificationCollector->collectionChanged( col, QList() << key ); return true; } @@ -778,25 +689,4 @@ return m_transactionLevel > 0; } -org::freedesktop::Akonadi::Resource * Akonadi::DataStore::resourceInterface( const QString &res ) -{ - org::freedesktop::Akonadi::Resource* iface = 0; - if ( mResourceInterfaceCache.contains( res ) ) - iface = mResourceInterfaceCache.value( res ); - if ( iface && iface->isValid() ) - return iface; - - delete iface; - iface = new org::freedesktop::Akonadi::Resource( QLatin1String("org.freedesktop.Akonadi.Resource.") + res, - QLatin1String("/"), QDBusConnection::sessionBus(), this ); - if ( !iface || !iface->isValid() ) { - qDebug() << QString::fromLatin1( "Cannot connect to agent instance with identifier '%1', error message: '%2'" ) - .arg( res, iface ? iface->lastError().message() : QString() ); - delete iface; - return 0; - } - mResourceInterfaceCache.insert( res, iface ); - return iface; -} - #include "datastore.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/datastore.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/datastore.h --- akonadi-1.1.1/server/src/storage/datastore.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/datastore.h 2009-07-28 17:52:07.000000000 +0100 @@ -32,16 +32,6 @@ #include "entities.h" #include "notificationcollector.h" -class OrgFreedesktopAkonadiResourceInterface; -namespace org { - namespace freedesktop { - namespace Akonadi { - typedef ::OrgFreedesktopAkonadiResourceInterface Resource; - } - } -} - - namespace Akonadi { class NotificationCollector; @@ -131,47 +121,41 @@ /* --- ItemFlags ----------------------------------------------------- */ bool setItemFlags( const PimItem &item, const QList &flags ); bool appendItemFlags( const PimItem &item, const QList &flags, - bool checkIfExists = true, const Location &loc = Location() ); + bool checkIfExists = true, const Collection &col = Collection() ); bool appendItemFlags( const PimItem &item, const QList &flags, - bool checkIfExists = true, const Location &loc = Location() ); + bool checkIfExists = true, const Collection &col = Collection() ); bool removeItemFlags( const PimItem &item, const QList &flags ); /* --- ItemParts ----------------------------------------------------- */ bool removeItemParts( const PimItem &item, const QList &parts ); - /* --- Location ------------------------------------------------------ */ - bool appendLocation( Location &location ); - /// removes the given location and all its content - bool cleanupLocation( Location &location ); - /// rename the collection @p location to @p newName. - bool renameLocation( Location &location, qint64 newParent, const QByteArray &newName ); + /* --- Collection ------------------------------------------------------ */ + bool appendCollection( Collection &collection ); + /// removes the given collection and all its content + bool cleanupCollection( Collection &collection ); + /// rename the collection @p collection to @p newName. + bool renameCollection( Collection &collection, qint64 newParent, const QByteArray &newName ); - bool appendMimeTypeForLocation( qint64 locationId, const QStringList & mimeTypes ); - bool removeMimeTypesForLocation( qint64 locationId ); + bool appendMimeTypeForCollection( qint64 collectionId, const QStringList & mimeTypes ); - static QString locationDelimiter() { return QLatin1String("/"); } + static QString collectionDelimiter() { return QLatin1String("/"); } /** - Determines the active cache policy for this Location. - The active cache policy is set in the corresponding Location fields. + Determines the active cache policy for this Collection. + The active cache policy is set in the corresponding Collection fields. */ - void activeCachePolicy( Location &loc ); + void activeCachePolicy( Collection &col ); /* --- MimeType ------------------------------------------------------ */ bool appendMimeType( const QString & mimetype, qint64 *insertId = 0 ); /* --- PimItem ------------------------------------------------------- */ - bool appendPimItem( const QList & parts, + bool appendPimItem( QList & parts, const MimeType & mimetype, - const Location & location, + const Collection & collection, const QDateTime & dateTime, - const QByteArray & remote_id, + const QString & remote_id, PimItem &pimItem ); - bool updatePimItem( PimItem &pimItem ); - bool updatePimItem( PimItem &pimItem, const Location &destination ); - bool updatePimItem( PimItem &pimItem, const QString &remoteId ); - - QList listPimItems( const Location & location, const Flag &flag ); /** * Removes the pim item and all referenced data ( e.g. flags ) @@ -181,13 +165,13 @@ /** * Cleanups all items which have the '\\Deleted' flag set */ - bool cleanupPimItems( const Location &location ); + bool cleanupPimItems( const Collection &collection ); qint64 highestPimItemId() const; /* --- Collection attributes ------------------------------------------ */ - bool addCollectionAttribute( const Location &loc, const QByteArray &key, const QByteArray &value ); - bool removeCollectionAttribute( const Location &loc, const QByteArray &key ); + bool addCollectionAttribute( const Collection &col, const QByteArray &key, const QByteArray &value ); + bool removeCollectionAttribute( const Collection &col, const QByteArray &key ); /* --- Helper functions ---------------------------------------------- */ /** Returns the id of the next PIM item that is added to the db. @@ -254,10 +238,6 @@ void debugLastDbError( const char* actionDescription ) const; void debugLastQueryError( const QSqlQuery &query, const char* actionDescription ) const; public: - void retrieveDataFromResource( qint64 uid, const QByteArray& remote_id, const QByteArray& mimeType, - const QString &resource, const QStringList &parts ); - void triggerCollectionSync( const Location &location ); - /** Returns the id of the most recent inserted row, or -1 if there's no such id. @param query the query we want to get the last insert id for @@ -281,11 +261,6 @@ */ static QDateTime dateTimeToQDateTime( const QByteArray & dateTime ); - /** - Returns the D-Bus interface of the given resource. - */ - org::freedesktop::Akonadi::Resource *resourceInterface( const QString &res ); - private: QString m_connectionName; QSqlDatabase m_database; @@ -293,13 +268,6 @@ uint m_transactionLevel; QByteArray mSessionId; NotificationCollector* mNotificationCollector; - - static QList mPendingItemDeliveries; - static QMutex mPendingItemDeliveriesMutex; - static QWaitCondition mPendingItemDeliveriesCondition; - - // resource dbus interface cache - QHash mResourceInterfaceCache; }; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/dbconfig.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/dbconfig.cpp --- akonadi-1.1.1/server/src/storage/dbconfig.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/dbconfig.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -50,6 +50,22 @@ akFatal() << "No usable database driver found."; } + mSizeThreshold = 4096; + QVariant v = settings.value( QLatin1String("General/SizeThreshold"), mSizeThreshold ); + if ( v.canConvert() ) + { + mSizeThreshold = v.value(); + } else + { + mSizeThreshold = 0; + } + + if ( mSizeThreshold < 0 ) + mSizeThreshold = 0; + + mUseExternalPayloadFile = false; + mUseExternalPayloadFile = settings.value( QLatin1String("General/ExternalPayload"), mUseExternalPayloadFile ).toBool(); + // determine default settings depending on the driver QString defaultDbName; QString defaultOptions; @@ -106,6 +122,8 @@ // store back the default values settings.setValue( QLatin1String( "General/Driver" ), mDriverName ); + settings.setValue( QLatin1String( "General/SizeThreshold" ), mSizeThreshold ); + settings.setValue( QLatin1String( "General/ExternalPayload" ), mUseExternalPayloadFile ); settings.beginGroup( mDriverName ); settings.setValue( QLatin1String( "Name" ), mName ); settings.setValue( QLatin1String( "User" ), mUserName ); @@ -188,6 +206,8 @@ QString mConnectionOptions; QString mServerPath; bool mInternalServer; + bool mUseExternalPayloadFile; + qint64 mSizeThreshold; }; Q_GLOBAL_STATIC( DbConfigStatic, sInstance ) @@ -234,3 +254,13 @@ return sInstance()->mName; } +qint64 DbConfig::sizeThreshold() +{ + return sInstance()->mSizeThreshold; +} + +bool DbConfig::useExternalPayloadFile() +{ + return sInstance()->mUseExternalPayloadFile; +} + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/dbconfig.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/dbconfig.h --- akonadi-1.1.1/server/src/storage/dbconfig.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/dbconfig.h 2009-07-28 17:52:07.000000000 +0100 @@ -58,6 +58,19 @@ */ QString databaseName(); + /** + * Payload data bigger than this value will be stored in separate files, instead of the database. Valid + * only if @ref useExternalPayloadFile returns true, otherwise it is ignored. + * @return the size threshold in bytes, defaults to 4096. + */ + qint64 sizeThreshold(); + + /** + * Check if big payload data (@ref sizeThreshold) is stored in external files instead of the database. + * @return true, if the big data is stored in external files. Default is false. + */ + bool useExternalPayloadFile(); + } #endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/dbinitializer.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/dbinitializer.cpp --- akonadi-1.1.1/server/src/storage/dbinitializer.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/dbinitializer.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -30,6 +30,7 @@ #include #include #include +#include DbInitializer::DbInitializer( const QSqlDatabase &database, const QString &templateFile ) : mDatabase( database ), mTemplateFile( templateFile ) @@ -103,9 +104,21 @@ ColumnEntry entry; entry.first = columnElement.attribute( QLatin1String("name") ); if ( columnElement.attribute( QLatin1String("sqltype") ).isEmpty() ) - entry.second = sqlType( columnElement.attribute( QLatin1String("type") ) ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") ); + entry.second = sqlType( columnElement.attribute( QLatin1String("type") ) ); else - entry.second = columnElement.attribute( QLatin1String("sqltype") ) + QLatin1String( " " ) + columnElement.attribute( QLatin1String("properties") ); + entry.second = columnElement.attribute( QLatin1String("sqltype") ); + QString props = columnElement.attribute(QLatin1String("properties")); + if ( mDatabase.driverName().startsWith( QLatin1String("QSQLITE") ) ) { + if ( props.contains(QLatin1String("PRIMARY KEY")) ) + if ( entry.second == QLatin1String("BIGINT") ) + entry.second = QLatin1String("INTEGER"); + if ( props.contains(QLatin1String("BINARY")) ) + if ( !(props.contains(QLatin1String("COLLATE BINARY"))) ) + props.replace(QLatin1String("BINARY"), QLatin1String("COLLATE BINARY")); + if ( props.contains(QLatin1String("character set utf8 collate utf8_bin")) ) + props.remove(QLatin1String("character set utf8 collate utf8_bin")); + } + entry.second += QLatin1String(" ")+props; if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) ) { if ( entry.second.contains( QLatin1String("AUTOINCREMENT") ) ) entry.second = QLatin1String("SERIAL PRIMARY KEY NOT NULL"); @@ -159,7 +172,8 @@ qDebug() << statement; if ( !query.exec( statement ) ) { - mErrorMsg = QLatin1String( "Unable to create entire table." ); + mErrorMsg = QLatin1String( "Unable to create entire table.\n" ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'" ).arg( query.lastError().text() ); return false; } } else { @@ -189,7 +203,8 @@ .arg( tableName, entry.first, entry.second ); if ( !query.exec( statement ) ) { - mErrorMsg = QString::fromLatin1( "Unable to add column '%1' to table '%2'." ).arg( entry.first, tableName ); + mErrorMsg = QString::fromLatin1( "Unable to add column '%1' to table '%2'.\n" ).arg( entry.first, tableName ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'" ).arg( query.lastError().text() ); return false; } } @@ -202,12 +217,13 @@ columnElement = element.firstChildElement(); while ( !columnElement.isNull() ) { if ( columnElement.tagName() == QLatin1String( "index" ) ) { - if ( !hasIndex( tableName, columnElement.attribute( QLatin1String("name") ) ) ) { + QString indexName = QString::fromLatin1( "%1_%2" ).arg( tableName ).arg( columnElement.attribute( QLatin1String("name") ) ); // sqlite3 needs unique index identifiers per db + if ( !hasIndex( tableName, indexName ) ) { QString statement = QLatin1String( "CREATE " ); if ( columnElement.attribute( QLatin1String("unique") ) == QLatin1String( "true" ) ) statement += QLatin1String( "UNIQUE " ); statement += QLatin1String( "INDEX " ); - statement += columnElement.attribute( QLatin1String("name") ); + statement += indexName; statement += QLatin1String( " ON " ); statement += tableName; statement += QLatin1String( " (" ); @@ -216,7 +232,8 @@ QSqlQuery query( mDatabase ); qDebug() << "adding index" << statement; if ( !query.exec( statement ) ) { - mErrorMsg = QLatin1String( "Unable to create index." ); + mErrorMsg = QLatin1String( "Unable to create index.\n" ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'" ).arg( query.lastError().text() ); return false; } } @@ -228,13 +245,15 @@ // add initial data if table is empty const QString statement = QString::fromLatin1( "SELECT * FROM %1 LIMIT 1" ).arg( tableName ); if ( !query.exec( statement ) ) { - mErrorMsg = QString::fromLatin1( "Unable to retrieve data from table '%1'." ).arg( tableName ); + mErrorMsg = QString::fromLatin1( "Unable to retrieve data from table '%1'.\n" ).arg( tableName ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'" ).arg( query.lastError().text() ); return false; } if ( query.size() == 0 || !query.first() ) { foreach ( const QString &stmt, dataList ) { if ( !query.exec( stmt ) ) { - mErrorMsg = QString::fromLatin1( "Unable to add initial data to table '%1'." ).arg( tableName ); + mErrorMsg = QString::fromLatin1( "Unable to add initial data to table '%1'.\n" ).arg( tableName ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'\n" ).arg( query.lastError().text() ); mErrorMsg += QString::fromLatin1( "Query was: %1" ).arg( stmt ); return false; } @@ -261,36 +280,21 @@ .arg( col1 ) .arg( table1 ) .arg( col1 ); - statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4))" ) + statement += QString::fromLatin1("%1_%2 INTEGER REFERENCES %3(%4), " ) .arg( table2 ) .arg( col2 ) .arg( table2 ) .arg( col2 ); + statement += QString::fromLatin1("PRIMARY KEY (%1_%2, %3_%4));" ).arg( table1 ).arg( col1 ).arg( table2 ).arg( col2 ); qDebug() << statement; QSqlQuery query( mDatabase ); if ( !query.exec( statement ) ) { - mErrorMsg = QLatin1String( "Unable to create entire table." ); + mErrorMsg = QLatin1String( "Unable to create entire table.\n" ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'" ).arg( query.lastError().text() ); return false; } } - - if ( !hasIndex( tableName, QLatin1String("PRIMARY") ) ) { - QString statement = QLatin1String( "ALTER TABLE " ); - statement += tableName; - statement += QLatin1String( " ADD PRIMARY KEY (" ); - statement += QString::fromLatin1( "%1_%2" ).arg( table1 ).arg( col1 ); - statement += QLatin1String( ", " ); - statement += QString::fromLatin1( "%1_%2" ).arg( table2 ).arg( col2 ); - statement += QLatin1Char(')'); - QSqlQuery query( mDatabase ); - qDebug() << "adding index" << statement; - if ( !query.exec( statement ) ) { - mErrorMsg = QLatin1String( "Unable to create index." ); - return false; - } - } - return true; } @@ -321,27 +325,7 @@ bool DbInitializer::hasTable(const QString & tableName) { - QString statement; - if ( mDatabase.driverName() == QLatin1String( "QPSQL" ) ) - statement = QLatin1String("SELECT tablename FROM pg_tables ORDER BY tablename;" ); - else if ( mDatabase.driverName() == QLatin1String( "QSQLITE" ) ) - statement = QLatin1String("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"); - else if ( mDatabase.driverName().startsWith( QLatin1String("QMYSQL") ) ) - statement = QLatin1String("SHOW TABLES"); - else - Q_ASSERT( false ); // unknown driver - - QSqlQuery query( mDatabase ); - if ( !query.exec( statement ) ) { - mErrorMsg = QLatin1String( "Unable to retrieve table information from database." ); - return false; - } - - while ( query.next() ) { - if ( query.value( 0 ).toString().toLower() == tableName.toLower() ) - return true; - } - return false; + return mDatabase.tables().contains( tableName, Qt::CaseInsensitive ); } bool DbInitializer::hasIndex(const QString & tableName, const QString & indexName) @@ -355,6 +339,8 @@ statement = QLatin1String( "SELECT indexname FROM pq_indexes" ); statement += QString::fromLatin1( " WHERE tablename = '%1'" ).arg( tableName ); statement += QString::fromLatin1( " AND indexname = '%1';" ).arg( indexName ); + } else if ( mDatabase.driverName() == QLatin1String("QSQLITE") ) { + statement = QString::fromLatin1( "SELECT * FROM sqlite_master WHERE type='index' AND tbl_name='%1' AND name='%2';" ).arg( tableName ).arg( indexName ); } else { qFatal( "Implement index support for your database!" ); } @@ -362,7 +348,8 @@ // query it QSqlQuery query( mDatabase ); if ( !query.exec( statement ) ) { - mErrorMsg = QString::fromLatin1( "Unable to list index information for table %1." ).arg( tableName ); + mErrorMsg = QString::fromLatin1( "Unable to list index information for table %1.\n" ).arg( tableName ); + mErrorMsg += QString::fromLatin1( "Query error: '%1'" ).arg( query.lastError().text() ); return false; } return query.next(); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/dbupdater.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/dbupdater.cpp --- akonadi-1.1.1/server/src/storage/dbupdater.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/dbupdater.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -19,8 +19,8 @@ #include "dbupdater.h" #include "entities.h" +#include "akdebug.h" -#include #include #include @@ -34,7 +34,7 @@ { QFile file( m_filename ); if ( !file.open( QIODevice::ReadOnly ) ) { - qWarning() << "Unable to open update description file" << m_filename; + akError() << "Unable to open update description file" << m_filename; return false; } @@ -43,13 +43,13 @@ QString errorMsg; int line, column; if ( !document.setContent( &file, &errorMsg, &line, &column ) ) { - qWarning() << "Unable to parse update description file" << m_filename << ":" + akError() << "Unable to parse update description file" << m_filename << ":" << errorMsg << "at line" << line << "column" << column; return false; } const QDomElement documentElement = document.documentElement(); if ( documentElement.tagName() != QLatin1String( "updates" ) ) { - qWarning() << "Invalid update description file formant"; + akError() << "Invalid update description file formant"; return false; } @@ -62,17 +62,17 @@ if ( updateElement.tagName() == QLatin1String("update") ) { int version = updateElement.attribute( QLatin1String( "version" ), QLatin1String( "-1" ) ).toInt(); if ( version <= 0 ) { - qWarning() << "Invalid version attribute in database update description"; + akError() << "Invalid version attribute in database update description"; return false; } if ( updates.contains( version ) ) { - qWarning() << "Duplicate version attribute in database update description"; + akError() << "Duplicate version attribute in database update description"; return false; } if ( version > currentVersion.version() ) { updates.insert( version, updateElement ); } else { - qDebug() << "skipping update" << version; + akDebug() << "skipping update" << version; } } updateElement = updateElement.nextSiblingElement(); @@ -85,12 +85,18 @@ bool abortOnFailure = false; if ( it.value().attribute( QLatin1String( "abortOnFailure" ) ) == QLatin1String( "true" ) ) abortOnFailure = true; - qDebug() << "DbUpdater: update to version:" << it.key() << " mandatory:" << abortOnFailure << " code:" << sql; + akDebug() << "DbUpdater: update to version:" << it.key() << " mandatory:" << abortOnFailure << " code:" << sql; bool success = m_database.transaction(); if ( success ) { QSqlQuery query( m_database ); success = query.exec( sql ); + if ( !success ) { + akError() << "DBUpdater: query error:" << query.lastError().text() << m_database.lastError().text(); + akError() << "Query was: " << sql; + akError() << "Target version was: " << it.key(); + akError() << "Mandatory: " << abortOnFailure; + } } if ( success ) { currentVersion.setVersion( it.key() ); @@ -98,7 +104,7 @@ } if ( !success || !m_database.commit() ) { - qWarning() << "Failed to commit transaction for database update"; + akError() << "Failed to commit transaction for database update"; m_database.rollback(); if ( abortOnFailure ) return false; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/dbupdate.xml /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/dbupdate.xml --- akonadi-1.1.1/server/src/storage/dbupdate.xml 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/dbupdate.xml 2009-07-28 17:52:07.000000000 +0100 @@ -52,7 +52,29 @@ UPDATE parttable SET name = 'PLD:ENVELOPE' WHERE name = 'ENVELOPE'; UPDATE parttable SET name = 'PLD:RFC822' WHERE name = 'RFC822'; UPDATE parttable SET name = 'PLD:HEAD' WHERE name = 'HEAD'; - UPDATE parttable SET name = concat( 'ATR:', name ) WHERE left( name, 4 ) != 'PLD:'; + UPDATE parttable SET name = concat( 'ATR:', name ) WHERE substr( name, 1, 4 ) != 'PLD:'; + + + DROP TABLE CollectionTable; + ALTER TABLE LocationTable RENAME TO CollectionTable; + ALTER TABLE PimItemTable DROP COLUMN collectionId; + ALTER TABLE PimItemTable CHANGE locationId collectionId BIGINT; + DROP TABLE CollectionAttributeTable; + ALTER TABLE LocationAttributeTable CHANGE locationId collectionId BIGINT; + ALTER TABLE LocationAttributeTable RENAME TO CollectionAttributeTable; + DROP TABLE CollectionMimeTypeRelation; + ALTER TABLE LocationMimeTypeRelation CHANGE Location_Id Collection_Id BIGINT NOT NULL DEFAULT '0'; + ALTER TABLE LocationMimeTypeRelation RENAME TO CollectionMimeTypeRelation; + DROP TABLE CollectionPimItemRelation; + ALTER TABLE LocationPimItemRelation CHANGE Location_Id Collection_Id BIGINT NOT NULL DEFAULT '0'; + ALTER TABLE LocationPimItemRelation RENAME TO CollectionPimItemRelation; + + + + ALTER TABLE PartTable CHANGE datasize datasize BIGINT; + + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/entities-header.xsl /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/entities-header.xsl --- akonadi-1.1.1/server/src/storage/entities-header.xsl 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/entities-header.xsl 2009-07-28 17:52:07.000000000 +0100 @@ -43,12 +43,12 @@ // constructor (); - ( + explicit ( , ); - ( + explicit ( , ); @@ -161,6 +161,11 @@ bool insert( qint64* insertId = 0 ); /** + Returns @c true if this record has any pending changes. + */ + bool hasPendingChanges() const; + + /** Stores all changes made to this record into the database. Note that this method assumes the existence of an 'id' column to identify the record to update. If that column does not exist, all records will be @@ -223,7 +228,7 @@ static bool clears( qint64 id ); - protected: +// protected: // delete records static bool remove( const QString &column, const QVariant &value ); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/entities-source.xsl /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/entities-source.xsl --- akonadi-1.1.1/server/src/storage/entities-source.xsl 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/entities-source.xsl 2009-07-28 17:52:07.000000000 +0100 @@ -453,6 +453,14 @@ return true; } +bool ::hasPendingChanges() const +{ + return false + + || d->_changed + ; +} + // update existing data bool ::update() { diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/entity.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/entity.h --- akonadi-1.1.1/server/src/storage/entity.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/entity.h 2009-07-28 17:52:07.000000000 +0100 @@ -41,6 +41,7 @@ class Entity { public: + typedef qint64 Id; qint64 id() const; void setId( qint64 id ); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemqueryhelper.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemqueryhelper.cpp --- akonadi-1.1.1/server/src/storage/itemqueryhelper.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemqueryhelper.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,86 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "itemqueryhelper.h" + +#include "akonadiconnection.h" +#include "entities.h" +#include "storage/querybuilder.h" +#include "libs/imapset_p.h" +#include "handler/scope.h" +#include "handler.h" +#include "storage/queryhelper.h" + +using namespace Akonadi; + +void ItemQueryHelper::itemSetToQuery(const ImapSet& set, QueryBuilder& qb, const Collection& collection ) +{ + QueryHelper::setToQuery( set, PimItem::idFullColumnName(), qb ); + if ( collection.isValid() ) { + // FIXME: we probably want to do both paths here in all cases, but that is apparently + // non-trivial with SQL + if ( collection.resource().name() == QLatin1String("akonadi_search_resource") || + collection.resource().name().contains( QLatin1String("nepomuk") ) ) + { + qb.addTable( CollectionPimItemRelation::tableName() ); + qb.addValueCondition( CollectionPimItemRelation::leftFullColumnName(), Query::Equals, collection.id() ); + qb.addColumnCondition( CollectionPimItemRelation::rightFullColumnName(), Query::Equals, + PimItem::idFullColumnName() ); + } else { + qb.addValueCondition( PimItem::collectionIdColumn(), Query::Equals, collection.id() ); + } + } +} + +void ItemQueryHelper::itemSetToQuery(const ImapSet& set, bool isUid, AkonadiConnection* connection, QueryBuilder& qb) +{ + if ( !isUid && connection->selectedCollectionId() >= 0 ) + itemSetToQuery( set, qb, connection->selectedCollection() ); + else + itemSetToQuery( set, qb ); +} + +void ItemQueryHelper::remoteIdToQuery(const QStringList& rids, AkonadiConnection* connection, QueryBuilder& qb) +{ + if ( rids.size() == 1 ) + qb.addValueCondition( PimItem::remoteIdFullColumnName(), Query::Equals, rids.first() ); + else + qb.addValueCondition( PimItem::remoteIdFullColumnName(), Query::In, rids ); + + if ( connection->selectedCollectionId() > 0 ) { + qb.addTable( Collection::tableName() ); + qb.addValueCondition( PimItem::collectionIdFullColumnName(), Query::Equals, connection->selectedCollectionId() ); + } else if ( connection->resourceContext().isValid() ) { + qb.addTable( Collection::tableName() ); + qb.addColumnCondition( PimItem::collectionIdFullColumnName(), Query::Equals, Collection::idFullColumnName() ); + qb.addValueCondition( Collection::resourceIdFullColumnName(), Query::Equals, connection->resourceContext().id() ); + } +} + +void ItemQueryHelper::scopeToQuery(const Scope& scope, AkonadiConnection* connection, QueryBuilder& qb) +{ + if ( scope.scope() == Scope::None || scope.scope() == Scope::Uid ) { + itemSetToQuery( scope.uidSet(), scope.scope() == Scope::Uid, connection, qb ); + } else if ( scope.scope() == Scope::Rid ) { + if ( connection->selectedCollectionId() <= 0 && !connection->resourceContext().isValid() ) + throw HandlerException( "Operations based on remote identifiers require a resource or collection context" ); + ItemQueryHelper::remoteIdToQuery( scope.ridSet(), connection, qb ); + } else + throw HandlerException( "WTF?" ); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemqueryhelper.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemqueryhelper.h --- akonadi-1.1.1/server/src/storage/itemqueryhelper.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemqueryhelper.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,65 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_ITEMQUERYHELPER_H +#define AKONADI_ITEMQUERYHELPER_H + +#include "entities.h" +#include "handler/scope.h" + +namespace Akonadi { + +class AkonadiConnection; +class ImapSet; +class QueryBuilder; +class Scope; + +/** + Helper methods to generate WHERE clauses for item queries based on the item set + used in the protocol. +*/ +namespace ItemQueryHelper +{ + /** + Add conditions to @p qb for the given item set @p set. If @p collection is valid, + only items in this collection are considered. + */ + void itemSetToQuery( const ImapSet &set, QueryBuilder &qb, const Collection &collection = Collection() ); + + /** + Convenience method, does essentially the same as the one above. + */ + void itemSetToQuery( const ImapSet &set, bool isUid, AkonadiConnection *connection, QueryBuilder &qb ); + + /** + Add conditions to @p qb for the given remote identifier @p rid. + The rid context is taken from @p connection. + */ + void remoteIdToQuery( const QStringList &rids, AkonadiConnection* connection, QueryBuilder &qb ); + + /** + Add conditions to @p qb for the given item operation scope @p scope. + The rid context is taken from @p connection, if none is specified an exception is thrown. + */ + void scopeToQuery( const Scope &scope, AkonadiConnection* connection, QueryBuilder &qb ); +} + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemretrievalmanager.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemretrievalmanager.cpp --- akonadi-1.1.1/server/src/storage/itemretrievalmanager.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemretrievalmanager.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,216 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "itemretrievalmanager.h" + +#include "resourceinterface.h" +#include "akdebug.h" + +#include +#include +#include +#include +#include + +using namespace Akonadi; + +ItemRetrievalManager::Request::Request() : + processed( false ) +{ +} + + +ItemRetrievalManager* ItemRetrievalManager::sInstance = 0; + +ItemRetrievalManager::ItemRetrievalManager( QObject *parent ) : + QObject( parent ) +{ + // make sure we are created from the retrieval thread and only once + Q_ASSERT( QThread::currentThread() != QCoreApplication::instance()->thread() ); + Q_ASSERT( sInstance == 0 ); + sInstance = this; + + mLock = new QReadWriteLock(); + mWaitCondition = new QWaitCondition(); + + connect( QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SLOT(serviceOwnerChanged(QString,QString,QString)) ); + connect( this, SIGNAL(requestAdded()), this, SLOT(processRequest()), Qt::QueuedConnection ); + connect( this, SIGNAL(syncCollection(QString,qint64)), this, SLOT(triggerCollectionSync(QString,qint64)), Qt::QueuedConnection ); +} + +ItemRetrievalManager::~ItemRetrievalManager() +{ + delete mWaitCondition; + delete mLock; +} + +ItemRetrievalManager* ItemRetrievalManager::instance() +{ + Q_ASSERT( sInstance ); + return sInstance; +} + +// called within the retrieval thread +void ItemRetrievalManager::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner, const QString& newOwner) +{ + Q_UNUSED( newOwner ); + if ( oldOwner.isEmpty() ) + return; + if ( !serviceName.startsWith( QLatin1String("org.freedesktop.Akonadi.Resource.") ) ) + return; + const QString resourceId = serviceName.mid( 33 ); + qDebug() << "Lost connection to resource" << serviceName << ", discarding cached interface"; + mResourceInterfaces.remove( resourceId ); +} + +// called within the retrieval thread +OrgFreedesktopAkonadiResourceInterface* ItemRetrievalManager::resourceInterface(const QString& id) +{ + if ( id.isEmpty() ) + return 0; + + OrgFreedesktopAkonadiResourceInterface *iface = 0; + if ( mResourceInterfaces.contains( id ) ) + iface = mResourceInterfaces.value( id ); + if ( iface && iface->isValid() ) + return iface; + + delete iface; + iface = new OrgFreedesktopAkonadiResourceInterface( QLatin1String("org.freedesktop.Akonadi.Resource.") + id, + QLatin1String("/"), QDBusConnection::sessionBus(), this ); + if ( !iface || !iface->isValid() ) { + qDebug() << QString::fromLatin1( "Cannot connect to agent instance with identifier '%1', error message: '%2'" ) + .arg( id, iface ? iface->lastError().message() : QString() ); + delete iface; + return 0; + } + mResourceInterfaces.insert( id, iface ); + return iface; +} + +// called from any thread +void ItemRetrievalManager::requestItemDelivery( qint64 uid, const QByteArray& remoteId, const QByteArray& mimeType, + const QString& resource, const QStringList& parts ) +{ + qDebug() << "requestItemDelivery() - current thread:" << QThread::currentThread() << " retrieval thread:" << thread(); + + Request *req = new Request(); + req->id = uid; + req->remoteId = remoteId; + req->mimeType = mimeType; + req->resourceId = resource; + req->parts = parts; + + mLock->lockForWrite(); + qDebug() << "posting retrieval request for item" << uid ; + mPendingRequests.append( req ); + mLock->unlock(); + + emit requestAdded(); + + mLock->lockForRead(); + forever { + qDebug() << "checking if request for item" << uid << "has been processed..."; + if ( req->processed ) { + Q_ASSERT( !mPendingRequests.contains( req ) ); + const QString errorMsg = req->errorMsg; + mLock->unlock(); + qDebug() << "request for item" << uid << "processed, error:" << errorMsg; + delete req; + if ( errorMsg.isEmpty() ) + return; + else + throw ItemRetrieverException( errorMsg ); + } else { + qDebug() << "request for item" << uid << "still pending - waiting"; + mWaitCondition->wait( mLock ); + qDebug() << "continuing"; + } + } + + throw ItemRetrieverException( "WTF?" ); +} + +// called within the retrieval thread +void ItemRetrievalManager::processRequest() +{ + qDebug() << "processRequest() - current thread:" << QThread::currentThread() << " retrieval thread:" << thread(); + mLock->lockForRead(); + // TODO: check if there is another one for the same uid with more parts requested + Request *currentRequest = 0; + if ( !mPendingRequests.isEmpty() ) + currentRequest = mPendingRequests.first(); + mLock->unlock(); + + if ( !currentRequest ) { + akError() << "WTF: processRequest() called but no request queued!?"; + mWaitCondition->wakeAll(); + return; + } + + qDebug() << "processing retrieval request for item" << currentRequest->id << " parts:" << currentRequest->parts; + // call the resource + QString errorMsg; + OrgFreedesktopAkonadiResourceInterface *interface = resourceInterface( currentRequest->resourceId ); + if ( interface ) { + QDBusReply reply = interface->requestItemDelivery( currentRequest->id, + QString::fromUtf8( currentRequest->remoteId ), + QString::fromUtf8( currentRequest->mimeType ), + currentRequest->parts ); + if ( !reply.isValid() ) + errorMsg = QString::fromLatin1( "Unable to retrieve item from resource: %1" ).arg( reply.error().message() ); + else if ( reply.value() == false ) + errorMsg = QString::fromLatin1( "Resource was unable to deliver item" ); + } else { + errorMsg = QString::fromLatin1( "Unable to contact resource" ); + } + + mLock->lockForWrite(); + currentRequest->errorMsg = errorMsg; + currentRequest->processed = true; + mPendingRequests.removeAll( currentRequest ); + // TODO check if (*it)->parts is a subset of currentRequest->parts + for ( QList::Iterator it = mPendingRequests.begin(); it != mPendingRequests.end(); ) { + if ( (*it)->id == currentRequest->id ) { + qDebug() << "someone else requested item" << currentRequest->id << "as well, marking as processed"; + (*it)->errorMsg = errorMsg; + (*it)->processed = true; + it = mPendingRequests.erase( it ); + } else { + ++it; + } + } + mWaitCondition->wakeAll(); + mLock->unlock(); +} + +void ItemRetrievalManager::requestCollectionSync(const Collection& collection) +{ + emit syncCollection( collection.resource().name(), collection.id() ); +} + +void ItemRetrievalManager::triggerCollectionSync(const QString& resource, qint64 colId) +{ + OrgFreedesktopAkonadiResourceInterface *interface = resourceInterface( resource ); + if ( interface ) + interface->synchronizeCollection( colId ); +} + +#include "itemretrievalmanager.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemretrievalmanager.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemretrievalmanager.h --- akonadi-1.1.1/server/src/storage/itemretrievalmanager.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemretrievalmanager.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,92 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_ITEMRETRIEVERMANAGER_H +#define AKONADI_ITEMRETRIEVERMANAGER_H + +#include "itemretriever.h" + +#include +#include +#include + +class QReadWriteLock; +class QWaitCondition; +class OrgFreedesktopAkonadiResourceInterface; + +namespace Akonadi { + +class Collection; + +/** Manages and processes item retrieval requests. */ +class ItemRetrievalManager : public QObject +{ + Q_OBJECT + public: + ItemRetrievalManager( QObject *parent = 0 ); + ~ItemRetrievalManager(); + + void requestItemDelivery( qint64 uid, const QByteArray& remoteId, const QByteArray& mimeType, + const QString &resource, const QStringList &parts ); + void requestCollectionSync( const Collection &collection ); + + static ItemRetrievalManager* instance(); + + signals: + void requestAdded(); + void syncCollection( const QString &resource, qint64 colId ); + + private: + OrgFreedesktopAkonadiResourceInterface* resourceInterface( const QString &id ); + + private slots: + void serviceOwnerChanged( const QString &serviceName, const QString &oldOwner, const QString &newOwner ); + void processRequest(); + void triggerCollectionSync( const QString &resource, qint64 colId ); + + private: + class Request + { + public: + Request(); + qint64 id; + QByteArray remoteId; + QByteArray mimeType; + QString resourceId; + QStringList parts; + QString errorMsg; + bool processed; + private: + Q_DISABLE_COPY( Request ) + }; + + static ItemRetrievalManager *sInstance; + /// Protects mPendingRequests and every Request object posted to it + QReadWriteLock *mLock; + /// Used to let requesting threads wait until the request has been processed + QWaitCondition *mWaitCondition; + QList mPendingRequests; + + // resource dbus interface cache + QHash mResourceInterfaces; +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemretrievalthread.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemretrievalthread.cpp --- akonadi-1.1.1/server/src/storage/itemretrievalthread.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemretrievalthread.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,41 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "itemretrievalthread.h" +#include "itemretrievalmanager.h" + +#include + +using namespace Akonadi; + +ItemRetrievalThread::ItemRetrievalThread( QObject *parent ) : + QThread( parent ) +{ + // make sure we are created from the main thread, ie. before all other threads start to potentially use us + Q_ASSERT( QThread::currentThread() == QCoreApplication::instance()->thread() ); +} + +void ItemRetrievalThread::run() +{ + ItemRetrievalManager *mgr = new ItemRetrievalManager(); + exec(); + delete mgr; +} + +#include "itemretrievalthread.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemretrievalthread.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemretrievalthread.h --- akonadi-1.1.1/server/src/storage/itemretrievalthread.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemretrievalthread.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,42 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_ITEMRETRIEVERTHREAD_H +#define AKONADI_ITEMRETRIEVERTHREAD_H + +#include "itemretriever.h" + +#include + +namespace Akonadi { + +/** Container thread for the item retrieval manager. */ +class ItemRetrievalThread : public QThread +{ + Q_OBJECT + public: + ItemRetrievalThread( QObject *parent = 0 ); + + protected: + /* reimpl */ void run(); +}; + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemretriever.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemretriever.cpp --- akonadi-1.1.1/server/src/storage/itemretriever.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemretriever.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,217 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "itemretriever.h" + +#include "akonadiconnection.h" +#include "storage/itemqueryhelper.h" +#include "storage/itemretrievalmanager.h" +#include "storage/parthelper.h" +#include "storage/querybuilder.h" + +#include + +using namespace Akonadi; + +ItemRetriever::ItemRetriever( AkonadiConnection *connection ) : + mScope( Scope::Invalid ), + mConnection( connection ), + mFullPayload( false ), + mRecursive( false ) +{ +} + +void ItemRetriever::setRetrieveParts(const QStringList& parts) +{ + mParts = parts; +} + +void ItemRetriever::setItemSet(const ImapSet& set, const Collection &collection ) +{ + mItemSet = set; + mCollection = collection; +} + +void ItemRetriever::setItemSet(const ImapSet& set, bool isUid) +{ + Q_ASSERT( mConnection ); + if ( !isUid && mConnection->selectedCollectionId() >= 0 ) + setItemSet( set, mConnection->selectedCollection() ); + else + setItemSet( set ); +} + +void ItemRetriever::setItem( const Akonadi::Entity::Id& id ) +{ + ImapSet set; + set.add( ImapInterval( id, id ) ); + mItemSet = set; + mCollection = Collection(); +} + +void ItemRetriever::setRetrieveFullPayload(bool fullPayload) +{ + mFullPayload = fullPayload; + // HACK, we need a full payload available flag in PimItem + if ( fullPayload && !mParts.contains( QLatin1String( "PLD:RFC822" ) ) ) + mParts += QLatin1String( "PLD:RFC822" ); +} + +void ItemRetriever::setCollection(const Collection& collection, bool recursive) +{ + mCollection = collection; + mItemSet = ImapSet(); + mRecursive = recursive; +} + +void ItemRetriever::setScope(const Scope& scope) +{ + mScope = scope; +} + +static const int itemQueryIdColumn = 0; +static const int itemQueryRidColumn = 1; +static const int itemQueryMimeTypeColumn = 2; +static const int itemQueryResouceColumn = 3; + +Akonadi::QueryBuilder ItemRetriever::buildItemQuery() const +{ + QueryBuilder itemQuery; + itemQuery.addTable( PimItem::tableName() ); + itemQuery.addTable( MimeType::tableName() ); + itemQuery.addTable( Collection::tableName() ); + itemQuery.addTable( Resource::tableName() ); + // make sure the columns indexes here and in the constants defined above match + itemQuery.addColumn( PimItem::idFullColumnName() ); + itemQuery.addColumn( PimItem::remoteIdFullColumnName() ); + itemQuery.addColumn( MimeType::nameFullColumnName() ); + itemQuery.addColumn( Resource::nameFullColumnName() ); + itemQuery.addColumnCondition( PimItem::mimeTypeIdFullColumnName(), Query::Equals, MimeType::idFullColumnName() ); + itemQuery.addColumnCondition( PimItem::collectionIdFullColumnName(), Query::Equals, Collection::idFullColumnName() ); + itemQuery.addColumnCondition( Collection::resourceIdFullColumnName(), Query::Equals, Resource::idFullColumnName() ); + + // prevent a resource to trigger item retrieval from itself + if ( mConnection ) { + itemQuery.addValueCondition( Resource::nameFullColumnName(), Query::NotEquals, + QString::fromLatin1( mConnection->sessionId() ) ); + } + + if ( mScope.scope() != Scope::Invalid ) + ItemQueryHelper::scopeToQuery( mScope, mConnection, itemQuery ); + else + ItemQueryHelper::itemSetToQuery( mItemSet, itemQuery, mCollection ); + itemQuery.addSortColumn( PimItem::idFullColumnName(), Query::Ascending ); + + + if ( !itemQuery.exec() ) + throw ItemRetrieverException( "Unable to list items" ); + + itemQuery.query().next(); + return itemQuery; +} + +static const int partQueryPimIdColumn = 0; +static const int partQueryIdColumn = 1; +static const int partQueryNameColumn = 2; +static const int partQueryDataColumn = 3; +static const int partQueryExternalColumn = 4; + +Akonadi::QueryBuilder ItemRetriever::buildPartQuery() const +{ + QueryBuilder partQuery; + partQuery.addTable( PimItem::tableName() ); + partQuery.addTable( Part::tableName() ); + partQuery.addColumn( PimItem::idFullColumnName() ); + partQuery.addColumn( Part::idFullColumnName() ); + partQuery.addColumn( Part::nameFullColumnName() ); + partQuery.addColumn( Part::dataFullColumnName() ); + partQuery.addColumn( Part::externalFullColumnName() ); + partQuery.addColumnCondition( PimItem::idFullColumnName(), Query::Equals, Part::pimItemIdFullColumnName() ); + partQuery.addValueCondition( QString::fromLatin1( "substr(%1, 1, 4 )" ).arg( Part::nameFullColumnName() ), Query::Equals, QLatin1String( "PLD:" ) ); + + if ( !mParts.isEmpty() ) + partQuery.addValueCondition( Part::nameFullColumnName(), Query::In, mParts ); + + ItemQueryHelper::itemSetToQuery( mItemSet, partQuery, mCollection ); + partQuery.addSortColumn( PimItem::idFullColumnName(), Query::Ascending ); + return partQuery; +} + +void ItemRetriever::exec() +{ + qDebug() << "ItemRetriever::exec()"; + if ( mParts.isEmpty() && !mFullPayload ) + return; + + // TODO: I'm sure this can be done with a single query instead of manually + QueryBuilder itemQuery = buildItemQuery(); + QueryBuilder partQuery = buildPartQuery(); + if ( !partQuery.exec() ) + throw ItemRetrieverException( "Unable to retrieve item parts" ); + partQuery.query().next(); + + while ( itemQuery.query().isValid() ) { + const qint64 pimItemId = itemQuery.query().value( itemQueryIdColumn ).toLongLong(); + QStringList missingParts = mParts; + while ( partQuery.query().isValid() ) { + const qint64 id = partQuery.query().value( partQueryPimIdColumn ).toLongLong(); + if ( id < pimItemId ) { + partQuery.query().next(); + continue; + } else if ( id > pimItemId ) { + break; + } + const QString partName = partQuery.query().value( partQueryNameColumn ).toString(); + qint64 partId = partQuery.query().value( partQueryIdColumn ).toLongLong(); + QByteArray data = partQuery.query().value( partQueryDataColumn ).toByteArray(); + // FIXME: loading the actual data is not needed here! + // ### maybe add an flag indicating if a part is cached? + data = PartHelper::translateData(partId, data, partQuery.query().value( partQueryExternalColumn ).toBool()); + if ( data.isNull() ) { + if ( mFullPayload && !missingParts.contains( partName ) ) + missingParts << partName; + } else { + missingParts.removeAll( partName ); + } + partQuery.query().next(); + } + if ( !missingParts.isEmpty() ) { + QStringList missingPayloadIds; + foreach ( const QString &s, missingParts ) + missingPayloadIds << s.mid( 4 ); + ItemRetrievalManager::instance()->requestItemDelivery( pimItemId, + itemQuery.query().value( itemQueryRidColumn ).toString().toUtf8(), + itemQuery.query().value( itemQueryMimeTypeColumn ).toString().toUtf8(), + itemQuery.query().value( itemQueryResouceColumn ).toString(), missingPayloadIds ); + } + itemQuery.query().next(); + } + + // retrieve items in child collections if requested + if ( mRecursive && mCollection.isValid() ) { + foreach ( const Collection &col, mCollection.children() ) { + ItemRetriever retriever( mConnection ); + retriever.setCollection( col, mRecursive ); + retriever.setRetrieveParts( mParts ); + retriever.setRetrieveFullPayload( mFullPayload ); + retriever.exec(); + } + } + qDebug() << "ItemRetriever::exec() done"; +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/itemretriever.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/itemretriever.h --- akonadi-1.1.1/server/src/storage/itemretriever.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/itemretriever.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,78 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef ITEMRETRIEVER_H +#define ITEMRETRIEVER_H + +#include "exception.h" +#include "entities.h" +#include "handler/scope.h" + +#include "libs/imapset_p.h" + +#include + +AKONADI_EXCEPTION_MAKE_INSTANCE( ItemRetrieverException ); + +namespace Akonadi { + class AkonadiConnection; + class QueryBuilder; + +/** + Helper class for retrieving missing items parts from remote resources. + + Stuff in here happens in the calling thread and does not access shared data. + + @todo make usable for Fetch by allowing to share queries +*/ +class ItemRetriever +{ + public: + ItemRetriever( AkonadiConnection *connection ); + + void setRetrieveParts( const QStringList &parts ); + void setRetrieveFullPayload( bool fullPayload ); + void setItemSet( const ImapSet &set, const Collection &collection = Collection() ); + void setItemSet( const ImapSet &set, bool isUid ); + void setItem( const Akonadi::Entity::Id &id ); + /** Retrieve all items in the given collection. */ + void setCollection( const Akonadi::Collection &collection, bool recursive = true ); + /** Retrieve all items matching the given item scope. */ + void setScope( const Scope &scope ); + + void exec(); + + private: + QueryBuilder buildItemQuery() const; + QueryBuilder buildPartQuery() const; + + private: + ImapSet mItemSet; + Collection mCollection; + Scope mScope; + AkonadiConnection* mConnection; + QStringList mParts; + bool mFullPayload; + bool mRecursive; +}; + +} + +#endif + diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/make-unittest-mysql-embedded.sh /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/make-unittest-mysql-embedded.sh --- akonadi-1.1.1/server/src/storage/make-unittest-mysql-embedded.sh 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/make-unittest-mysql-embedded.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,43 +0,0 @@ -#!/bin/sh -if [ -L $0 ]; then - abs_path=`readlink $0` -else - abs_path=$0 -fi - -pushd `dirname $abs_path` > /dev/null - -akonadidb=akonadi -socketfile=$HOME/.local/share/akonadi/db_misc/mysql.socket -logfile=$HOME/.local/share/akonadi/db_data/mysql-bin.index -if ! [ -S $socketfile ]; then - startserver=true -else - unset startserver -fi - -if ! [ -z "$startserver" ]; then - akonadihome=$HOME/.local/share/akonadi - globalconfig=$KDEDIR/share/akonadi/mysql-global.conf - localconfig=$HOME/.config/akonadi/mysql-local.conf - if [ -f $globalconfig ]; then - cat $globalconfig $localconfig > $akonadihome/mysql.conf - fi - - /usr/sbin/mysqld \ - --defaults-file=$akonadihome/mysql.conf \ - --datadir=$akonadihome/db_data/ \ - --socket=$akonadihome/db_misc/mysql.socket \ - --skip-grant-tables \ - --skip-networking & - - sleep 2; -fi - -cat create-unittest-values.sql | mysql --socket=$socketfile -D $akonadidb - -if ! [ -z "$startserver" ]; then - killall mysqld -fi - -popd > /dev/null diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/make-unittest-mysql.sh /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/make-unittest-mysql.sh --- akonadi-1.1.1/server/src/storage/make-unittest-mysql.sh 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/make-unittest-mysql.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,14 +0,0 @@ -#!/bin/sh -if [ -L $0 ]; then - abs_path=`readlink $0` -else - abs_path=$0 -fi - -pushd `dirname $abs_path` > /dev/null - -akonadidb=akonadi - -cat create-unittest-values.sql | mysql -u root -D $akonadidb - -popd > /dev/null diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/make-unittest-sqlite.sh /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/make-unittest-sqlite.sh --- akonadi-1.1.1/server/src/storage/make-unittest-sqlite.sh 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/make-unittest-sqlite.sh 1970-01-01 01:00:00.000000000 +0100 @@ -1,14 +0,0 @@ -#!/bin/sh -if [ -L $0 ]; then - abs_path=`readlink $0` -else - abs_path=$0 -fi - -pushd `dirname $abs_path` > /dev/null - -akonadidb=$HOME/.akonadi/akonadi.db - -cat create-unittest-values.sql | sqlite3 $akonadidb - -popd > /dev/null diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/notificationcollector.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/notificationcollector.cpp --- akonadi-1.1.1/server/src/storage/notificationcollector.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/notificationcollector.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -39,24 +39,25 @@ } void Akonadi::NotificationCollector::itemAdded( const PimItem &item, - const Location &collection, + const Collection &collection, const QString & mimeType, const QByteArray & resource ) { - itemNotification( NotificationMessage::Add, item, collection, Location(), mimeType, resource ); + itemNotification( NotificationMessage::Add, item, collection, Collection(), mimeType, resource ); } void Akonadi::NotificationCollector::itemChanged( const PimItem &item, - const Location &collection, + const QSet &changedParts, + const Collection &collection, const QString & mimeType, const QByteArray & resource ) { - itemNotification( NotificationMessage::Modify, item, collection, Location(), mimeType, resource ); + itemNotification( NotificationMessage::Modify, item, collection, Collection(), mimeType, resource, changedParts ); } void Akonadi::NotificationCollector::itemMoved( const PimItem &item, - const Location &collectionSrc, - const Location &collectionDest, + const Collection &collectionSrc, + const Collection &collectionDest, const QString &mimeType, const QByteArray &resource ) { @@ -65,36 +66,37 @@ void Akonadi::NotificationCollector::itemRemoved( const PimItem &item, - const Location &collection, + const Collection &collection, const QString & mimeType, const QByteArray & resource ) { - itemNotification( NotificationMessage::Remove, item, collection, Location(), mimeType, resource ); + itemNotification( NotificationMessage::Remove, item, collection, Collection(), mimeType, resource ); } -void NotificationCollector::itemLinked(const PimItem & item, const Location & collection) +void NotificationCollector::itemLinked(const PimItem & item, const Collection & collection) { - itemNotification( NotificationMessage::Link, item, collection, Location(), QString(), QByteArray() ); + itemNotification( NotificationMessage::Link, item, collection, Collection(), QString(), QByteArray() ); } -void NotificationCollector::itemUnlinked(const PimItem & item, const Location & collection) +void NotificationCollector::itemUnlinked(const PimItem & item, const Collection & collection) { - itemNotification( NotificationMessage::Unlink, item, collection, Location(), QString(), QByteArray() ); + itemNotification( NotificationMessage::Unlink, item, collection, Collection(), QString(), QByteArray() ); } -void Akonadi::NotificationCollector::collectionAdded( const Location &collection, +void Akonadi::NotificationCollector::collectionAdded( const Collection &collection, const QByteArray &resource ) { collectionNotification( NotificationMessage::Add, collection, resource ); } -void Akonadi::NotificationCollector::collectionChanged( const Location &collection, +void Akonadi::NotificationCollector::collectionChanged( const Collection &collection, + const QList &changes, const QByteArray &resource ) { - collectionNotification( NotificationMessage::Modify, collection, resource ); + collectionNotification( NotificationMessage::Modify, collection, resource, changes.toSet() ); } -void Akonadi::NotificationCollector::collectionRemoved( const Location &collection, +void Akonadi::NotificationCollector::collectionRemoved( const Collection &collection, const QByteArray &resource ) { collectionNotification( NotificationMessage::Remove, collection, resource ); @@ -123,22 +125,24 @@ void NotificationCollector::itemNotification( NotificationMessage::Operation op, const PimItem & item, - const Location & collection, - const Location & collectionDest, + const Collection & collection, + const Collection & collectionDest, const QString & mimeType, - const QByteArray & resource) + const QByteArray & resource, + const QSet &parts ) { NotificationMessage msg; msg.setSessionId( mSessionId ); msg.setType( NotificationMessage::Item ); msg.setOperation( op ); msg.setUid( item.id() ); - msg.setRemoteId( QString::fromUtf8( item.remoteId() ) ); + msg.setRemoteId( item.remoteId() ); + msg.setItemParts( parts ); - Location loc = collection; - if ( !loc.isValid() ) - loc = item.location(); - msg.setParentCollection( loc.id() ); + Collection col = collection; + if ( !col.isValid() ) + col = item.collection(); + msg.setParentCollection( col.id() ); // will be valid if it is a move message msg.setParentDestCollection( collectionDest.id() ); QString mt = mimeType; @@ -147,14 +151,15 @@ msg.setMimeType( mt ); QByteArray res = resource; if ( res.isEmpty() ) - res = loc.resource().name().toLatin1(); + res = col.resource().name().toLatin1(); msg.setResource( res ); dispatchNotification( msg ); } void NotificationCollector::collectionNotification( NotificationMessage::Operation op, - const Location & collection, - const QByteArray & resource) + const Collection & collection, + const QByteArray & resource, + const QSet &changes ) { NotificationMessage msg; msg.setType( NotificationMessage::Collection ); @@ -163,6 +168,7 @@ msg.setUid( collection.id() ); msg.setRemoteId( collection.remoteId() ); msg.setParentCollection( collection.parentId() ); + msg.setItemParts( changes ); QByteArray res = resource; if ( res.isEmpty() ) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/notificationcollector.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/notificationcollector.h --- akonadi-1.1.1/server/src/storage/notificationcollector.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/notificationcollector.h 2009-07-28 17:52:07.000000000 +0100 @@ -64,7 +64,7 @@ Provide as many parameters as you have at hand currently, everything that is missing will be looked up in the database later. */ - void itemAdded( const PimItem &item, const Location &collection = Location(), + void itemAdded( const PimItem &item, const Collection &collection = Collection(), const QString &mimeType = QString(), const QByteArray &resource = QByteArray() ); @@ -73,7 +73,9 @@ Provide as many parameters as you have at hand currently, everything that is missing will be looked up in the database later. */ - void itemChanged( const PimItem &item, const Location &collection = Location(), + void itemChanged( const PimItem &item, + const QSet &changedParts, + const Collection &collection = Collection(), const QString &mimeType = QString(), const QByteArray &resource = QByteArray() ); @@ -82,8 +84,8 @@ Provide as many parameters as you have at hand currently, everything that is missing will be looked up in the database later. */ - void itemMoved( const PimItem &item, const Location &collectionSrc = Location(), - const Location &collectionDest = Location(), + void itemMoved( const PimItem &item, const Collection &collectionSrc = Collection(), + const Collection &collectionDest = Collection(), const QString &mimeType = QString(), const QByteArray &resource = QByteArray() ); @@ -92,26 +94,26 @@ Make sure you either provide all parameters or call this function before actually removing the item from database. */ - void itemRemoved( const PimItem &item, const Location &collection = Location(), + void itemRemoved( const PimItem &item, const Collection &collection = Collection(), const QString &mimeType = QString(), const QByteArray &resource = QByteArray() ); /** * Notify about a linked item. */ - void itemLinked( const PimItem &item, const Location &collection ); + void itemLinked( const PimItem &item, const Collection &collection ); /** * Notify about a unlinked item. */ - void itemUnlinked( const PimItem &item, const Location &collection ); + void itemUnlinked( const PimItem &item, const Collection &collection ); /** Notify about a added collection. Provide as many parameters as you have at hand currently, everything that is missing will be looked up in the database later. */ - void collectionAdded( const Location &collection, + void collectionAdded( const Collection &collection, const QByteArray &resource = QByteArray() ); /** @@ -119,7 +121,8 @@ Provide as many parameters as you have at hand currently, everything that is missing will be looked up in the database later. */ - void collectionChanged( const Location &collection, + void collectionChanged( const Collection &collection, + const QList &changes, const QByteArray &resource = QByteArray() ); /** @@ -127,7 +130,7 @@ Make sure you either provide all parameters or call this function before actually removing the item from database. */ - void collectionRemoved( const Location &collection, + void collectionRemoved( const Collection &collection, const QByteArray &resource = QByteArray() ); Q_SIGNALS: @@ -135,13 +138,15 @@ private: void itemNotification( NotificationMessage::Operation op, const PimItem &item, - const Location &collection, - const Location &collectionDest, + const Collection &collection, + const Collection &collectionDest, const QString &mimeType, - const QByteArray &resource ); + const QByteArray &resource, + const QSet &parts = QSet() ); void collectionNotification( NotificationMessage::Operation op, - const Location &collection, - const QByteArray &resource ); + const Collection &collection, + const QByteArray &resource, + const QSet &changes = QSet() ); void dispatchNotification( const NotificationMessage &msg ); void clear(); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/parthelper.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/parthelper.cpp --- akonadi-1.1.1/server/src/storage/parthelper.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/parthelper.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (C) 2009 by Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "parthelper.h" +#include "entities.h" +#include "selectquerybuilder.h" +#include "dbconfig.h" +#include "../../libs/xdgbasedirs_p.h" + +#include +#include +#include + +using namespace Akonadi; + +PartHelper::PartHelper() +{ +} + + +PartHelper::~PartHelper() +{ +} + +bool PartHelper::update( Part *part, const QByteArray &data, qint64 dataSize ) +{ + if (!part) + return false; + + if (DbConfig::useExternalPayloadFile() && part->external()) + { + QString origFileName = QString::fromUtf8( part->data() ); + QString fileName = origFileName; + QString rev = QString::fromAscii("_r0"); + if (fileName.contains( QString::fromAscii("_r") )) + { + int revIndex = fileName.indexOf(QString::fromAscii("_r")); + rev = fileName.mid( revIndex + 2 ); + int r = rev.toInt(); + r++; + rev = QString::number( r ); + fileName = fileName.left( revIndex ); + rev.prepend( QString::fromAscii("_r") ); + } + fileName += rev; + + QFile file(fileName); + + if (file.open( QIODevice::WriteOnly | QIODevice::Truncate )) + { + qDebug() << "Update part file " << fileName <<" with " << QString::fromUtf8(data).left(50); + + file.write( data ); + QByteArray fileNameData = fileName.toLocal8Bit(); + part->setData( fileNameData ); + part->setDatasize( fileNameData.size() ); + part->setExternal( true ); + file.close(); + qDebug() << "Removing part file " << origFileName; + QFile::remove(origFileName); + } else + { + qDebug() << "Update: payload file " << fileName << " could not be open for writing!"; + qDebug() << "Error: " << file.errorString(); + return false; + } + } else + { + part->setData( data ); + part->setDatasize( dataSize ); + part->setExternal( false ); + } + return part->update(); +} + +bool PartHelper::insert( Part *part, qint64* insertId ) +{ + if (!part) + return false; + +// qDebug() << "Insert original data " << part->data(); + QByteArray fileNameData(""); + QByteArray data; + bool storeInFile = DbConfig::useExternalPayloadFile() && ( part->datasize() > DbConfig::sizeThreshold() || part->external() ); + + //it is needed to insert first the metadata so a new id is generated for the part, + //and we need this id for the payload file name + if (storeInFile) + { + data = part->data(); + part->setData( fileNameData ); + part->setDatasize( fileNameData.size() ); + part->setExternal( true ); + } else + { + part->setExternal( false ); + } + + bool result = part->insert( insertId ); + + if (storeInFile && result) + { + QString fileName = PartHelper::fileNameForId( part->id() ); + fileName += QString::fromUtf8("_r0"); + + QFile file( fileName ); + + if (file.open( QIODevice::WriteOnly | QIODevice::Truncate )) + { + qDebug() << "Insert: create part file " << fileName << "with " << QString::fromUtf8(data).left(50); + + file.write(data); + fileNameData = fileName.toLocal8Bit(); + part->setData(fileNameData); + part->setDatasize(fileNameData.size()); + part->update(); + file.close(); + } else + { + qDebug() << "Insert: payload file " << fileName << " could not be open for writing!"; + qDebug() << "Error: " << file.errorString(); + return false; + } + } + return result; +} + +bool PartHelper::remove( Akonadi::Part *part ) +{ + if (!part) + return false; + + if (DbConfig::useExternalPayloadFile() && part->external()) + { + qDebug() << "remove part file " << part->data(); + QString fileName = QString::fromUtf8( part->data() ); + QFile::remove( fileName ); + } + return part->remove(); +} + +bool PartHelper::remove( const QString &column, const QVariant &value ) +{ + if ( DbConfig::useExternalPayloadFile() ) + { + SelectQueryBuilder builder; + builder.addValueCondition( column, Query::Equals, value ); + if ( !builder.exec() ) { + qDebug() << "Error selecting records to be deleted from table" + << Part::tableName() << builder.query().lastError().text(); + return false; + } + Part::List parts = builder.result(); + Part::List::Iterator it = parts.begin(); + Part::List::Iterator end = parts.end(); + for ( ; it != end; ++it ) + { + if ((*it).external()) + { + QString fileName = QString::fromUtf8( (*it).data() ); + qDebug() << "remove part file " << fileName; + QFile::remove( fileName ); + } + } + } + return Part::remove( column, value ); +} + +bool PartHelper::loadData( Part::List &parts ) +{ + Part::List::Iterator it = parts.begin(); + Part::List::Iterator end = parts.end(); + for ( ; it != end; ++it ) + { + if ( !loadData( (*it) ) ) + { + return false; + } + } + + return true; +} + +bool PartHelper::loadData( Part &part ) +{ + if ( DbConfig::useExternalPayloadFile() && part.external() ) + { + QString fileName = QString::fromUtf8( part.data() ); + QFile file( fileName ); + if (file.open( QIODevice::ReadOnly )) + { + QByteArray data = file.readAll(); + part.setData( data ); + part.setDatasize( data.size() ); + qDebug() << "load part file " << fileName << QString::fromUtf8(data).left(50); + file.close(); + } else + { + qDebug() << "Payload file " << fileName << " could not be open for reading!"; + qDebug() << "Error: " << file.errorString(); + return false; + } + } else + if ( part.external() ) //external payload is disabled, but the item is marked as external + { + return false; + } + + return true; +} + +QByteArray PartHelper::translateData( qint64 id, const QByteArray &data, bool isExternal ) +{ + Q_UNUSED(id); + + if ( DbConfig::useExternalPayloadFile() && isExternal ) + { + //qDebug() << "translateData " << id; + QString fileName = QString::fromUtf8( data ); + QFile file( fileName ); + if (file.open( QIODevice::ReadOnly )) + { + QByteArray payload = file.readAll(); + file.close(); + return payload; + } else + { + qDebug() << "Payload file " << fileName << " could not be open for reading!"; + qDebug() << "Error: " << file.errorString(); + return QByteArray(); + } + } else + if ( isExternal ) //external payload is disabled, but the item is marked as external + { + return QByteArray(); + } else + return data; +} + +QByteArray PartHelper::translateData( const Part& part ) +{ + return translateData( part.id(), part.data(), part.external() ); +} + +/** Returns the record with id @p id. */ +Part PartHelper::retrieveById( qint64 id ) +{ + Part part = Part::retrieveById( id ); + loadData(part); + return part; +} + +QString PartHelper::fileNameForId( qint64 id ) +{ + const QString dataDir = XdgBaseDirs::saveDir( "data", QLatin1String( "akonadi/file_db_data" ) ) + QDir::separator(); + return dataDir + QString::number(id); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/parthelper.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/parthelper.h --- akonadi-1.1.1/server/src/storage/parthelper.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/parthelper.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2009 by Andras Mantia * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef PARTHELPER_H +#define PARTHELPER_H + +#include +#include "entities.h" + +class QString; +class QVariant; + +namespace Akonadi +{ +/** +A specialized Part class that stores data in a file instead of the database. + + @author Andras Mantia +*/ +class PartHelper +{ + public: + PartHelper(); + ~PartHelper(); + + static bool update( Part *part, const QByteArray &data, qint64 dataSize ); + static bool insert( Part *part, qint64* insertId = 0 ); + static bool remove( Part *part); + static bool remove( const QString &column, const QVariant &value ); + static bool loadData( Part::List &parts ); + static bool loadData( Part &part ); + static QByteArray translateData( qint64 id, const QByteArray &data, bool isExternal ); + static QByteArray translateData( const Part& part ); + /** Returns the record with id @p id. */ + static Part retrieveById( qint64 id ); + + static QString fileNameForId( qint64 id ); +}; +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/querybuilder.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/querybuilder.cpp --- akonadi-1.1.1/server/src/storage/querybuilder.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/querybuilder.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -88,7 +88,8 @@ void QueryBuilder::addTable(const QString & table) { - mTables << table; + if ( !mTables.contains( table ) ) + mTables << table; } void QueryBuilder::addValueCondition(const QString & column, Query::CompareOperator op, const QVariant & value) @@ -169,8 +170,13 @@ #ifndef QUERYBUILDER_UNITTEST mQuery.prepare( statement ); + //too heavy debug info but worths to have from time to time +// qDebug() << "Executing query" << statement; for ( int i = 0; i < mBindValues.count(); ++i ) + { mQuery.bindValue( QString::fromLatin1( ":%1" ).arg( i ), mBindValues[i] ); +// qDebug() << QString::fromLatin1( ":%1" ).arg( i ) << mBindValues[i]; + } if ( !mQuery.exec() ) { qDebug() << "Error during executing query" << statement << ": " << mQuery.lastError().text(); return false; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/queryhelper.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/queryhelper.cpp --- akonadi-1.1.1/server/src/storage/queryhelper.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/queryhelper.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,48 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "queryhelper.h" + +#include "storage/querybuilder.h" +#include "libs/imapset_p.h" + +using namespace Akonadi; + +void QueryHelper::setToQuery(const ImapSet& set, const QString &column, QueryBuilder& qb ) +{ + Query::Condition cond( Query::Or ); + foreach ( const ImapInterval i, set.intervals() ) { + if ( i.hasDefinedBegin() && i.hasDefinedEnd() ) { + if ( i.size() == 1 ) { + cond.addValueCondition( column, Query::Equals, i.begin() ); + } else { + Query::Condition subCond( Query::And ); + subCond.addValueCondition( column, Query::GreaterOrEqual, i.begin() ); + subCond.addValueCondition( column, Query::LessOrEqual, i.end() ); + cond.addCondition( subCond ); + } + } else if ( i.hasDefinedBegin() ) { + cond.addValueCondition( column, Query::GreaterOrEqual, i.begin() ); + } else if ( i.hasDefinedEnd() ) { + cond.addValueCondition( column, Query::LessOrEqual, i.end() ); + } + } + if ( !cond.isEmpty() ) + qb.addCondition( cond ); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/queryhelper.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/queryhelper.h --- akonadi-1.1.1/server/src/storage/queryhelper.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/src/storage/queryhelper.h 2009-07-28 17:52:07.000000000 +0100 @@ -0,0 +1,43 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADI_QUERYHELPER_H +#define AKONADI_QUERYHELPER_H + +#include "handler/scope.h" + +namespace Akonadi { + +class QueryBuilder; +class Scope; + +/** + Helper methods for common query tasks. +*/ +namespace QueryHelper +{ + /** + Add conditions to @p qb for the given uid set @p set applied to @p column. + */ + void setToQuery( const ImapSet &set, const QString &column, QueryBuilder &qb ); +} + +} + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/transaction.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/transaction.cpp --- akonadi-1.1.1/server/src/storage/transaction.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/transaction.cpp 2009-07-28 17:52:07.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -20,10 +20,11 @@ #include "transaction.h" #include "storage/datastore.h" -Akonadi::Transaction::Transaction(DataStore * db) : - mDb( db ), mCommitted( false ) +Akonadi::Transaction::Transaction(DataStore * db, bool beginTransaction ) : + mDb( db ), mCommitted( false ) { - mDb->beginTransaction(); + if ( beginTransaction ) + mDb->beginTransaction(); } Akonadi::Transaction::~Transaction() @@ -37,3 +38,8 @@ mCommitted = true; return mDb->commitTransaction(); } + +void Akonadi::Transaction::begin() +{ + mDb->beginTransaction(); +} diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/storage/transaction.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/storage/transaction.h --- akonadi-1.1.1/server/src/storage/transaction.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/storage/transaction.h 2009-07-28 17:52:07.000000000 +0100 @@ -1,5 +1,5 @@ /* - Copyright (c) 2006 Volker Krause + Copyright (c) 2006 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -41,8 +41,9 @@ instead of creating a new one. @param db The corresponding DataStore. You must not delete @p db during the lifetime of a Transaction object. + @param beginTransaction if false, the transaction won't be started, until begin is eplicitely called. The default is to begin the transaction right away. */ - Transaction( DataStore *db ); + Transaction( DataStore *db, bool beginTransaction = true); /** Rolls back the transaction if it hasn't been committed explicitly. @@ -57,6 +58,8 @@ */ bool commit(); + void begin(); + private: Q_DISABLE_COPY( Transaction ) DataStore* mDb; diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/xesammanager.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/xesammanager.cpp --- akonadi-1.1.1/server/src/xesammanager.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/xesammanager.cpp 2009-07-28 17:52:13.000000000 +0100 @@ -86,7 +86,7 @@ if ( list.isEmpty() ) continue; qint64 itemId = uriToItemId( list.first().toString() ); - Entity::addToRelation( colId, itemId ); + Entity::addToRelation( colId, itemId ); } } @@ -104,7 +104,7 @@ if ( list.isEmpty() ) continue; qint64 itemId = uriToItemId( list.first().toString() ); - Entity::removeFromRelation( colId, itemId ); + Entity::removeFromRelation( colId, itemId ); } } @@ -120,23 +120,23 @@ qWarning() << "No valid search resource found!"; return; } - Location::List locs = res.locations(); - foreach ( const Location &l, locs ) { - addSearch( l ); + Collection::List cols = res.collections(); + foreach ( const Collection &col, cols ) { + addSearch( col ); } } -bool XesamManager::addSearch(const Location & loc) +bool XesamManager::addSearch(const Collection & col) { if ( !mInterface->isValid() || !mValid ) return false; QMutexLocker lock( &mMutex ); - if ( loc.remoteId().isEmpty() ) + if ( col.remoteId().isEmpty() ) return false; - QString searchId = mInterface->NewSearch( mSession, loc.remoteId() ); - qDebug() << "XesamManager::addSeach" << loc << searchId; - mSearchMap[ searchId ] = loc.id(); - mInvSearchMap[ loc.id() ] = searchId; + QString searchId = mInterface->NewSearch( mSession, col.remoteId() ); + qDebug() << "XesamManager::addSeach" << col << searchId; + mSearchMap[ searchId ] = col.id(); + mInvSearchMap[ col.id() ] = searchId; mInterface->StartSearch( searchId ); #if 0 @@ -149,13 +149,13 @@ return true; } -bool XesamManager::removeSearch(qint64 loc) +bool XesamManager::removeSearch(qint64 col) { QMutexLocker lock( &mMutex ); - if ( !mInvSearchMap.contains( loc ) ) + if ( !mInvSearchMap.contains( col ) ) return false; - QString searchId = mInvSearchMap.value( loc ); - mInvSearchMap.remove( loc ); + QString searchId = mInvSearchMap.value( col ); + mInvSearchMap.remove( col ); mSearchMap.remove( searchId ); return true; } @@ -167,9 +167,9 @@ qWarning() << "No valid search resource found!"; return; } - Location::List locs = res.locations(); - foreach ( const Location &l, locs ) { - removeSearch( l.id() ); + Collection::List cols = res.collections(); + foreach ( const Collection &col, cols ) { + removeSearch( col.id() ); } } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/src/xesammanager.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/src/xesammanager.h --- akonadi-1.1.1/server/src/xesammanager.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/src/xesammanager.h 2009-07-28 17:52:13.000000000 +0100 @@ -37,8 +37,8 @@ XesamManager( QObject* parent = 0 ); ~XesamManager(); - bool addSearch( const Location &location ); - bool removeSearch( qint64 location ); + bool addSearch( const Collection &collection ); + bool removeSearch( qint64 collection ); private: void reloadSearches(); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/templates/cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/templates/cpp --- akonadi-1.1.1/server/templates/cpp 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/templates/cpp 1970-01-01 01:00:00.000000000 +0100 @@ -1,18 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Till Adam * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/templates/h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/templates/h --- akonadi-1.1.1/server/templates/h 2009-01-21 18:29:04.000000000 +0000 +++ akonadi-1.2.0/server/templates/h 1970-01-01 01:00:00.000000000 +0100 @@ -1,18 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Till Adam * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Library General Public License as * - * published by the Free Software Foundation; either version 2 of the * - * License, or (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/CMakeLists.txt /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/CMakeLists.txt --- akonadi-1.1.1/server/tests/unittest/CMakeLists.txt 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/tests/unittest/CMakeLists.txt 2009-07-28 17:52:03.000000000 +0100 @@ -1,11 +1,18 @@ ########### next target ############### set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) -include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) +include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../src) -set(handlertest_SRCS handlertest.cpp teststoragebackend.cpp ) +macro(add_server_test _source) + set(_test ${_source}) + get_filename_component(_name ${_source} NAME_WE) + automoc4_add_executable(${_name} ${_source}) + add_test(akonadi-${_name} ${_name}) + target_link_libraries(${_name} akonadiprivate ${QT_QTTEST_LIBRARY}) +endmacro(add_server_test) -automoc4_add_executable(handlertest ${handlertest_SRCS}) -add_test(akonadi-handlertest handlertest) - -target_link_libraries(handlertest akonadiprivate ${QT_QTTEST_LIBRARY}) +add_server_test(handlertest.cpp) +if(NOT WIN32) + add_server_test(imapstreamparsertest.cpp) +endif(NOT WIN32) +add_server_test(scopetest.cpp) diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/handlertest.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/handlertest.cpp --- akonadi-1.1.1/server/tests/unittest/handlertest.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/tests/unittest/handlertest.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -26,6 +26,7 @@ #include "mockobjects.h" #include "handlertest.h" +#include "imapstreamparser.h" using namespace Akonadi; @@ -41,13 +42,20 @@ /// ---- List ---- void HandlerTest::testSeparatorList() { - Handler* l = getHandlerFor("LIST"); - QVERIFY( dynamic_cast(l) != 0 ); + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + + const QByteArray line = "LIST \"\" \"\""; + buffer.write( line ); + buffer.seek(0); - const QByteArray line = "1 LIST \"\" \"\""; + Handler* l = getHandlerFor("LIST", &parser); + QVERIFY( dynamic_cast(l) != 0 ); + l->setStreamParser(&parser); QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& ))); - l->handleLine( line ); + l->parseStream(); QCOMPARE(spy.count(), 2); const QString expectedFirstResponse = "* LIST (\\Noselect) \"/\" \"\""; @@ -60,13 +68,20 @@ void HandlerTest::testRootPercentList() { QSKIP( "Does not work without MockBackend", SkipAll ); - Handler* l = getHandlerFor("LIST"); - QVERIFY( dynamic_cast(l) != 0 ); + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + + const QByteArray line = "LIST \"\" \"%\""; + buffer.write( line ); + buffer.seek(0); - const QByteArray line = "1 LIST \"\" \"%\""; + Handler* l = getHandlerFor("LIST", &parser); + QVERIFY( dynamic_cast(l) != 0 ); + l->setStreamParser(&parser); QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& ))); - l->handleLine( line ); + l->parseStream(); QCOMPARE(spy.count(), 2); const QByteArray expectedFirstResponse = "* LIST () \"/\" \"INBOX\""; @@ -79,13 +94,20 @@ void HandlerTest::testRootStarList() { QSKIP( "Does not work without MockBackend", SkipAll ); - Handler* l = getHandlerFor("LIST"); - QVERIFY( dynamic_cast(l) != 0 ); + const QByteArray line = "LIST \"\" \"*\""; + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); - const QByteArray line = "1 LIST \"\" \"*\""; + buffer.write( line ); + buffer.seek(0); + + Handler* l = getHandlerFor("LIST", &parser); + QVERIFY( dynamic_cast(l) != 0 ); + l->setStreamParser(&parser); QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& ))); - l->handleLine( line ); + l->parseStream(); QCOMPARE(spy.count(), 3); const QByteArray expectedFirstResponse = "* LIST () \"/\" \"INBOX\""; @@ -101,13 +123,20 @@ void HandlerTest::testInboxList() { QSKIP( "Does not work without MockBackend", SkipAll ); - Handler* l = getHandlerFor("LIST"); - QVERIFY( dynamic_cast(l) != 0 ); + const QByteArray line = "LIST \"\" \"INBOX\""; + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); - const QByteArray line = "1 LIST \"\" \"INBOX\""; + buffer.write( line ); + buffer.seek(0); + + Handler* l = getHandlerFor("LIST", &parser); + QVERIFY( dynamic_cast(l) != 0 ); + l->setStreamParser(&parser); QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& ))); - l->handleLine( line ); + l->parseStream(); QCOMPARE(spy.count(), 3); const QByteArray expectedFirstResponse = "* LIST () \"/\" \"foo\""; @@ -135,9 +164,9 @@ return r; } -Handler* HandlerTest::getHandlerFor(const QByteArray& command ) +Handler* HandlerTest::getHandlerFor( const QByteArray& command, ImapStreamParser *parser ) { - Handler *h = Handler::findHandlerForCommandAuthenticated( command ); + Handler *h = Handler::findHandlerForCommandAuthenticated( command, parser ); if( h != 0 ) { h->setTag("1"); h->setConnection( MockObjects::mockConnection() ); diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/handlertest.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/handlertest.h --- akonadi-1.1.1/server/tests/unittest/handlertest.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/tests/unittest/handlertest.h 2009-07-28 17:52:03.000000000 +0100 @@ -27,6 +27,10 @@ using namespace Akonadi; +namespace Akonadi { + class ImapStreamParser; +} + class HandlerTest: public QObject { Q_OBJECT @@ -43,7 +47,7 @@ private: // Helper Response nextResponse( QSignalSpy& spy ); - Handler* getHandlerFor( const QByteArray& command ); + Handler* getHandlerFor( const QByteArray& command, ImapStreamParser *parser ); }; #endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/imapstreamparsertest.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/imapstreamparsertest.cpp --- akonadi-1.1.1/server/tests/unittest/imapstreamparsertest.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/tests/unittest/imapstreamparsertest.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -0,0 +1,445 @@ +/* + Copyright (c) 2006 Volker Krause + Copyright (c) 2009 Andras Mantia + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "imapstreamparsertest.h" + +#include +#include +#include + +#define private public +#include "imapstreamparser.h" +#undef private +#define private private + +Q_DECLARE_METATYPE( QList ) +Q_DECLARE_METATYPE( QList ) + +using namespace Akonadi; + +QString akBacktrace() +{ + QString s; + + /* FIXME: is there an equivalent for darwin, *BSD, or windows? */ +#ifdef HAVE_EXECINFO_H + void* trace[256]; + int n = backtrace(trace, 256); + if (!n) + return s; + + char** strings = backtrace_symbols (trace, n); + + s = QLatin1String("[\n"); + + for ( int i = 0; i < n; ++i ) { + s += QString::number(i) + + QLatin1String(": ") + + QLatin1String(strings[i]) + QLatin1String("\n"); + } + s += QLatin1String("]\n"); + + if (strings) + free (strings); +#endif + + return s; +} + +QTEST_MAIN( ImapStreamParserTest ) + + +void ImapStreamParserTest::testParseQuotedString( ) +{ + QByteArray result; + + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + buffer.write( QByteArray( "\"quoted \\\"NIL\\\" string inside\"" ) ); + buffer.write( QByteArray( "quoted") ); + buffer.write( QByteArray( "\" string inside\"" ) ); + buffer.write( QByteArray( " string " ) ); + buffer.write("NIL \"NIL\" \"\""); + buffer.write( " string" ); + buffer.write( "\"\\\"some \\\\ quoted stuff\\\"\"" ); + buffer.write( "LOGOUT\nFOO" ); + buffer.seek(0); + + try { + // the whole thing + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "quoted \"NIL\" string inside" ) ); + + // unqoted string + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "quoted" ) ); + + // whitespaces in qouted string + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( " string inside" ) ); + + // whitespaces before unquoted string + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "string" ) ); + + + // NIL and emptyness tests + + // unqoted NIL + result = parser.parseQuotedString(); + QVERIFY( result.isNull() ); + + // quoted NIL + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "NIL" ) ); + + // quoted empty string + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "" ) ); + + // unquoted string at input end + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "string" ) ); + +// // out of bounds access +// result = parser.parseQuotedString(); +// QVERIFY( result.isEmpty() ); + + // de-quoting + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray( "\"some \\ quoted stuff\"" ) ); + + // linebreak as separator + result = parser.parseQuotedString(); + QCOMPARE( result, QByteArray("LOGOUT") ); + } catch ( const Akonadi::Exception &e ) { + qDebug() << "Caught exception: " << e.type() << " : " << + e.what(); + } catch (...) + { + qDebug() << "Unknown exception caught: " << akBacktrace(); + } +} + + +void ImapStreamParserTest::testParseString( ) +{ + + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + + buffer.write( QByteArray( "\"quoted\" unquoted {7}\nliteral {0}\n " ) ); + buffer.seek(0); + + QByteArray result; + + try { + // quoted strings + result = parser.readString(); + QCOMPARE( result, QByteArray( "quoted" ) ); + + // unquoted string + result = parser.readString( ); + QCOMPARE( result, QByteArray( "unquoted" ) ); + + // literal string + result = parser.readString( ); + QCOMPARE( result, QByteArray( "literal" ) ); + + // empty literal string + result = parser.readString(); + QCOMPARE( result, QByteArray( "" ) ); + + // out of bounds access + + //this results in an ImapStreamParser exception + result = parser.readString(); + QCOMPARE( result, QByteArray() ); + } catch ( const Akonadi::Exception &e ) { + qDebug() << "Caught exception: " << e.type() << " : " << + e.what(); + } catch (...) + { + qDebug() << "Unknown exception caught: " << akBacktrace(); + } +} + +void ImapStreamParserTest::testParseParenthesizedList( ) +{ + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + + buffer.write( QByteArray( "() ( )" ) ); + buffer.write( QByteArray( "(entry1 \"entry2()\" (sub list) \")))\" {6}\nentry3) " ) ); + buffer.write( QByteArray( "some_list-less_text" ) ); + buffer.write( QByteArray( "(foo {6}\n\n\nbar\n bla)" )); + buffer.seek(0); + + QListresult; + + try { + // empty lists + result = parser.readParenthesizedList(); + QVERIFY( result.isEmpty() ); + + result = parser.readParenthesizedList(); + QVERIFY( result.isEmpty() ); + + // complex list with all kind of entries + qDebug() << "complex test"; + result = parser.readParenthesizedList(); + QList reference; + reference << "entry1"; + reference << "entry2()"; + reference << "(sub list)"; + reference << ")))"; + reference << "entry3"; + + + QCOMPARE( result, reference ); + + // no list at all + result = parser.readParenthesizedList( ); + QVERIFY( result.isEmpty() ); + + parser.readString(); //just to advance in the data + +// // out of bounds access +// result = parser.readParenthesizedList( input, result, input.length() ); +// QVERIFY( result.isEmpty() ); +// QCOMPARE( consumed, input.length() ); + + // newline literal (based on itemappendtest bug) + result = parser.readParenthesizedList( ); + reference.clear(); + reference << "foo"; + reference << "\n\nbar\n"; + reference << "bla"; + + QCOMPARE( result, reference ); + } catch ( const Akonadi::Exception &e ) { + qDebug() << "Caught exception: " << e.type() << " : " << + e.what(); + } catch (...) + { + qDebug() << "Unknown exception caught: " << akBacktrace(); + } + +} + + +void ImapStreamParserTest::testParseNumber() +{ + QBuffer buffer; + buffer.open( QBuffer::ReadWrite | QIODevice::Truncate ); + ImapStreamParser parser(&buffer); + + buffer.write( QByteArray( " " ) ); + buffer.seek(0); + + qint64 result; + bool ok; + + try { + // empty string + result = parser.readNumber( &ok ); + QCOMPARE( ok, false ); + + buffer.close(); + buffer.open( QBuffer::ReadWrite | QIODevice::Truncate ); + buffer.write( QByteArray( " 1 23 4" ) ); + buffer.seek(0); + // leading spaces at the beginning + result = parser.readNumber( &ok ); + QCOMPARE( ok, true ); + QCOMPARE( result, 1ll ); + + // multiple digits + result = parser.readNumber( &ok ); + QCOMPARE( ok, true ); + QCOMPARE( result, 23ll ); + + // number at input end + result = parser.readNumber(&ok ); + QCOMPARE( ok, true ); + QCOMPARE( result, 4ll ); + + // out of bounds access - throws an exception + result = parser.readNumber( &ok ); + QCOMPARE( ok, false ); +} catch ( const Akonadi::Exception &e ) { + qDebug() << "Caught exception: " << e.type() << " : " << + e.what(); +} catch (...) +{ + qDebug() << "Unknown exception caught: " << akBacktrace(); +} +} + + +void ImapStreamParserTest::testParseSequenceSet_data() +{ + QTest::addColumn( "data" ); + QTest::addColumn( "result" ); + + QByteArray data( " 1 0:* 3:4,8:* *:5,1" ); + + QTest::newRow( "empty" ) << QByteArray() << ImapInterval::List() ; + + ImapInterval::List result; + result << ImapInterval( 1, 1 ); + data = " 1 "; + QTest::newRow( "single value" ) << data << result; + + result.clear(); + result << ImapInterval(); + data = " 0:* "; + QTest::newRow( "full interval" ) << data << result; + + data = " 3:4,8:*"; + result.clear(); + result << ImapInterval( 3, 4 ) << ImapInterval( 8 ); + QTest::newRow( "complex 1" ) << data << result ; + + data = " *:5,1"; + result.clear(); + result << ImapInterval( 0, 5 ) << ImapInterval( 1, 1 ); + QTest::newRow( "complex 2" ) << data << result ; +} + +void ImapStreamParserTest::testParseSequenceSet() +{ + QFETCH( QByteArray, data ); + QFETCH( ImapInterval::List, result ); + + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + buffer.write( data ); + buffer.seek(0); + + ImapSet res; + try { + res = parser.readSequenceSet(); + QCOMPARE( res.intervals(), result ); +} catch ( const Akonadi::Exception &e ) { + qDebug() << "Caught exception: " << e.type() << " : " << + e.what(); +} catch (...) +{ + qDebug() << "Unknown exception caught: " << akBacktrace(); +} +} + + +void ImapStreamParserTest::testParseDateTime_data() +{ + QTest::addColumn( "data" ); + QTest::addColumn( "result" ); + + QTest::newRow( "empty" ) << QByteArray() << QDateTime() ; + + QByteArray data( " \"28-May-2006 01:03:35 +0200\"" ); + QByteArray data2( "22-Jul-2008 16:31:48 +0000" ); + QByteArray data3( "ul-2008 16:31:48 +0000" ); + + QDateTime dt( QDate( 2006, 5, 27 ), QTime( 23, 3, 35 ), Qt::UTC ); + QDateTime dt2( QDate( 2008, 7, 22 ), QTime( 16, 31, 48 ), Qt::UTC ); + + QTest::newRow( "quoted" ) << data << dt ; + QTest::newRow( "unquoted" ) << data2 << dt2 ; + QTest::newRow( "invalid" ) << data3 << QDateTime(); +} + +void ImapStreamParserTest::testParseDateTime() +{ + QFETCH( QByteArray, data ); + QFETCH( QDateTime, result ); + + QBuffer buffer; + buffer.open( QBuffer::ReadWrite); + ImapStreamParser parser(&buffer); + buffer.write( data ); + buffer.seek(0); + + QDateTime actualResult; + try { + actualResult = parser.readDateTime(); + QCOMPARE( actualResult, result ); + } catch ( const Akonadi::Exception &e ) { + qDebug() << "Caught exception: " << e.type() << " : " << + e.what(); + } catch (...) + { + qDebug() << "Unknown exception caught: " << akBacktrace(); + } +} + +void ImapStreamParserTest::testReadUntilCommandEnd() +{ + QByteArray input( "2 UID STORE 2 NOREV SIZE 0 (+FLAGS.SILENT (\\Deleted))\n3 EXPUNGE\n" ); + QBuffer buffer( &input, this ); + buffer.open( QIODevice::ReadOnly ); + ImapStreamParser parser( &buffer ); + + try { + QCOMPARE( parser.readString(), QByteArray( "2" ) ); + QCOMPARE( parser.readString(), QByteArray( "UID" ) ); + QCOMPARE( parser.readString(), QByteArray( "STORE" ) ); + QCOMPARE( parser.readString(), QByteArray( "2" ) ); + QCOMPARE( parser.readString(), QByteArray( "NOREV" ) ); + QCOMPARE( parser.readString(), QByteArray( "SIZE" ) ); + QCOMPARE( parser.readString(), QByteArray( "0" ) ); + parser.stripLeadingSpaces(); + QCOMPARE( static_cast( parser.readChar() ), '(' ); + QVERIFY( !parser.atCommandEnd() ); + parser.readUntilCommandEnd(); + QCOMPARE( parser.readString(), QByteArray( "3" ) ); + } catch ( const Akonadi::Exception &e ) { + qDebug() << e.type() << e.what(); + QFAIL( "Exception caught" ); + } +} + +// real bug, infinite loop with { as first char in a quoted string +void ImapStreamParserTest::testReadUntilCommandEnd2() +{ + QByteArray input( "4 MODIFY 595 MIMETYPE (message/rfc822 inode/directory) NAME \"child2\" REMOTEID \"{b42}\"\nNEXTCOMMAND\n" ); + QBuffer buffer( &input, this ); + buffer.open( QIODevice::ReadOnly ); + ImapStreamParser parser( &buffer ); + + try { + QCOMPARE( parser.readString(), QByteArray( "4" ) ); + QCOMPARE( parser.readString(), QByteArray( "MODIFY" ) ); + QCOMPARE( parser.readString(), QByteArray( "595" ) ); + QCOMPARE( parser.readUntilCommandEnd(), QByteArray( " MIMETYPE (message/rfc822 inode/directory) NAME \"child2\" REMOTEID \"{b42}\"\n" ) ); + QCOMPARE( parser.readString(), QByteArray( "NEXTCOMMAND" ) ); + } catch ( const Akonadi::Exception &e ) { + qDebug() << e.type() << e.what(); + QFAIL( "Exception caught" ); + } +} + + +#include "imapstreamparsertest.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/imapstreamparsertest.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/imapstreamparsertest.h --- akonadi-1.1.1/server/tests/unittest/imapstreamparsertest.h 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/tests/unittest/imapstreamparsertest.h 2009-07-28 17:52:03.000000000 +0100 @@ -0,0 +1,45 @@ +/* + Copyright (c) 2006 Volker Krause + Copyright (c) 2009 Andras Mantia + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef IMAPPARSER_TEST_H +#define IMAPPARSER_TEST_H + +#include +#include + +class ImapStreamParserTest : public QObject +{ + Q_OBJECT + private Q_SLOTS: + void testParseQuotedString(); + void testParseString(); + void testParseParenthesizedList(); + void testParseNumber(); + void testParseSequenceSet_data(); + void testParseSequenceSet(); + void testParseDateTime_data(); + void testParseDateTime(); + void testReadUntilCommandEnd(); + void testReadUntilCommandEnd2(); + +}; + + +#endif diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/scopetest.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/scopetest.cpp --- akonadi-1.1.1/server/tests/unittest/scopetest.cpp 1970-01-01 01:00:00.000000000 +0100 +++ akonadi-1.2.0/server/tests/unittest/scopetest.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -0,0 +1,107 @@ +/* + Copyright (c) 2009 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include +#include + +#include "handler/scope.cpp" +#include "imapstreamparser.h" + +using namespace Akonadi; + +class ScopeTest : public QObject +{ + Q_OBJECT + private slots: + void testUidSet() + { + Scope scope( Scope::Uid ); + + QByteArray input( "1:3,6 FOO\n" ); + QBuffer buffer( &input, this ); + buffer.open( QIODevice::ReadOnly ); + ImapStreamParser parser( &buffer ); + + scope.parseScope( &parser ); + QCOMPARE( parser.readRemainingData(), QByteArray( " FOO\n" ) ); + + QCOMPARE( scope.scope(), Scope::Uid ); + QCOMPARE( scope.uidSet().toImapSequenceSet(), QByteArray( "1:3,6" ) ); + QVERIFY( scope.ridSet().isEmpty() ); + } + + void testRid_data() + { + QTest::addColumn( "input" ); + QTest::addColumn( "rid" ); + QTest::addColumn( "remainder" ); + + QTest::newRow( "no list, remainder" ) + << QByteArray( "\"(my remote id)\" FOO\n" ) + << QString::fromLatin1( "(my remote id)" ) + << QByteArray( " FOO\n" ); + QTest::newRow( "list, no remainder" ) << QByteArray( "(\"A\")" ) << QString::fromLatin1( "A" ) << QByteArray(); + QTest::newRow( "list, no reaminder, leading space" ) + << QByteArray( " (\"A\")\n" ) << QString::fromLatin1( "A" ) << QByteArray( "\n" ); + } + + void testRid() + { + QFETCH( QByteArray, input ); + QFETCH( QString, rid ); + QFETCH( QByteArray, remainder ); + + Scope scope( Scope::Rid ); + + QBuffer buffer( &input, this ); + buffer.open( QIODevice::ReadOnly ); + ImapStreamParser parser( &buffer ); + + scope.parseScope( &parser ); + QCOMPARE( parser.readRemainingData(), remainder ); + + QCOMPARE( scope.scope(), Scope::Rid ); + QVERIFY( scope.uidSet().isEmpty() ); + QCOMPARE( scope.ridSet().size(), 1 ); + QCOMPARE( scope.ridSet().first(), rid ); + } + + void testRidSet() + { + Scope scope( Scope::Rid ); + + QByteArray input( "(\"my first remote id\" \"my second remote id\") FOO\n" ); + QBuffer buffer( &input, this ); + buffer.open( QIODevice::ReadOnly ); + ImapStreamParser parser( &buffer ); + + scope.parseScope( &parser ); + QCOMPARE( parser.readRemainingData(), QByteArray( " FOO\n" ) ); + + QCOMPARE( scope.scope(), Scope::Rid ); + QVERIFY( scope.uidSet().isEmpty() ); + QCOMPARE( scope.ridSet().size(), 2 ); + QCOMPARE( scope.ridSet().first(), QLatin1String( "my first remote id" ) ); + QCOMPARE( scope.ridSet().last(), QLatin1String( "my second remote id" ) ); + } +}; + +QTEST_MAIN( ScopeTest ) + +#include "scopetest.moc" diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/teststoragebackend.cpp /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/teststoragebackend.cpp --- akonadi-1.1.1/server/tests/unittest/teststoragebackend.cpp 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/tests/unittest/teststoragebackend.cpp 2009-07-28 17:52:03.000000000 +0100 @@ -23,23 +23,23 @@ bool MockBackend::listCollections( const QString& prefix, const QString& mailboxPattern, - QList &result ) const + QList &result ) const { Q_UNUSED(prefix); result.clear(); //qDebug() << "Prefix: " << prefix << " pattern: " << mailboxPattern; if ( mailboxPattern == QLatin1String("%") ) { - Location l; - l.setName( "INBOX" ); - result << l; + Collection col; + col.setName( "INBOX" ); + result << col; } else if ( mailboxPattern == QLatin1String("*") ) { - Location l1; l1.setName( "INBOX" ); - Location l2; l2.setName( "INBOX/foo" ); - result << l1 << l2; + Collection col1; col1.setName( "INBOX" ); + Collection col2; col2.setName( "INBOX/foo" ); + result << col1 << col2; } else if ( mailboxPattern.startsWith( "INBOX" ) ) { - Location l1; l1.setName( "foo" ); - Location l2; l2.setName( "bar" ); - result << l1 << l2; + Collection col1; col1.setName( "foo" ); + Collection col2; col2.setName( "bar" ); + result << col1 << col2; } return true; } diff -Nru /tmp/Zl3R6Fn7OA/akonadi-1.1.1/server/tests/unittest/teststoragebackend.h /tmp/x5zlUfQlMV/akonadi-1.2.0/server/tests/unittest/teststoragebackend.h --- akonadi-1.1.1/server/tests/unittest/teststoragebackend.h 2009-01-21 18:29:03.000000000 +0000 +++ akonadi-1.2.0/server/tests/unittest/teststoragebackend.h 2009-07-28 17:52:03.000000000 +0100 @@ -32,7 +32,7 @@ bool listCollections( const QString& prefix, const QString& mailboxPattern, - QList &result ) const; + QList &result ) const; }; }