From 40eaf6fc24db0a9818ea21dab4176899fcc86637 Mon Sep 17 00:00:00 2001 From: Maximiliano Curia Date: Tue, 28 Jun 2016 18:47:52 +0100 Subject: [PATCH] Import kdepim-runtime_16.04.2.orig.tar.xz [dgit import orig kdepim-runtime_16.04.2.orig.tar.xz] --- .arcconfig | 4 + .emacs-dirvars | 14 + .gitignore | 24 + .krazy | 6 + .reviewboardrc | 5 + CMakeLists.txt | 154 + COPYING | 346 + COPYING.LIB | 510 + CTestConfig.cmake | 13 + CTestCustom.cmake | 31 + MIGRATE-CONFIG-APPS | 3 + Mainpage.dox | 105 + README | 3 + agents/.krazy | 1 + agents/CMakeLists.txt | 10 + agents/Info.plist.template | 36 + agents/Mainpage.dox | 2 + agents/cmake/FindXsltproc.cmake | 32 + agents/invitations/CMakeLists.txt | 28 + agents/invitations/Messages.sh | 3 + agents/invitations/incidenceattribute.cpp | 93 + agents/invitations/incidenceattribute.h | 64 + agents/invitations/invitationsagent.cpp | 569 + agents/invitations/invitationsagent.desktop | 97 + agents/invitations/invitationsagent.h | 101 + agents/maildispatcher/CMakeLists.txt | 58 + agents/maildispatcher/Messages.sh | 4 + agents/maildispatcher/TODO | 16 + .../akonadi_maildispatcher_agent.notifyrc | 197 + .../maildispatcher/autotests/CMakeLists.txt | 48 + agents/maildispatcher/autotests/TODO | 6 + agents/maildispatcher/autotests/aborttest.cpp | 239 + agents/maildispatcher/autotests/aborttest.h | 54 + agents/maildispatcher/autotests/dupetest.cpp | 220 + agents/maildispatcher/autotests/dupetest.h | 48 + .../autotests/unittestenv/config.xml | 7 + .../kdehome/share/config/akonadi-firstrunrc | 3 + .../share/config/akonadi_knut_resource_0rc | 4 + .../akonadi_mailtransport_dummy_resource_0rc | 2 + .../unittestenv/kdehome/share/config/kdebugrc | 110 + .../unittestenv/kdehome/share/config/kdedrc | 3 + .../kdehome/share/config/kwalletrc | 2 + .../kdehome/share/config/mailtransports | 23 + .../unittestenv/kdehome/share/config/qttestrc | 2 + .../unittestenv/kdehome/testdata.xml | 4 + .../xdgconfig/akonadi/akonadiserverrc | 4 + .../autotests/unittestenv/xdglocal/.keep | 1 + agents/maildispatcher/maildispatcheragent.cpp | 361 + .../maildispatcheragent.desktop | 93 + agents/maildispatcher/maildispatcheragent.h | 76 + .../maildispatcher/maildispatcheragent.kcfg | 18 + ...reedesktop.Akonadi.MailDispatcherAgent.xml | 7 + agents/maildispatcher/outboxqueue.cpp | 455 + agents/maildispatcher/outboxqueue.h | 94 + agents/maildispatcher/sendjob.cpp | 486 + agents/maildispatcher/sendjob.h | 91 + agents/maildispatcher/sentactionhandler.cpp | 72 + agents/maildispatcher/sentactionhandler.h | 44 + agents/maildispatcher/settings.kcfgc | 8 + agents/maildispatcher/settings.ui | 77 + agents/maildispatcher/storeresultjob.cpp | 141 + agents/maildispatcher/storeresultjob.h | 75 + agents/migration/CMakeLists.txt | 43 + agents/migration/Messages.sh | 2 + agents/migration/autotests/CMakeLists.txt | 12 + agents/migration/autotests/dummymigrator.cpp | 68 + agents/migration/autotests/dummymigrator.h | 49 + agents/migration/autotests/schedulertest.cpp | 286 + agents/migration/migrationagent.cpp | 75 + agents/migration/migrationagent.desktop | 45 + agents/migration/migrationagent.h | 43 + agents/migration/migrationexecutor.cpp | 108 + agents/migration/migrationexecutor.h | 61 + agents/migration/migrationscheduler.cpp | 298 + agents/migration/migrationscheduler.h | 132 + agents/migration/migrationstatuswidget.cpp | 127 + agents/migration/migrationstatuswidget.h | 46 + agents/newmailnotifier/CMakeLists.txt | 63 + agents/newmailnotifier/Messages.sh | 4 + agents/newmailnotifier/TODO | 4 + .../akonadi_newmailnotifier_agent.notifyrc | 137 + .../newmailnotifier/newmailnotifieragent.cpp | 575 + .../newmailnotifieragent.desktop | 97 + agents/newmailnotifier/newmailnotifieragent.h | 117 + .../newmailnotifieragentsettings.kcfg | 40 + .../newmailnotifieragentsettings.kcfgc | 6 + .../newmailnotifierselectcollectionwidget.cpp | 218 + .../newmailnotifierselectcollectionwidget.h | 68 + .../newmailnotifiersettingsdialog.cpp | 231 + .../newmailnotifiersettingsdialog.h | 56 + .../newmailnotifiershowmessagejob.cpp | 59 + .../newmailnotifiershowmessagejob.h | 38 + ...rg.freedesktop.Akonadi.NewMailNotifier.xml | 71 + agents/newmailnotifier/specialnotifierjob.cpp | 181 + agents/newmailnotifier/specialnotifierjob.h | 53 + akonadi-prefix.h.cmake | 6 + akonadi-version.h.cmake | 25 + cmake/modules/FindXsltproc.cmake | 32 + defaultsetup/CMakeLists.txt | 10 + defaultsetup/birthdaycalendar.desktop | 3 + defaultsetup/defaultaddressbook.desktop | 56 + defaultsetup/defaultcalendar.desktop | 52 + defaultsetup/defaultnotebook.desktop | 79 + doc/git-migration.txt | 7 + doc/libakonadi.xmi | 9639 +++++++++++++++++ doc/pics/akonadi_agent_handling.eps | 1142 ++ doc/pics/akonadi_agent_handling.png | Bin 0 -> 9008 bytes doc/pics/akonadi_agent_handling_small.png | Bin 0 -> 3611 bytes doc/pics/akonadi_client_search.eps | 1282 +++ doc/pics/akonadi_client_search.png | Bin 0 -> 10244 bytes doc/pics/akonadi_client_search_small.png | Bin 0 -> 3726 bytes doc/pics/akonadi_communication.xmi | 185 + doc/pics/akonadi_concept_schema.sla | 721 ++ doc/pics/akonadi_overview_uml.png | Bin 0 -> 14070 bytes doc/pics/akonadi_overview_uml.ps | 2551 +++++ doc/pics/akonadi_overview_uml_small.png | Bin 0 -> 57683 bytes doc/pics/concept.eps | 2351 ++++ doc/pics/concept.png | Bin 0 -> 18310 bytes doc/pics/concept.sla | 783 ++ doc/pics/convert.sh | 5 + doc/todo.dox | 81 + kdepim-mime.xml | 44 + kdepim-runtime-version.h.cmake | 36 + kdepim-runtime.categories | 18 + kioslave/CMakeLists.txt | 25 + kioslave/Messages.sh | 2 + kioslave/akonadi.protocol | 10 + kioslave/akonadislave.cpp | 243 + kioslave/akonadislave.h | 62 + migration/CMakeLists.txt | 29 + migration/cmake/FindXsltproc.cmake | 32 + migration/entitytreecreatejob.cpp | 117 + migration/entitytreecreatejob.h | 49 + migration/gid/CMakeLists.txt | 24 + migration/gid/Messages.sh | 2 + migration/gid/gidmigrationjob.cpp | 142 + migration/gid/gidmigrationjob.h | 88 + migration/gid/gidmigrator.cpp | 70 + migration/gid/gidmigrator.h | 48 + migration/gid/main.cpp | 96 + migration/infodialog.cpp | 193 + migration/infodialog.h | 77 + migration/kmigratorbase.cpp | 142 + migration/kmigratorbase.h | 93 + migration/migratorbase.cpp | 291 + migration/migratorbase.h | 233 + plugins/CMakeLists.txt | 56 + plugins/Messages.sh | 2 + plugins/akonadi_serializer_addressee.cpp | 285 + plugins/akonadi_serializer_addressee.desktop | 101 + plugins/akonadi_serializer_addressee.h | 59 + plugins/akonadi_serializer_contactgroup.cpp | 137 + .../akonadi_serializer_contactgroup.desktop | 99 + plugins/akonadi_serializer_contactgroup.h | 58 + plugins/akonadi_serializer_kalarm.cpp | 315 + plugins/akonadi_serializer_kalarm.desktop | 94 + plugins/akonadi_serializer_kalarm.h | 65 + plugins/akonadi_serializer_kcalcore.cpp | 370 + plugins/akonadi_serializer_kcalcore.desktop | 100 + plugins/akonadi_serializer_kcalcore.h | 63 + plugins/akonadi_serializer_mail.cpp | 294 + plugins/akonadi_serializer_mail.desktop | 101 + plugins/akonadi_serializer_mail.h | 68 + plugins/autotests/CMakeLists.txt | 32 + plugins/autotests/addresseeserializertest.cpp | 47 + plugins/autotests/kcalcoreserializertest.cpp | 133 + .../autotests/mailserializerplugintest.cpp | 94 + plugins/autotests/mailserializerplugintest.h | 33 + plugins/autotests/mailserializertest.cpp | 372 + plugins/autotests/mailserializertest.h | 40 + plugins/kaeventformatter.cpp | 341 + plugins/kaeventformatter.h | 113 + resources/.krazy | 1 + resources/CMakeLists.txt | 87 + resources/Info.plist.template | 36 + resources/Mainpage.dox | 2 + resources/akonotes/CMakeLists.txt | 52 + resources/akonotes/Messages.sh | 2 + resources/akonotes/akonotesresource.cpp | 49 + resources/akonotes/akonotesresource.desktop | 97 + resources/akonotes/akonotesresource.h | 39 + resources/birthdays/CMakeLists.txt | 37 + resources/birthdays/Messages.sh | 3 + resources/birthdays/birthdaysresource.cpp | 365 + resources/birthdays/birthdaysresource.desktop | 100 + resources/birthdays/birthdaysresource.h | 70 + resources/birthdays/birthdaysresource.kcfg | 25 + resources/birthdays/configdialog.cpp | 92 + resources/birthdays/configdialog.h | 48 + resources/birthdays/configdialog.ui | 172 + resources/birthdays/settings.kcfgc | 7 + resources/cmake/FindXsltproc.cmake | 32 + resources/contacts/CMakeLists.txt | 38 + resources/contacts/Messages.sh | 3 + resources/contacts/contactsresource.cpp | 550 + resources/contacts/contactsresource.desktop | 101 + resources/contacts/contactsresource.h | 83 + resources/contacts/contactsresource.kcfg | 21 + resources/contacts/settings.kcfgc | 8 + resources/contacts/settingsdialog.cpp | 117 + resources/contacts/settingsdialog.h | 58 + resources/contacts/settingsdialog.ui | 100 + resources/contacts/wizard/CMakeLists.txt | 5 + resources/contacts/wizard/Messages.sh | 6 + .../contacts/wizard/contactswizard.desktop | 102 + .../contacts/wizard/contactswizard.es.cmake | 44 + resources/contacts/wizard/contactswizard.ui | 52 + resources/dav/CMakeLists.txt | 1 + resources/dav/COPYING | 340 + resources/dav/README | 50 + resources/dav/TODO | 2 + resources/dav/common/davcollection.cpp | 99 + resources/dav/common/davcollection.h | 154 + .../dav/common/davcollectiondeletejob.cpp | 63 + resources/dav/common/davcollectiondeletejob.h | 57 + .../dav/common/davcollectionmodifyjob.cpp | 163 + resources/dav/common/davcollectionmodifyjob.h | 80 + .../dav/common/davcollectionsfetchjob.cpp | 347 + resources/dav/common/davcollectionsfetchjob.h | 83 + .../common/davcollectionsmultifetchjob.cpp | 62 + .../dav/common/davcollectionsmultifetchjob.h | 77 + resources/dav/common/davitem.cpp | 93 + resources/dav/common/davitem.h | 109 + resources/dav/common/davitemcreatejob.cpp | 134 + resources/dav/common/davitemcreatejob.h | 64 + resources/dav/common/davitemdeletejob.cpp | 99 + resources/dav/common/davitemdeletejob.h | 69 + resources/dav/common/davitemfetchjob.cpp | 94 + resources/dav/common/davitemfetchjob.h | 61 + resources/dav/common/davitemmodifyjob.cpp | 144 + resources/dav/common/davitemmodifyjob.h | 75 + resources/dav/common/davitemsfetchjob.cpp | 154 + resources/dav/common/davitemsfetchjob.h | 72 + resources/dav/common/davitemslistjob.cpp | 233 + resources/dav/common/davitemslistjob.h | 102 + resources/dav/common/davjobbase.cpp | 81 + resources/dav/common/davjobbase.h | 83 + resources/dav/common/davmanager.cpp | 124 + resources/dav/common/davmanager.h | 105 + resources/dav/common/davmultigetprotocol.cpp | 23 + resources/dav/common/davmultigetprotocol.h | 52 + .../common/davprincipalhomesetsfetchjob.cpp | 223 + .../dav/common/davprincipalhomesetsfetchjob.h | 77 + .../dav/common/davprincipalsearchjob.cpp | 381 + resources/dav/common/davprincipalsearchjob.h | 112 + resources/dav/common/davprotocolbase.cpp | 51 + resources/dav/common/davprotocolbase.h | 130 + resources/dav/common/davutils.cpp | 379 + resources/dav/common/davutils.h | 185 + resources/dav/common/etagcache.cpp | 96 + resources/dav/common/etagcache.h | 105 + resources/dav/protocols/caldavprotocol.cpp | 430 + resources/dav/protocols/caldavprotocol.h | 43 + resources/dav/protocols/carddavprotocol.cpp | 192 + resources/dav/protocols/carddavprotocol.h | 43 + resources/dav/protocols/groupdavprotocol.cpp | 150 + resources/dav/protocols/groupdavprotocol.h | 38 + resources/dav/resource/CMakeLists.txt | 104 + resources/dav/resource/Messages.sh | 3 + resources/dav/resource/akonadi-resources.png | Bin 0 -> 69059 bytes resources/dav/resource/configdialog.cpp | 330 + resources/dav/resource/configdialog.h | 70 + resources/dav/resource/configdialog.ui | 363 + resources/dav/resource/ctagattribute.cpp | 56 + resources/dav/resource/ctagattribute.h | 44 + resources/dav/resource/davfreebusyhandler.cpp | 228 + resources/dav/resource/davfreebusyhandler.h | 99 + .../dav/resource/davgroupwareprovider.desktop | 72 + .../dav/resource/davgroupwareresource.cpp | 1306 +++ .../dav/resource/davgroupwareresource.desktop | 90 + resources/dav/resource/davgroupwareresource.h | 131 + .../dav/resource/davgroupwareresource.kcfg | 57 + .../dav/resource/davprotocolattribute.cpp | 55 + resources/dav/resource/davprotocolattribute.h | 43 + resources/dav/resource/searchdialog.cpp | 210 + resources/dav/resource/searchdialog.h | 59 + resources/dav/resource/searchdialog.ui | 221 + resources/dav/resource/settings.cpp | 706 ++ resources/dav/resource/settings.h | 152 + resources/dav/resource/settingsbase.kcfgc | 8 + resources/dav/resource/setupwizard.cpp | 561 + resources/dav/resource/setupwizard.h | 154 + .../dav/resource/urlconfigurationdialog.cpp | 295 + .../dav/resource/urlconfigurationdialog.h | 81 + .../dav/resource/urlconfigurationdialog.ui | 232 + resources/dav/services/citadel.desktop | 50 + resources/dav/services/davical.desktop | 52 + resources/dav/services/egroupware.desktop | 52 + resources/dav/services/opengroupware.desktop | 51 + resources/dav/services/owncloud-pre5.desktop | 48 + resources/dav/services/owncloud.desktop | 52 + resources/dav/services/scalix.desktop | 52 + resources/dav/services/sogo.desktop | 53 + resources/dav/services/yahoo.desktop | 57 + resources/dav/services/zarafa.desktop | 52 + resources/dav/services/zimbra.desktop | 53 + .../folderarchivesettings/CMakeLists.txt | 32 + resources/folderarchivesettings/Messages.sh | 2 + .../autotests/CMakeLists.txt | 13 + .../folderarchiveaccountinfotest.cpp | 73 + .../autotests/folderarchiveaccountinfotest.h | 37 + .../folderarchiveaccountinfo.cpp | 128 + .../folderarchiveaccountinfo.h | 67 + .../folderarchivesettingpage.cpp | 159 + .../folderarchivesettingpage.h | 68 + .../folderarchiveutil.cpp | 30 + .../folderarchivesettings/folderarchiveutil.h | 31 + resources/gmail/CMakeLists.txt | 51 + resources/gmail/Messages.sh | 3 + .../gmail/gmailchangeitemslabelstask.cpp | 73 + resources/gmail/gmailchangeitemslabelstask.h | 40 + resources/gmail/gmailconfigdialog.cpp | 274 + resources/gmail/gmailconfigdialog.h | 79 + resources/gmail/gmailconfigdialog.ui | 232 + resources/gmail/gmaillabelattribute.cpp | 105 + resources/gmail/gmaillabelattribute.h | 54 + resources/gmail/gmaillinkitemstask.cpp | 207 + resources/gmail/gmaillinkitemstask.h | 63 + resources/gmail/gmailmessagehelper.cpp | 106 + resources/gmail/gmailmessagehelper.h | 47 + resources/gmail/gmailpasswordrequester.cpp | 95 + resources/gmail/gmailpasswordrequester.h | 49 + resources/gmail/gmailresource.cpp | 239 + resources/gmail/gmailresource.desktop | 80 + resources/gmail/gmailresource.h | 66 + resources/gmail/gmailresource.kcfg | 116 + resources/gmail/gmailresourcestate.cpp | 48 + resources/gmail/gmailresourcestate.h | 44 + .../gmail/gmailretrievecollectionstask.cpp | 244 + .../gmail/gmailretrievecollectionstask.h | 36 + resources/gmail/gmailretrieveitemstask.cpp | 56 + resources/gmail/gmailretrieveitemstask.h | 51 + resources/gmail/gmailsettings.cpp | 300 + resources/gmail/gmailsettings.h | 88 + resources/gmail/saslplugin/CMakeLists.txt | 23 + resources/gmail/saslplugin/config.h | 579 + resources/gmail/saslplugin/plugin_common.c | 969 ++ resources/gmail/saslplugin/plugin_common.h | 221 + resources/gmail/saslplugin/xoauth2plugin.c | 241 + .../gmail/saslplugin/xoauth2plugin_init.c | 57 + resources/gmail/settingsbase.kcfgc | 6 + resources/google/CMakeLists.txt | 15 + resources/google/calendar/CMakeLists.txt | 70 + resources/google/calendar/Messages.sh | 4 + .../google/calendar/calendarresource.cpp | 824 ++ resources/google/calendar/calendarresource.h | 83 + .../calendar/defaultreminderattribute.cpp | 118 + .../calendar/defaultreminderattribute.h | 45 + .../calendar/googlecalendarresource.desktop | 90 + resources/google/calendar/settings.cpp | 63 + resources/google/calendar/settings.h | 33 + resources/google/calendar/settingsbase.kcfg | 37 + resources/google/calendar/settingsbase.kcfgc | 7 + resources/google/calendar/settingsdialog.cpp | 227 + resources/google/calendar/settingsdialog.h | 57 + .../google/common/googleaccountmanager.cpp | 248 + .../google/common/googleaccountmanager.h | 71 + resources/google/common/googleresource.cpp | 439 + resources/google/common/googleresource.h | 129 + resources/google/common/googlesettings.cpp | 55 + resources/google/common/googlesettings.h | 56 + .../google/common/googlesettingsdialog.cpp | 267 + .../google/common/googlesettingsdialog.h | 85 + resources/google/contacts/CMakeLists.txt | 68 + resources/google/contacts/Messages.sh | 4 + .../google/contacts/contactsresource.cpp | 559 + resources/google/contacts/contactsresource.h | 81 + .../contacts/googlecontactsresource.desktop | 93 + resources/google/contacts/settings.cpp | 61 + resources/google/contacts/settings.h | 33 + resources/google/contacts/settingsbase.kcfg | 26 + resources/google/contacts/settingsbase.kcfgc | 7 + resources/google/contacts/settingsdialog.cpp | 50 + resources/google/contacts/settingsdialog.h | 37 + resources/ical/CMakeLists.txt | 45 + resources/ical/Messages.sh | 4 + resources/ical/autotests/CMakeLists.txt | 4 + resources/ical/autotests/event.ical | 26 + resources/ical/autotests/ical-empty.xml | 6 + resources/ical/autotests/ical-step1.xml | 52 + resources/ical/autotests/icaltest.es | 31 + resources/ical/autotests/task.ical | 16 + resources/ical/icalresource.cpp | 22 + resources/ical/icalresource.desktop | 104 + resources/ical/icalresource.kcfg | 26 + resources/ical/notes/CMakeLists.txt | 31 + resources/ical/notes/notesresource.cpp | 55 + resources/ical/notes/notesresource.desktop | 128 + resources/ical/notes/notesresource.h | 45 + resources/ical/notes/notesresource.kcfg | 30 + resources/ical/notes/settings.kcfgc | 9 + resources/ical/settings.kcfgc | 10 + resources/ical/shared/icalresource.cpp | 154 + resources/ical/shared/icalresource.h | 61 + resources/ical/shared/icalresourcebase.cpp | 168 + resources/ical/shared/icalresourcebase.h | 110 + resources/ical/wizard/CMakeLists.txt | 4 + resources/ical/wizard/Messages.sh | 4 + resources/ical/wizard/icalwizard.desktop | 105 + resources/ical/wizard/icalwizard.es.cmake | 43 + resources/ical/wizard/icalwizard.ui | 45 + resources/icaldir/CMakeLists.txt | 36 + resources/icaldir/Messages.sh | 3 + resources/icaldir/dirsettingsdialog.cpp | 93 + resources/icaldir/dirsettingsdialog.h | 51 + resources/icaldir/icaldirresource.cpp | 315 + resources/icaldir/icaldirresource.desktop | 89 + resources/icaldir/icaldirresource.h | 62 + resources/icaldir/icaldirresource.kcfg | 22 + resources/icaldir/settings.kcfgc | 7 + resources/icaldir/settingsdialog.ui | 229 + resources/imap/CMakeLists.txt | 130 + resources/imap/Messages.sh | 3 + resources/imap/addcollectiontask.cpp | 159 + resources/imap/addcollectiontask.h | 54 + resources/imap/additemtask.cpp | 215 + resources/imap/additemtask.h | 50 + resources/imap/autotests/CMakeLists.txt | 51 + .../imap/autotests/dummypasswordrequester.cpp | 83 + .../imap/autotests/dummypasswordrequester.h | 53 + .../imap/autotests/dummyresourcestate.cpp | 442 + resources/imap/autotests/dummyresourcestate.h | 186 + resources/imap/autotests/imaptestbase.cpp | 138 + resources/imap/autotests/imaptestbase.h | 62 + .../imap/autotests/testaddcollectiontask.cpp | 182 + resources/imap/autotests/testadditemtask.cpp | 179 + .../autotests/testchangecollectiontask.cpp | 244 + .../imap/autotests/testchangeitemtask.cpp | 214 + .../autotests/testexpungecollectiontask.cpp | 127 + .../imap/autotests/testmovecollectiontask.cpp | 195 + .../imap/autotests/testmoveitemstask.cpp | 254 + .../testremovecollectionrecursivetask.cpp | 247 + resources/imap/autotests/testresourcetask.cpp | 173 + .../testretrievecollectionmetadatatask.cpp | 324 + .../autotests/testretrievecollectionstask.cpp | 480 + .../imap/autotests/testretrieveitemstask.cpp | 614 ++ .../imap/autotests/testretrieveitemtask.cpp | 126 + resources/imap/autotests/testsessionpool.cpp | 780 ++ resources/imap/batchfetcher.cpp | 243 + resources/imap/batchfetcher.h | 79 + resources/imap/changecollectiontask.cpp | 311 + resources/imap/changecollectiontask.h | 54 + resources/imap/changeitemsflagstask.cpp | 154 + resources/imap/changeitemsflagstask.h | 56 + resources/imap/changeitemtask.cpp | 296 + resources/imap/changeitemtask.h | 61 + resources/imap/collectionmetadatahelper.cpp | 88 + resources/imap/collectionmetadatahelper.h | 32 + resources/imap/expungecollectiontask.cpp | 99 + resources/imap/expungecollectiontask.h | 46 + resources/imap/highestmodseqattribute.cpp | 59 + resources/imap/highestmodseqattribute.h | 41 + resources/imap/imapaccount.cpp | 109 + resources/imap/imapaccount.h | 69 + resources/imap/imapflags.cpp | 26 + resources/imap/imapflags.h | 51 + resources/imap/imapidlemanager.cpp | 175 + resources/imap/imapidlemanager.h | 82 + resources/imap/imapresource.cpp | 80 + resources/imap/imapresource.desktop | 101 + resources/imap/imapresource.h | 51 + resources/imap/imapresource.kcfg | 118 + resources/imap/imapresourcebase.cpp | 756 ++ resources/imap/imapresourcebase.h | 169 + resources/imap/main.cpp | 22 + resources/imap/messagehelper.cpp | 72 + resources/imap/messagehelper.h | 42 + resources/imap/movecollectiontask.cpp | 149 + resources/imap/movecollectiontask.h | 50 + resources/imap/moveitemstask.cpp | 319 + resources/imap/moveitemstask.h | 58 + resources/imap/noinferiorsattribute.cpp | 59 + resources/imap/noinferiorsattribute.h | 39 + resources/imap/noselectattribute.cpp | 60 + resources/imap/noselectattribute.h | 40 + resources/imap/passwordrequesterinterface.cpp | 33 + resources/imap/passwordrequesterinterface.h | 57 + .../imap/removecollectionrecursivetask.cpp | 171 + .../imap/removecollectionrecursivetask.h | 56 + resources/imap/replacemessagejob.cpp | 184 + resources/imap/replacemessagejob.h | 62 + resources/imap/resourcestate.cpp | 369 + resources/imap/resourcestate.h | 163 + resources/imap/resourcestateinterface.cpp | 55 + resources/imap/resourcestateinterface.h | 133 + resources/imap/resourcetask.cpp | 598 + resources/imap/resourcetask.h | 169 + .../imap/retrievecollectionmetadatatask.cpp | 292 + .../imap/retrievecollectionmetadatatask.h | 53 + resources/imap/retrievecollectionstask.cpp | 235 + resources/imap/retrievecollectionstask.h | 55 + resources/imap/retrieveitemstask.cpp | 646 ++ resources/imap/retrieveitemstask.h | 97 + resources/imap/retrieveitemtask.cpp | 147 + resources/imap/retrieveitemtask.h | 56 + resources/imap/searchtask.cpp | 224 + resources/imap/searchtask.h | 48 + resources/imap/serverinfo.ui | 24 + resources/imap/serverinfodialog.cpp | 57 + resources/imap/serverinfodialog.h | 42 + resources/imap/sessionpool.cpp | 555 + resources/imap/sessionpool.h | 139 + resources/imap/sessionuiproxy.h | 41 + resources/imap/settings.cpp | 338 + resources/imap/settings.h | 74 + resources/imap/settingsbase.kcfgc | 6 + resources/imap/settingspasswordrequester.cpp | 157 + resources/imap/settingspasswordrequester.h | 57 + resources/imap/setupserver.cpp | 670 ++ resources/imap/setupserver.h | 116 + resources/imap/setupserverview_desktop.ui | 702 ++ resources/imap/subscriptiondialog.cpp | 396 + resources/imap/subscriptiondialog.h | 120 + resources/imap/tests/CMakeLists.txt | 15 + .../imap/tests/testsubscriptiondialog.cpp | 59 + resources/imap/tracer.cpp | 61 + resources/imap/tracer.h | 7 + resources/imap/uidnextattribute.cpp | 60 + resources/imap/uidnextattribute.h | 40 + resources/imap/uidvalidityattribute.cpp | 60 + resources/imap/uidvalidityattribute.h | 40 + resources/imap/wizard/CMakeLists.txt | 2 + resources/imap/wizard/Messages.sh | 4 + resources/imap/wizard/imapwizard.desktop | 101 + resources/imap/wizard/imapwizard.es | 128 + resources/imap/wizard/imapwizard.ui | 97 + resources/kalarm/CMakeLists.txt | 3 + resources/kalarm/Messages.sh | 4 + resources/kalarm/kalarm/CMakeLists.txt | 49 + resources/kalarm/kalarm/kalarmresource.cpp | 513 + .../kalarm/kalarm/kalarmresource.desktop | 98 + resources/kalarm/kalarm/kalarmresource.h | 81 + resources/kalarm/kalarm/kalarmresource.kcfg | 33 + resources/kalarm/kalarm/settings.kcfgc | 10 + resources/kalarm/kalarmdir/CMakeLists.txt | 45 + resources/kalarm/kalarmdir/autoqpointer.h | 55 + .../kalarm/kalarmdir/kalarmdirresource.cpp | 1157 ++ .../kalarmdir/kalarmdirresource.desktop | 94 + .../kalarm/kalarmdir/kalarmdirresource.h | 109 + .../kalarm/kalarmdir/kalarmdirresource.kcfg | 32 + resources/kalarm/kalarmdir/settings.kcfgc | 8 + resources/kalarm/kalarmdir/settingsdialog.cpp | 144 + resources/kalarm/kalarmdir/settingsdialog.h | 68 + resources/kalarm/kalarmdir/settingsdialog.ui | 162 + .../kalarm/shared/alarmtyperadiowidget.cpp | 73 + .../kalarm/shared/alarmtyperadiowidget.h | 49 + .../kalarm/shared/alarmtyperadiowidget.ui | 50 + resources/kalarm/shared/alarmtypewidget.cpp | 61 + resources/kalarm/shared/alarmtypewidget.h | 47 + resources/kalarm/shared/alarmtypewidget.ui | 50 + .../kalarm/shared/kalarmresourcecommon.cpp | 200 + .../kalarm/shared/kalarmresourcecommon.h | 62 + resources/kolab/64-apps-kolab.png | Bin 0 -> 5496 bytes resources/kolab/CMakeLists.txt | 70 + resources/kolab/kolabaddtagtask.cpp | 194 + resources/kolab/kolabaddtagtask.h | 49 + .../kolab/kolabchangeitemsrelationstask.cpp | 206 + .../kolab/kolabchangeitemsrelationstask.h | 56 + resources/kolab/kolabchangeitemstagstask.cpp | 141 + resources/kolab/kolabchangeitemstagstask.h | 54 + resources/kolab/kolabchangetagtask.cpp | 82 + resources/kolab/kolabchangetagtask.h | 49 + resources/kolab/kolabhelpers.cpp | 502 + resources/kolab/kolabhelpers.h | 48 + resources/kolab/kolabmessagehelper.cpp | 58 + resources/kolab/kolabmessagehelper.h | 43 + resources/kolab/kolabrelationresourcetask.cpp | 138 + resources/kolab/kolabrelationresourcetask.h | 56 + resources/kolab/kolabremovetagtask.cpp | 97 + resources/kolab/kolabremovetagtask.h | 44 + resources/kolab/kolabresource.cpp | 234 + resources/kolab/kolabresource.desktop | 88 + resources/kolab/kolabresource.h | 72 + resources/kolab/kolabresourcestate.cpp | 103 + resources/kolab/kolabresourcestate.h | 38 + .../kolab/kolabretrievecollectionstask.cpp | 534 + .../kolab/kolabretrievecollectionstask.h | 101 + resources/kolab/kolabretrievetagstask.cpp | 227 + resources/kolab/kolabretrievetagstask.h | 70 + resources/kolab/kolabsettings.cpp | 53 + resources/kolab/kolabsettings.h | 35 + resources/kolab/tagchangehelper.cpp | 129 + resources/kolab/tagchangehelper.h | 71 + resources/kolab/tests/CMakeLists.txt | 25 + resources/kolab/tests/imaptestbase.cpp | 136 + resources/kolab/tests/imaptestbase.h | 62 + .../kolab/tests/testchangeitemstagstask.cpp | 235 + .../kolab/tests/testretrievetagstask.cpp | 184 + .../tests/unittestenv/config-mysql-db.xml | 8 + .../tests/unittestenv/config-mysql-fs.xml | 8 + .../unittestenv/config-postgresql-db.xml | 8 + .../unittestenv/config-postgresql-fs.xml | 8 + .../tests/unittestenv/config-sqlite-db.xml | 8 + .../kdehome/share/config/akonadi-firstrunrc | 4 + .../share/config/akonadi_knut_resource_0rc | 4 + .../unittestenv/kdehome/share/config/kdebugrc | 80 + .../unittestenv/kdehome/share/config/kdedrc | 3 + .../unittestenv/kdehome/testdata-res1.xml | 37 + .../akonadi/akonadiserverrc | 5 + .../akonadi/akonadiserverrc | 6 + .../akonadi/akonadiserverrc | 9 + .../akonadi/akonadiserverrc | 10 + .../akonadi/akonadiserverrc | 10 + resources/kolab/updatemessagejob.cpp | 235 + resources/kolab/updatemessagejob.h | 85 + resources/kolab/wizard/CMakeLists.txt | 5 + resources/kolab/wizard/Messages.sh | 4 + resources/kolab/wizard/kolabwizard.desktop | 101 + resources/kolab/wizard/kolabwizard.es | 322 + resources/kolab/wizard/kolabwizard.ui | 98 + resources/kolab/wizard/kolabwizard2.ui | 180 + resources/maildir/CMakeLists.txt | 59 + resources/maildir/Messages.sh | 3 + resources/maildir/autotests/CMakeLists.txt | 46 + resources/maildir/autotests/maildir-empty.xml | 5 + resources/maildir/autotests/maildir-step1.xml | 29 + resources/maildir/autotests/maildir-step2.xml | 29 + resources/maildir/autotests/maildir.js | 63 + resources/maildir/autotests/maildir.xml | 732 ++ .../grandchild/cur/1237726881.6570.rfoxg!2,S | 282 + .../.child1.directory/grandchild/new/.keep | 1 + .../.child1.directory/grandchild/tmp/.keep | 1 + .../child1/cur/1237726858.6570.dtdn4!2,S | 107 + .../child1/cur/1237726875.6570.R4KOW!2,S | 150 + .../maildir/.root.directory/child1/new/.keep | 1 + .../maildir/.root.directory/child1/tmp/.keep | 1 + .../maildir/.root.directory/child2/.keep | 1 + .../maildir/.root.directory/child2/cur/.keep | 1 + .../maildir/.root.directory/child2/new/.keep | 1 + .../maildir/.root.directory/child2/tmp/.keep | 1 + .../root/cur/1237726845.6570.BejQg!2,S | 171 + .../maildir/autotests/maildir/root/new/.keep | 1 + .../maildir/autotests/maildir/root/tmp/.keep | 1 + resources/maildir/autotests/synctest.cpp | 64 + resources/maildir/autotests/synctest.h | 43 + resources/maildir/autotests/testmail.mbox | 19 + .../maildir/autotests/unittestenv/config.xml | 6 + .../kdehome/share/config/akonadi-firstrunrc | 3 + .../share/config/akonadi_maildir_resource_0rc | 2 + .../unittestenv/kdehome/share/config/kdebugrc | 110 + .../unittestenv/kdehome/share/config/kdedrc | 3 + .../kdehome/share/config/kwalletrc | 2 + .../unittestenv/kdehome/share/config/qttestrc | 2 + .../unittestenv/kdehome/testdata.xml | 4 + .../xdgconfig/akonadi/akonadiserverrc | 4 + .../autotests/unittestenv/xdglocal/.keep | 1 + resources/maildir/configdialog.cpp | 146 + resources/maildir/configdialog.h | 55 + resources/maildir/libmaildir/CMakeLists.txt | 24 + .../libmaildir/autotests/CMakeLists.txt | 19 + .../libmaildir/autotests/testmaildir.cpp | 436 + .../libmaildir/autotests/testmaildir.h | 59 + resources/maildir/libmaildir/keycache.cpp | 94 + resources/maildir/libmaildir/keycache.h | 78 + resources/maildir/libmaildir/maildir.cpp | 869 ++ resources/maildir/libmaildir/maildir.h | 258 + resources/maildir/maildirresource.cpp | 891 ++ resources/maildir/maildirresource.desktop | 107 + resources/maildir/maildirresource.h | 106 + resources/maildir/maildirresource.kcfg | 25 + resources/maildir/main.cpp | 22 + resources/maildir/retrieveitemsjob.cpp | 209 + resources/maildir/retrieveitemsjob.h | 72 + resources/maildir/settings.kcfgc | 9 + resources/maildir/settings.ui | 96 + resources/maildir/wizard/CMakeLists.txt | 2 + resources/maildir/wizard/Messages.sh | 4 + .../maildir/wizard/maildirwizard.desktop | 106 + resources/maildir/wizard/maildirwizard.es | 41 + resources/maildir/wizard/maildirwizard.ui | 56 + resources/mbox/CMakeLists.txt | 52 + resources/mbox/Messages.sh | 3 + resources/mbox/autotests/CMakeLists.txt | 7 + .../autotests/deleteitemsattributetest.cpp | 84 + .../mbox/autotests/deleteitemsattributetest.h | 38 + resources/mbox/compactpage.cpp | 140 + resources/mbox/compactpage.h | 50 + resources/mbox/compactpage.ui | 132 + resources/mbox/deleteditemsattribute.cpp | 104 + resources/mbox/deleteditemsattribute.h | 65 + resources/mbox/lockfilepage.ui | 156 + resources/mbox/lockmethodpage.cpp | 59 + resources/mbox/lockmethodpage.h | 40 + resources/mbox/mboxresource.cpp | 378 + resources/mbox/mboxresource.desktop | 102 + resources/mbox/mboxresource.h | 68 + resources/mbox/mboxresource.kcfg | 54 + resources/mbox/settings.kcfgc | 8 + resources/mbox/wizard/CMakeLists.txt | 2 + resources/mbox/wizard/Messages.sh | 4 + resources/mbox/wizard/mailboxwizard.desktop | 104 + resources/mbox/wizard/mailboxwizard.es | 41 + resources/mbox/wizard/mailboxwizard.ui | 56 + resources/mixedmaildir/CMakeLists.txt | 69 + resources/mixedmaildir/Messages.sh | 3 + .../mixedmaildir/autotests/CMakeLists.txt | 205 + .../autotests/collectioncreatetest.cpp | 330 + .../autotests/collectiondeletetest.cpp | 468 + .../autotests/collectionfetchtest.cpp | 476 + .../autotests/collectionmodifytest.cpp | 631 ++ .../autotests/collectionmovetest.cpp | 2002 ++++ .../mixedmaildir/autotests/data/.dimap.index | Bin 0 -> 1441 bytes .../autotests/data/.maildir-tagged.index | Bin 0 -> 1525 bytes .../autotests/data/.maildir.index | Bin 0 -> 1441 bytes .../autotests/data/.mbox-tagged.index | Bin 0 -> 1317 bytes .../autotests/data/.mbox-unpurged.index | Bin 0 -> 673 bytes .../mixedmaildir/autotests/data/.mbox.index | Bin 0 -> 1257 bytes resources/mixedmaildir/autotests/data/README | 38 + .../data/dimap/cur/1279980064.4595.LUBVK | 46 + .../data/dimap/cur/1279980064.4595.RTmAd_2,S | 45 + .../data/dimap/cur/1279980064.4595.g8PCJ | 46 + .../data/dimap/cur/1279980064.4595.qs6V9_2,S | 48 + .../autotests/data/dimap/new/.keep | 1 + .../autotests/data/dimap/tmp/.keep | 1 + .../cur/1279982188.18722.6qZsA_2,S | 45 + .../cur/1279982188.18722.Xdz3R_2,S | 46 + .../cur/1279982188.18722.f0l49_2,S | 45 + .../cur/1279982188.18722.kwx1b_2,S | 47 + .../autotests/data/maildir-tagged/new/.keep | 1 + .../autotests/data/maildir-tagged/tmp/.keep | 1 + .../data/maildir/cur/1279979617.4595.bwXSm | 45 + .../maildir/cur/1279979618.4595.CStza_2,S | 47 + .../maildir/cur/1279979618.4595.DUl0I_2,S | 44 + .../data/maildir/cur/1279979618.4595.pY5ny | 45 + .../autotests/data/maildir/new/.keep | 1 + .../autotests/data/maildir/tmp/.keep | 1 + resources/mixedmaildir/autotests/data/mbox | 187 + .../mixedmaildir/autotests/data/mbox-tagged | 187 + .../mixedmaildir/autotests/data/mbox-unpurged | 187 + .../mixedmaildir/autotests/itemcreatetest.cpp | 538 + .../mixedmaildir/autotests/itemdeletetest.cpp | 609 ++ .../mixedmaildir/autotests/itemfetchtest.cpp | 1165 ++ .../mixedmaildir/autotests/itemmodifytest.cpp | 671 ++ .../mixedmaildir/autotests/itemmovetest.cpp | 676 ++ .../autotests/storecompacttest.cpp | 409 + .../autotests/templatemethodstest.cpp | 235 + resources/mixedmaildir/autotests/testdata.qrc | 31 + .../mixedmaildir/autotests/testdatatest.cpp | 106 + .../mixedmaildir/autotests/testdatautil.cpp | 201 + .../mixedmaildir/autotests/testdatautil.h | 43 + .../mixedmaildir/compactchangehelper.cpp | 236 + resources/mixedmaildir/compactchangehelper.h | 61 + resources/mixedmaildir/configdialog.cpp | 106 + resources/mixedmaildir/configdialog.h | 47 + .../mixedmaildir/kmindexreader/CMakeLists.txt | 23 + .../kmindexreader/autotests/CMakeLists.txt | 15 + .../autotests/TestIdxReader_data.h | 55 + .../kmindexreader/autotests/data/.keep | 1 + .../kmindexreader/autotests/testidxreader.cpp | 106 + .../kmindexreader/autotests/testidxreader.h | 39 + .../kmindexreader/kmindexreader.cpp | 617 ++ .../kmindexreader/kmindexreader.h | 150 + resources/mixedmaildir/mixedmaildir_debug.cpp | 22 + resources/mixedmaildir/mixedmaildir_debug.h | 27 + .../mixedmaildir/mixedmaildirresource.cpp | 831 ++ .../mixedmaildir/mixedmaildirresource.desktop | 95 + resources/mixedmaildir/mixedmaildirresource.h | 113 + .../mixedmaildir/mixedmaildirresource.kcfg | 22 + .../mixedmaildirresource_debug.cpp | 22 + .../mixedmaildir/mixedmaildirresource_debug.h | 27 + resources/mixedmaildir/mixedmaildirstore.cpp | 2390 ++++ resources/mixedmaildir/mixedmaildirstore.h | 53 + resources/mixedmaildir/retrieveitemsjob.cpp | 366 + resources/mixedmaildir/retrieveitemsjob.h | 70 + resources/mixedmaildir/settings.kcfgc | 8 + resources/mixedmaildir/settings.ui | 69 + resources/openxchange/CMakeLists.txt | 63 + resources/openxchange/Messages.sh | 3 + resources/openxchange/configdialog.cpp | 128 + resources/openxchange/configdialog.h | 51 + resources/openxchange/configdialog.ui | 156 + resources/openxchange/icons/128-apps-ox.png | Bin 0 -> 4840 bytes resources/openxchange/icons/16-apps-ox.png | Bin 0 -> 286 bytes resources/openxchange/icons/32-apps-ox.png | Bin 0 -> 534 bytes resources/openxchange/icons/48-apps-ox.png | Bin 0 -> 954 bytes resources/openxchange/icons/64-apps-ox.png | Bin 0 -> 1427 bytes resources/openxchange/icons/CMakeLists.txt | 1 + resources/openxchange/openxchangeresource.cpp | 1178 ++ .../openxchange/openxchangeresource.desktop | 90 + resources/openxchange/openxchangeresource.h | 87 + .../openxchange/openxchangeresource.kcfg | 35 + .../openxchange/oxa/connectiontestjob.cpp | 77 + resources/openxchange/oxa/connectiontestjob.h | 50 + resources/openxchange/oxa/contactutils.cpp | 435 + resources/openxchange/oxa/contactutils.h | 58 + resources/openxchange/oxa/davmanager.cpp | 73 + resources/openxchange/oxa/davmanager.h | 97 + resources/openxchange/oxa/davutils.cpp | 72 + resources/openxchange/oxa/davutils.h | 70 + resources/openxchange/oxa/folder.cpp | 198 + resources/openxchange/oxa/folder.h | 199 + resources/openxchange/oxa/foldercreatejob.cpp | 97 + resources/openxchange/oxa/foldercreatejob.h | 69 + resources/openxchange/oxa/folderdeletejob.cpp | 78 + resources/openxchange/oxa/folderdeletejob.h | 66 + resources/openxchange/oxa/foldermodifyjob.cpp | 96 + resources/openxchange/oxa/foldermodifyjob.h | 71 + resources/openxchange/oxa/foldermovejob.cpp | 96 + resources/openxchange/oxa/foldermovejob.h | 74 + .../openxchange/oxa/folderrequestjob.cpp | 85 + resources/openxchange/oxa/folderrequestjob.h | 71 + .../oxa/foldersrequestdeltajob.cpp | 95 + .../openxchange/oxa/foldersrequestdeltajob.h | 79 + .../openxchange/oxa/foldersrequestjob.cpp | 95 + resources/openxchange/oxa/foldersrequestjob.h | 81 + resources/openxchange/oxa/folderutils.cpp | 188 + resources/openxchange/oxa/folderutils.h | 53 + resources/openxchange/oxa/incidenceutils.cpp | 595 + resources/openxchange/oxa/incidenceutils.h | 61 + resources/openxchange/oxa/object.cpp | 123 + resources/openxchange/oxa/object.h | 97 + resources/openxchange/oxa/objectcreatejob.cpp | 120 + resources/openxchange/oxa/objectcreatejob.h | 53 + resources/openxchange/oxa/objectdeletejob.cpp | 78 + resources/openxchange/oxa/objectdeletejob.h | 50 + resources/openxchange/oxa/objectmodifyjob.cpp | 118 + resources/openxchange/oxa/objectmodifyjob.h | 53 + resources/openxchange/oxa/objectmovejob.cpp | 97 + resources/openxchange/oxa/objectmovejob.h | 54 + .../openxchange/oxa/objectrequestjob.cpp | 85 + resources/openxchange/oxa/objectrequestjob.h | 52 + .../oxa/objectsrequestdeltajob.cpp | 95 + .../openxchange/oxa/objectsrequestdeltajob.h | 81 + .../openxchange/oxa/objectsrequestjob.cpp | 96 + resources/openxchange/oxa/objectsrequestjob.h | 73 + resources/openxchange/oxa/objectutils.cpp | 133 + resources/openxchange/oxa/objectutils.h | 70 + resources/openxchange/oxa/oxerrors.cpp | 50 + resources/openxchange/oxa/oxerrors.h | 58 + resources/openxchange/oxa/oxutils.cpp | 137 + resources/openxchange/oxa/oxutils.h | 51 + resources/openxchange/oxa/updateusersjob.cpp | 94 + resources/openxchange/oxa/updateusersjob.h | 56 + resources/openxchange/oxa/user.cpp | 64 + resources/openxchange/oxa/user.h | 58 + .../openxchange/oxa/useridrequestjob.cpp | 78 + resources/openxchange/oxa/useridrequestjob.h | 50 + resources/openxchange/oxa/users.cpp | 159 + resources/openxchange/oxa/users.h | 70 + resources/openxchange/oxa/usersrequestjob.cpp | 100 + resources/openxchange/oxa/usersrequestjob.h | 52 + resources/openxchange/settings.kcfgc | 7 + resources/pop3/CMakeLists.txt | 59 + resources/pop3/Messages.sh | 3 + resources/pop3/TODO | 49 + resources/pop3/accountdialog.cpp | 664 ++ resources/pop3/accountdialog.h | 87 + resources/pop3/autotests/CMakeLists.txt | 53 + .../pop3/autotests/fakeserver/fakeserver.cpp | 293 + .../pop3/autotests/fakeserver/fakeserver.h | 105 + resources/pop3/autotests/pop3test.cpp | 916 ++ resources/pop3/autotests/pop3test.h | 73 + .../pop3/autotests/unittestenv/config.xml | 6 + .../unittestenv/kdehome/share/apps/.keep | 1 + .../kdehome/share/config/akonadi-firstrunrc | 3 + .../unittestenv/kdehome/share/config/kdebugrc | 103 + .../xdgconfig/akonadi/akonadiserverrc | 4 + .../pop3/autotests/unittestenv/xdglocal/.keep | 1 + resources/pop3/jobs.cpp | 500 + resources/pop3/jobs.h | 200 + resources/pop3/metatype.h | 29 + resources/pop3/pop3resource.cpp | 1009 ++ resources/pop3/pop3resource.desktop | 102 + resources/pop3/pop3resource.h | 203 + resources/pop3/popsettings.ui | 625 ++ resources/pop3/settings.cpp | 84 + resources/pop3/settings.h | 47 + resources/pop3/settings.kcfg | 93 + resources/pop3/settingsbase.kcfgc | 8 + resources/pop3/wizard/CMakeLists.txt | 2 + resources/pop3/wizard/Messages.sh | 4 + resources/pop3/wizard/pop3wizard.desktop | 97 + resources/pop3/wizard/pop3wizard.es | 63 + resources/pop3/wizard/pop3wizard.ui | 90 + resources/shared/CMakeLists.txt | 3 + resources/shared/filestore/CMakeLists.txt | 42 + resources/shared/filestore/Messages.sh | 2 + .../shared/filestore/abstractlocalstore.cpp | 884 ++ .../shared/filestore/abstractlocalstore.h | 125 + .../shared/filestore/autotests/CMakeLists.txt | 29 + .../autotests/abstractlocalstoretest.cpp | 971 ++ .../shared/filestore/collectioncreatejob.cpp | 68 + .../shared/filestore/collectioncreatejob.h | 65 + .../shared/filestore/collectiondeletejob.cpp | 62 + .../shared/filestore/collectiondeletejob.h | 63 + .../shared/filestore/collectionfetchjob.cpp | 95 + .../shared/filestore/collectionfetchjob.h | 81 + .../shared/filestore/collectionmodifyjob.cpp | 62 + .../shared/filestore/collectionmodifyjob.h | 63 + .../shared/filestore/collectionmovejob.cpp | 69 + .../shared/filestore/collectionmovejob.h | 65 + .../entitycompactchangeattribute.cpp | 106 + .../filestore/entitycompactchangeattribute.h | 70 + resources/shared/filestore/itemcreatejob.cpp | 66 + resources/shared/filestore/itemcreatejob.h | 65 + resources/shared/filestore/itemdeletejob.cpp | 59 + resources/shared/filestore/itemdeletejob.h | 62 + resources/shared/filestore/itemfetchjob.cpp | 96 + resources/shared/filestore/itemfetchjob.h | 78 + resources/shared/filestore/itemmodifyjob.cpp | 85 + resources/shared/filestore/itemmodifyjob.h | 70 + resources/shared/filestore/itemmovejob.cpp | 67 + resources/shared/filestore/itemmovejob.h | 66 + resources/shared/filestore/job.cpp | 50 + resources/shared/filestore/job.h | 108 + resources/shared/filestore/session.cpp | 144 + resources/shared/filestore/session_p.h | 88 + resources/shared/filestore/sessionimpls.cpp | 201 + resources/shared/filestore/sessionimpls_p.h | 60 + .../shared/filestore/storecompactjob.cpp | 78 + resources/shared/filestore/storecompactjob.h | 70 + resources/shared/filestore/storeinterface.h | 88 + .../shared/singlefileresource/CMakeLists.txt | 56 + .../shared/singlefileresource/Messages.sh | 4 + .../autotests/CMakeLists.txt | 16 + .../collectionannotationattributetest.cpp | 85 + .../autotests/imapaclattributetest.cpp | 256 + .../collectionannotationsattribute.cpp | 98 + .../collectionannotationsattribute.h | 48 + .../collectionflagsattribute.cpp | 68 + .../collectionflagsattribute.h | 46 + .../createandsettagsjob.cpp | 73 + .../singlefileresource/createandsettagsjob.h | 46 + .../singlefileresource/getcredentialsjob.cpp | 102 + .../singlefileresource/getcredentialsjob.h | 64 + .../singlefileresource/imapaclattribute.cpp | 163 + .../singlefileresource/imapaclattribute.h | 58 + .../singlefileresource/imapquotaattribute.cpp | 196 + .../singlefileresource/imapquotaattribute.h | 60 + .../singlefileresource/settingsdialog.ui | 229 + .../singlefileresource/singlefileresource.h | 386 + .../singlefileresourcebase.cpp | 289 + .../singlefileresourcebase.h | 189 + .../singlefileresourceconfigdialog.h | 60 + .../singlefileresourceconfigdialog.ui | 12 + .../singlefileresourceconfigdialog_desktop.ui | 175 + .../singlefileresourceconfigdialogbase.cpp | 233 + .../singlefileresourceconfigdialogbase.h | 146 + resources/vcard/CMakeLists.txt | 36 + resources/vcard/Messages.sh | 3 + resources/vcard/autotests/CMakeLists.txt | 6 + .../vcard/autotests/vcardtest-readonly.js | 12 + .../vcard/autotests/vcardtest-readonly.xml | 24 + resources/vcard/autotests/vcardtest.js | 11 + resources/vcard/autotests/vcardtest.vcf | 15 + resources/vcard/autotests/vcardtest.xml | 24 + resources/vcard/settings.kcfgc | 9 + resources/vcard/vcardresource.cpp | 196 + resources/vcard/vcardresource.desktop | 91 + resources/vcard/vcardresource.h | 60 + resources/vcard/vcardresource.kcfg | 26 + resources/vcard/wizard/CMakeLists.txt | 4 + resources/vcard/wizard/Messages.sh | 4 + resources/vcard/wizard/vcardwizard.desktop | 82 + resources/vcard/wizard/vcardwizard.es.cmake | 43 + resources/vcard/wizard/vcardwizard.ui | 45 + resources/vcarddir/CMakeLists.txt | 41 + resources/vcarddir/Messages.sh | 3 + resources/vcarddir/dirsettingsdialog.cpp | 93 + resources/vcarddir/dirsettingsdialog.h | 51 + resources/vcarddir/settings.kcfgc | 7 + resources/vcarddir/vcarddirresource.cpp | 287 + resources/vcarddir/vcarddirresource.desktop | 90 + resources/vcarddir/vcarddirresource.h | 61 + resources/vcarddir/vcarddirresource.kcfg | 22 + resources/vcarddir/wizard/CMakeLists.txt | 4 + resources/vcarddir/wizard/Messages.sh | 4 + .../vcarddir/wizard/vcarddirwizard.desktop | 83 + .../vcarddir/wizard/vcarddirwizard.es.cmake | 43 + resources/vcarddir/wizard/vcarddirwizard.ui | 45 + 970 files changed, 132999 insertions(+) create mode 100644 .arcconfig create mode 100644 .emacs-dirvars create mode 100644 .gitignore create mode 100644 .krazy create mode 100644 .reviewboardrc create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 CTestConfig.cmake create mode 100644 CTestCustom.cmake create mode 100644 MIGRATE-CONFIG-APPS create mode 100644 Mainpage.dox create mode 100644 README create mode 100644 agents/.krazy create mode 100644 agents/CMakeLists.txt create mode 100644 agents/Info.plist.template create mode 100644 agents/Mainpage.dox create mode 100644 agents/cmake/FindXsltproc.cmake create mode 100644 agents/invitations/CMakeLists.txt create mode 100755 agents/invitations/Messages.sh create mode 100644 agents/invitations/incidenceattribute.cpp create mode 100644 agents/invitations/incidenceattribute.h create mode 100644 agents/invitations/invitationsagent.cpp create mode 100644 agents/invitations/invitationsagent.desktop create mode 100644 agents/invitations/invitationsagent.h create mode 100644 agents/maildispatcher/CMakeLists.txt create mode 100755 agents/maildispatcher/Messages.sh create mode 100644 agents/maildispatcher/TODO create mode 100644 agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc create mode 100644 agents/maildispatcher/autotests/CMakeLists.txt create mode 100644 agents/maildispatcher/autotests/TODO create mode 100644 agents/maildispatcher/autotests/aborttest.cpp create mode 100644 agents/maildispatcher/autotests/aborttest.h create mode 100644 agents/maildispatcher/autotests/dupetest.cpp create mode 100644 agents/maildispatcher/autotests/dupetest.h create mode 100644 agents/maildispatcher/autotests/unittestenv/config.xml create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc create mode 100644 agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml create mode 100644 agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc create mode 100644 agents/maildispatcher/autotests/unittestenv/xdglocal/.keep create mode 100644 agents/maildispatcher/maildispatcheragent.cpp create mode 100644 agents/maildispatcher/maildispatcheragent.desktop create mode 100644 agents/maildispatcher/maildispatcheragent.h create mode 100644 agents/maildispatcher/maildispatcheragent.kcfg create mode 100644 agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml create mode 100644 agents/maildispatcher/outboxqueue.cpp create mode 100644 agents/maildispatcher/outboxqueue.h create mode 100644 agents/maildispatcher/sendjob.cpp create mode 100644 agents/maildispatcher/sendjob.h create mode 100644 agents/maildispatcher/sentactionhandler.cpp create mode 100644 agents/maildispatcher/sentactionhandler.h create mode 100644 agents/maildispatcher/settings.kcfgc create mode 100644 agents/maildispatcher/settings.ui create mode 100644 agents/maildispatcher/storeresultjob.cpp create mode 100644 agents/maildispatcher/storeresultjob.h create mode 100644 agents/migration/CMakeLists.txt create mode 100755 agents/migration/Messages.sh create mode 100644 agents/migration/autotests/CMakeLists.txt create mode 100644 agents/migration/autotests/dummymigrator.cpp create mode 100644 agents/migration/autotests/dummymigrator.h create mode 100644 agents/migration/autotests/schedulertest.cpp create mode 100644 agents/migration/migrationagent.cpp create mode 100644 agents/migration/migrationagent.desktop create mode 100644 agents/migration/migrationagent.h create mode 100644 agents/migration/migrationexecutor.cpp create mode 100644 agents/migration/migrationexecutor.h create mode 100644 agents/migration/migrationscheduler.cpp create mode 100644 agents/migration/migrationscheduler.h create mode 100644 agents/migration/migrationstatuswidget.cpp create mode 100644 agents/migration/migrationstatuswidget.h create mode 100644 agents/newmailnotifier/CMakeLists.txt create mode 100755 agents/newmailnotifier/Messages.sh create mode 100644 agents/newmailnotifier/TODO create mode 100644 agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc create mode 100644 agents/newmailnotifier/newmailnotifieragent.cpp create mode 100644 agents/newmailnotifier/newmailnotifieragent.desktop create mode 100644 agents/newmailnotifier/newmailnotifieragent.h create mode 100644 agents/newmailnotifier/newmailnotifieragentsettings.kcfg create mode 100644 agents/newmailnotifier/newmailnotifieragentsettings.kcfgc create mode 100644 agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp create mode 100644 agents/newmailnotifier/newmailnotifierselectcollectionwidget.h create mode 100644 agents/newmailnotifier/newmailnotifiersettingsdialog.cpp create mode 100644 agents/newmailnotifier/newmailnotifiersettingsdialog.h create mode 100644 agents/newmailnotifier/newmailnotifiershowmessagejob.cpp create mode 100644 agents/newmailnotifier/newmailnotifiershowmessagejob.h create mode 100644 agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml create mode 100644 agents/newmailnotifier/specialnotifierjob.cpp create mode 100644 agents/newmailnotifier/specialnotifierjob.h create mode 100644 akonadi-prefix.h.cmake create mode 100644 akonadi-version.h.cmake create mode 100644 cmake/modules/FindXsltproc.cmake create mode 100644 defaultsetup/CMakeLists.txt create mode 100644 defaultsetup/birthdaycalendar.desktop create mode 100644 defaultsetup/defaultaddressbook.desktop create mode 100644 defaultsetup/defaultcalendar.desktop create mode 100644 defaultsetup/defaultnotebook.desktop create mode 100644 doc/git-migration.txt create mode 100644 doc/libakonadi.xmi create mode 100644 doc/pics/akonadi_agent_handling.eps create mode 100644 doc/pics/akonadi_agent_handling.png create mode 100644 doc/pics/akonadi_agent_handling_small.png create mode 100644 doc/pics/akonadi_client_search.eps create mode 100644 doc/pics/akonadi_client_search.png create mode 100644 doc/pics/akonadi_client_search_small.png create mode 100644 doc/pics/akonadi_communication.xmi create mode 100644 doc/pics/akonadi_concept_schema.sla create mode 100644 doc/pics/akonadi_overview_uml.png create mode 100644 doc/pics/akonadi_overview_uml.ps create mode 100644 doc/pics/akonadi_overview_uml_small.png create mode 100644 doc/pics/concept.eps create mode 100644 doc/pics/concept.png create mode 100644 doc/pics/concept.sla create mode 100755 doc/pics/convert.sh create mode 100644 doc/todo.dox create mode 100644 kdepim-mime.xml create mode 100644 kdepim-runtime-version.h.cmake create mode 100644 kdepim-runtime.categories create mode 100644 kioslave/CMakeLists.txt create mode 100644 kioslave/Messages.sh create mode 100644 kioslave/akonadi.protocol create mode 100644 kioslave/akonadislave.cpp create mode 100644 kioslave/akonadislave.h create mode 100644 migration/CMakeLists.txt create mode 100644 migration/cmake/FindXsltproc.cmake create mode 100644 migration/entitytreecreatejob.cpp create mode 100644 migration/entitytreecreatejob.h create mode 100644 migration/gid/CMakeLists.txt create mode 100644 migration/gid/Messages.sh create mode 100644 migration/gid/gidmigrationjob.cpp create mode 100644 migration/gid/gidmigrationjob.h create mode 100644 migration/gid/gidmigrator.cpp create mode 100644 migration/gid/gidmigrator.h create mode 100644 migration/gid/main.cpp create mode 100644 migration/infodialog.cpp create mode 100644 migration/infodialog.h create mode 100644 migration/kmigratorbase.cpp create mode 100644 migration/kmigratorbase.h create mode 100644 migration/migratorbase.cpp create mode 100644 migration/migratorbase.h create mode 100644 plugins/CMakeLists.txt create mode 100644 plugins/Messages.sh create mode 100644 plugins/akonadi_serializer_addressee.cpp create mode 100644 plugins/akonadi_serializer_addressee.desktop create mode 100644 plugins/akonadi_serializer_addressee.h create mode 100644 plugins/akonadi_serializer_contactgroup.cpp create mode 100644 plugins/akonadi_serializer_contactgroup.desktop create mode 100644 plugins/akonadi_serializer_contactgroup.h create mode 100644 plugins/akonadi_serializer_kalarm.cpp create mode 100644 plugins/akonadi_serializer_kalarm.desktop create mode 100644 plugins/akonadi_serializer_kalarm.h create mode 100644 plugins/akonadi_serializer_kcalcore.cpp create mode 100644 plugins/akonadi_serializer_kcalcore.desktop create mode 100644 plugins/akonadi_serializer_kcalcore.h create mode 100644 plugins/akonadi_serializer_mail.cpp create mode 100644 plugins/akonadi_serializer_mail.desktop create mode 100644 plugins/akonadi_serializer_mail.h create mode 100644 plugins/autotests/CMakeLists.txt create mode 100644 plugins/autotests/addresseeserializertest.cpp create mode 100644 plugins/autotests/kcalcoreserializertest.cpp create mode 100644 plugins/autotests/mailserializerplugintest.cpp create mode 100644 plugins/autotests/mailserializerplugintest.h create mode 100644 plugins/autotests/mailserializertest.cpp create mode 100644 plugins/autotests/mailserializertest.h create mode 100644 plugins/kaeventformatter.cpp create mode 100644 plugins/kaeventformatter.h create mode 100644 resources/.krazy create mode 100644 resources/CMakeLists.txt create mode 100644 resources/Info.plist.template create mode 100644 resources/Mainpage.dox create mode 100644 resources/akonotes/CMakeLists.txt create mode 100644 resources/akonotes/Messages.sh create mode 100644 resources/akonotes/akonotesresource.cpp create mode 100644 resources/akonotes/akonotesresource.desktop create mode 100644 resources/akonotes/akonotesresource.h create mode 100644 resources/birthdays/CMakeLists.txt create mode 100644 resources/birthdays/Messages.sh create mode 100644 resources/birthdays/birthdaysresource.cpp create mode 100644 resources/birthdays/birthdaysresource.desktop create mode 100644 resources/birthdays/birthdaysresource.h create mode 100644 resources/birthdays/birthdaysresource.kcfg create mode 100644 resources/birthdays/configdialog.cpp create mode 100644 resources/birthdays/configdialog.h create mode 100644 resources/birthdays/configdialog.ui create mode 100644 resources/birthdays/settings.kcfgc create mode 100644 resources/cmake/FindXsltproc.cmake create mode 100644 resources/contacts/CMakeLists.txt create mode 100644 resources/contacts/Messages.sh create mode 100644 resources/contacts/contactsresource.cpp create mode 100644 resources/contacts/contactsresource.desktop create mode 100644 resources/contacts/contactsresource.h create mode 100644 resources/contacts/contactsresource.kcfg create mode 100644 resources/contacts/settings.kcfgc create mode 100644 resources/contacts/settingsdialog.cpp create mode 100644 resources/contacts/settingsdialog.h create mode 100644 resources/contacts/settingsdialog.ui create mode 100644 resources/contacts/wizard/CMakeLists.txt create mode 100644 resources/contacts/wizard/Messages.sh create mode 100644 resources/contacts/wizard/contactswizard.desktop create mode 100644 resources/contacts/wizard/contactswizard.es.cmake create mode 100644 resources/contacts/wizard/contactswizard.ui create mode 100644 resources/dav/CMakeLists.txt create mode 100644 resources/dav/COPYING create mode 100644 resources/dav/README create mode 100644 resources/dav/TODO create mode 100644 resources/dav/common/davcollection.cpp create mode 100644 resources/dav/common/davcollection.h create mode 100644 resources/dav/common/davcollectiondeletejob.cpp create mode 100644 resources/dav/common/davcollectiondeletejob.h create mode 100644 resources/dav/common/davcollectionmodifyjob.cpp create mode 100644 resources/dav/common/davcollectionmodifyjob.h create mode 100644 resources/dav/common/davcollectionsfetchjob.cpp create mode 100644 resources/dav/common/davcollectionsfetchjob.h create mode 100644 resources/dav/common/davcollectionsmultifetchjob.cpp create mode 100644 resources/dav/common/davcollectionsmultifetchjob.h create mode 100644 resources/dav/common/davitem.cpp create mode 100644 resources/dav/common/davitem.h create mode 100644 resources/dav/common/davitemcreatejob.cpp create mode 100644 resources/dav/common/davitemcreatejob.h create mode 100644 resources/dav/common/davitemdeletejob.cpp create mode 100644 resources/dav/common/davitemdeletejob.h create mode 100644 resources/dav/common/davitemfetchjob.cpp create mode 100644 resources/dav/common/davitemfetchjob.h create mode 100644 resources/dav/common/davitemmodifyjob.cpp create mode 100644 resources/dav/common/davitemmodifyjob.h create mode 100644 resources/dav/common/davitemsfetchjob.cpp create mode 100644 resources/dav/common/davitemsfetchjob.h create mode 100644 resources/dav/common/davitemslistjob.cpp create mode 100644 resources/dav/common/davitemslistjob.h create mode 100644 resources/dav/common/davjobbase.cpp create mode 100644 resources/dav/common/davjobbase.h create mode 100644 resources/dav/common/davmanager.cpp create mode 100644 resources/dav/common/davmanager.h create mode 100644 resources/dav/common/davmultigetprotocol.cpp create mode 100644 resources/dav/common/davmultigetprotocol.h create mode 100644 resources/dav/common/davprincipalhomesetsfetchjob.cpp create mode 100644 resources/dav/common/davprincipalhomesetsfetchjob.h create mode 100644 resources/dav/common/davprincipalsearchjob.cpp create mode 100644 resources/dav/common/davprincipalsearchjob.h create mode 100644 resources/dav/common/davprotocolbase.cpp create mode 100644 resources/dav/common/davprotocolbase.h create mode 100644 resources/dav/common/davutils.cpp create mode 100644 resources/dav/common/davutils.h create mode 100644 resources/dav/common/etagcache.cpp create mode 100644 resources/dav/common/etagcache.h create mode 100644 resources/dav/protocols/caldavprotocol.cpp create mode 100644 resources/dav/protocols/caldavprotocol.h create mode 100644 resources/dav/protocols/carddavprotocol.cpp create mode 100644 resources/dav/protocols/carddavprotocol.h create mode 100644 resources/dav/protocols/groupdavprotocol.cpp create mode 100644 resources/dav/protocols/groupdavprotocol.h create mode 100644 resources/dav/resource/CMakeLists.txt create mode 100644 resources/dav/resource/Messages.sh create mode 100644 resources/dav/resource/akonadi-resources.png create mode 100644 resources/dav/resource/configdialog.cpp create mode 100644 resources/dav/resource/configdialog.h create mode 100644 resources/dav/resource/configdialog.ui create mode 100644 resources/dav/resource/ctagattribute.cpp create mode 100644 resources/dav/resource/ctagattribute.h create mode 100644 resources/dav/resource/davfreebusyhandler.cpp create mode 100644 resources/dav/resource/davfreebusyhandler.h create mode 100644 resources/dav/resource/davgroupwareprovider.desktop create mode 100644 resources/dav/resource/davgroupwareresource.cpp create mode 100644 resources/dav/resource/davgroupwareresource.desktop create mode 100644 resources/dav/resource/davgroupwareresource.h create mode 100644 resources/dav/resource/davgroupwareresource.kcfg create mode 100644 resources/dav/resource/davprotocolattribute.cpp create mode 100644 resources/dav/resource/davprotocolattribute.h create mode 100644 resources/dav/resource/searchdialog.cpp create mode 100644 resources/dav/resource/searchdialog.h create mode 100644 resources/dav/resource/searchdialog.ui create mode 100644 resources/dav/resource/settings.cpp create mode 100644 resources/dav/resource/settings.h create mode 100644 resources/dav/resource/settingsbase.kcfgc create mode 100644 resources/dav/resource/setupwizard.cpp create mode 100644 resources/dav/resource/setupwizard.h create mode 100644 resources/dav/resource/urlconfigurationdialog.cpp create mode 100644 resources/dav/resource/urlconfigurationdialog.h create mode 100644 resources/dav/resource/urlconfigurationdialog.ui create mode 100644 resources/dav/services/citadel.desktop create mode 100644 resources/dav/services/davical.desktop create mode 100644 resources/dav/services/egroupware.desktop create mode 100644 resources/dav/services/opengroupware.desktop create mode 100644 resources/dav/services/owncloud-pre5.desktop create mode 100644 resources/dav/services/owncloud.desktop create mode 100644 resources/dav/services/scalix.desktop create mode 100644 resources/dav/services/sogo.desktop create mode 100644 resources/dav/services/yahoo.desktop create mode 100644 resources/dav/services/zarafa.desktop create mode 100644 resources/dav/services/zimbra.desktop create mode 100644 resources/folderarchivesettings/CMakeLists.txt create mode 100755 resources/folderarchivesettings/Messages.sh create mode 100644 resources/folderarchivesettings/autotests/CMakeLists.txt create mode 100644 resources/folderarchivesettings/autotests/folderarchiveaccountinfotest.cpp create mode 100644 resources/folderarchivesettings/autotests/folderarchiveaccountinfotest.h create mode 100644 resources/folderarchivesettings/folderarchiveaccountinfo.cpp create mode 100644 resources/folderarchivesettings/folderarchiveaccountinfo.h create mode 100644 resources/folderarchivesettings/folderarchivesettingpage.cpp create mode 100644 resources/folderarchivesettings/folderarchivesettingpage.h create mode 100644 resources/folderarchivesettings/folderarchiveutil.cpp create mode 100644 resources/folderarchivesettings/folderarchiveutil.h create mode 100644 resources/gmail/CMakeLists.txt create mode 100755 resources/gmail/Messages.sh create mode 100644 resources/gmail/gmailchangeitemslabelstask.cpp create mode 100644 resources/gmail/gmailchangeitemslabelstask.h create mode 100644 resources/gmail/gmailconfigdialog.cpp create mode 100644 resources/gmail/gmailconfigdialog.h create mode 100644 resources/gmail/gmailconfigdialog.ui create mode 100644 resources/gmail/gmaillabelattribute.cpp create mode 100644 resources/gmail/gmaillabelattribute.h create mode 100644 resources/gmail/gmaillinkitemstask.cpp create mode 100644 resources/gmail/gmaillinkitemstask.h create mode 100644 resources/gmail/gmailmessagehelper.cpp create mode 100644 resources/gmail/gmailmessagehelper.h create mode 100644 resources/gmail/gmailpasswordrequester.cpp create mode 100644 resources/gmail/gmailpasswordrequester.h create mode 100644 resources/gmail/gmailresource.cpp create mode 100644 resources/gmail/gmailresource.desktop create mode 100644 resources/gmail/gmailresource.h create mode 100644 resources/gmail/gmailresource.kcfg create mode 100644 resources/gmail/gmailresourcestate.cpp create mode 100644 resources/gmail/gmailresourcestate.h create mode 100644 resources/gmail/gmailretrievecollectionstask.cpp create mode 100644 resources/gmail/gmailretrievecollectionstask.h create mode 100644 resources/gmail/gmailretrieveitemstask.cpp create mode 100644 resources/gmail/gmailretrieveitemstask.h create mode 100644 resources/gmail/gmailsettings.cpp create mode 100644 resources/gmail/gmailsettings.h create mode 100644 resources/gmail/saslplugin/CMakeLists.txt create mode 100644 resources/gmail/saslplugin/config.h create mode 100644 resources/gmail/saslplugin/plugin_common.c create mode 100644 resources/gmail/saslplugin/plugin_common.h create mode 100644 resources/gmail/saslplugin/xoauth2plugin.c create mode 100644 resources/gmail/saslplugin/xoauth2plugin_init.c create mode 100644 resources/gmail/settingsbase.kcfgc create mode 100644 resources/google/CMakeLists.txt create mode 100644 resources/google/calendar/CMakeLists.txt create mode 100644 resources/google/calendar/Messages.sh create mode 100644 resources/google/calendar/calendarresource.cpp create mode 100644 resources/google/calendar/calendarresource.h create mode 100644 resources/google/calendar/defaultreminderattribute.cpp create mode 100644 resources/google/calendar/defaultreminderattribute.h create mode 100644 resources/google/calendar/googlecalendarresource.desktop create mode 100644 resources/google/calendar/settings.cpp create mode 100644 resources/google/calendar/settings.h create mode 100644 resources/google/calendar/settingsbase.kcfg create mode 100644 resources/google/calendar/settingsbase.kcfgc create mode 100644 resources/google/calendar/settingsdialog.cpp create mode 100644 resources/google/calendar/settingsdialog.h create mode 100644 resources/google/common/googleaccountmanager.cpp create mode 100644 resources/google/common/googleaccountmanager.h create mode 100644 resources/google/common/googleresource.cpp create mode 100644 resources/google/common/googleresource.h create mode 100644 resources/google/common/googlesettings.cpp create mode 100644 resources/google/common/googlesettings.h create mode 100644 resources/google/common/googlesettingsdialog.cpp create mode 100644 resources/google/common/googlesettingsdialog.h create mode 100644 resources/google/contacts/CMakeLists.txt create mode 100644 resources/google/contacts/Messages.sh create mode 100644 resources/google/contacts/contactsresource.cpp create mode 100644 resources/google/contacts/contactsresource.h create mode 100644 resources/google/contacts/googlecontactsresource.desktop create mode 100644 resources/google/contacts/settings.cpp create mode 100644 resources/google/contacts/settings.h create mode 100644 resources/google/contacts/settingsbase.kcfg create mode 100644 resources/google/contacts/settingsbase.kcfgc create mode 100644 resources/google/contacts/settingsdialog.cpp create mode 100644 resources/google/contacts/settingsdialog.h create mode 100644 resources/ical/CMakeLists.txt create mode 100644 resources/ical/Messages.sh create mode 100644 resources/ical/autotests/CMakeLists.txt create mode 100644 resources/ical/autotests/event.ical create mode 100644 resources/ical/autotests/ical-empty.xml create mode 100644 resources/ical/autotests/ical-step1.xml create mode 100644 resources/ical/autotests/icaltest.es create mode 100644 resources/ical/autotests/task.ical create mode 100644 resources/ical/icalresource.cpp create mode 100644 resources/ical/icalresource.desktop create mode 100644 resources/ical/icalresource.kcfg create mode 100644 resources/ical/notes/CMakeLists.txt create mode 100644 resources/ical/notes/notesresource.cpp create mode 100644 resources/ical/notes/notesresource.desktop create mode 100644 resources/ical/notes/notesresource.h create mode 100644 resources/ical/notes/notesresource.kcfg create mode 100644 resources/ical/notes/settings.kcfgc create mode 100644 resources/ical/settings.kcfgc create mode 100644 resources/ical/shared/icalresource.cpp create mode 100644 resources/ical/shared/icalresource.h create mode 100644 resources/ical/shared/icalresourcebase.cpp create mode 100644 resources/ical/shared/icalresourcebase.h create mode 100644 resources/ical/wizard/CMakeLists.txt create mode 100644 resources/ical/wizard/Messages.sh create mode 100644 resources/ical/wizard/icalwizard.desktop create mode 100644 resources/ical/wizard/icalwizard.es.cmake create mode 100644 resources/ical/wizard/icalwizard.ui create mode 100644 resources/icaldir/CMakeLists.txt create mode 100644 resources/icaldir/Messages.sh create mode 100644 resources/icaldir/dirsettingsdialog.cpp create mode 100644 resources/icaldir/dirsettingsdialog.h create mode 100644 resources/icaldir/icaldirresource.cpp create mode 100644 resources/icaldir/icaldirresource.desktop create mode 100644 resources/icaldir/icaldirresource.h create mode 100644 resources/icaldir/icaldirresource.kcfg create mode 100644 resources/icaldir/settings.kcfgc create mode 100644 resources/icaldir/settingsdialog.ui create mode 100644 resources/imap/CMakeLists.txt create mode 100755 resources/imap/Messages.sh create mode 100644 resources/imap/addcollectiontask.cpp create mode 100644 resources/imap/addcollectiontask.h create mode 100644 resources/imap/additemtask.cpp create mode 100644 resources/imap/additemtask.h create mode 100644 resources/imap/autotests/CMakeLists.txt create mode 100644 resources/imap/autotests/dummypasswordrequester.cpp create mode 100644 resources/imap/autotests/dummypasswordrequester.h create mode 100644 resources/imap/autotests/dummyresourcestate.cpp create mode 100644 resources/imap/autotests/dummyresourcestate.h create mode 100644 resources/imap/autotests/imaptestbase.cpp create mode 100644 resources/imap/autotests/imaptestbase.h create mode 100644 resources/imap/autotests/testaddcollectiontask.cpp create mode 100644 resources/imap/autotests/testadditemtask.cpp create mode 100644 resources/imap/autotests/testchangecollectiontask.cpp create mode 100644 resources/imap/autotests/testchangeitemtask.cpp create mode 100644 resources/imap/autotests/testexpungecollectiontask.cpp create mode 100644 resources/imap/autotests/testmovecollectiontask.cpp create mode 100644 resources/imap/autotests/testmoveitemstask.cpp create mode 100644 resources/imap/autotests/testremovecollectionrecursivetask.cpp create mode 100644 resources/imap/autotests/testresourcetask.cpp create mode 100644 resources/imap/autotests/testretrievecollectionmetadatatask.cpp create mode 100644 resources/imap/autotests/testretrievecollectionstask.cpp create mode 100644 resources/imap/autotests/testretrieveitemstask.cpp create mode 100644 resources/imap/autotests/testretrieveitemtask.cpp create mode 100644 resources/imap/autotests/testsessionpool.cpp create mode 100644 resources/imap/batchfetcher.cpp create mode 100644 resources/imap/batchfetcher.h create mode 100644 resources/imap/changecollectiontask.cpp create mode 100644 resources/imap/changecollectiontask.h create mode 100644 resources/imap/changeitemsflagstask.cpp create mode 100644 resources/imap/changeitemsflagstask.h create mode 100644 resources/imap/changeitemtask.cpp create mode 100644 resources/imap/changeitemtask.h create mode 100644 resources/imap/collectionmetadatahelper.cpp create mode 100644 resources/imap/collectionmetadatahelper.h create mode 100644 resources/imap/expungecollectiontask.cpp create mode 100644 resources/imap/expungecollectiontask.h create mode 100644 resources/imap/highestmodseqattribute.cpp create mode 100644 resources/imap/highestmodseqattribute.h create mode 100644 resources/imap/imapaccount.cpp create mode 100644 resources/imap/imapaccount.h create mode 100644 resources/imap/imapflags.cpp create mode 100644 resources/imap/imapflags.h create mode 100644 resources/imap/imapidlemanager.cpp create mode 100644 resources/imap/imapidlemanager.h create mode 100644 resources/imap/imapresource.cpp create mode 100644 resources/imap/imapresource.desktop create mode 100644 resources/imap/imapresource.h create mode 100644 resources/imap/imapresource.kcfg create mode 100644 resources/imap/imapresourcebase.cpp create mode 100644 resources/imap/imapresourcebase.h create mode 100644 resources/imap/main.cpp create mode 100644 resources/imap/messagehelper.cpp create mode 100644 resources/imap/messagehelper.h create mode 100644 resources/imap/movecollectiontask.cpp create mode 100644 resources/imap/movecollectiontask.h create mode 100644 resources/imap/moveitemstask.cpp create mode 100644 resources/imap/moveitemstask.h create mode 100644 resources/imap/noinferiorsattribute.cpp create mode 100644 resources/imap/noinferiorsattribute.h create mode 100644 resources/imap/noselectattribute.cpp create mode 100644 resources/imap/noselectattribute.h create mode 100644 resources/imap/passwordrequesterinterface.cpp create mode 100644 resources/imap/passwordrequesterinterface.h create mode 100644 resources/imap/removecollectionrecursivetask.cpp create mode 100644 resources/imap/removecollectionrecursivetask.h create mode 100644 resources/imap/replacemessagejob.cpp create mode 100644 resources/imap/replacemessagejob.h create mode 100644 resources/imap/resourcestate.cpp create mode 100644 resources/imap/resourcestate.h create mode 100644 resources/imap/resourcestateinterface.cpp create mode 100644 resources/imap/resourcestateinterface.h create mode 100644 resources/imap/resourcetask.cpp create mode 100644 resources/imap/resourcetask.h create mode 100644 resources/imap/retrievecollectionmetadatatask.cpp create mode 100644 resources/imap/retrievecollectionmetadatatask.h create mode 100644 resources/imap/retrievecollectionstask.cpp create mode 100644 resources/imap/retrievecollectionstask.h create mode 100644 resources/imap/retrieveitemstask.cpp create mode 100644 resources/imap/retrieveitemstask.h create mode 100644 resources/imap/retrieveitemtask.cpp create mode 100644 resources/imap/retrieveitemtask.h create mode 100644 resources/imap/searchtask.cpp create mode 100644 resources/imap/searchtask.h create mode 100644 resources/imap/serverinfo.ui create mode 100644 resources/imap/serverinfodialog.cpp create mode 100644 resources/imap/serverinfodialog.h create mode 100644 resources/imap/sessionpool.cpp create mode 100644 resources/imap/sessionpool.h create mode 100644 resources/imap/sessionuiproxy.h create mode 100644 resources/imap/settings.cpp create mode 100644 resources/imap/settings.h create mode 100644 resources/imap/settingsbase.kcfgc create mode 100644 resources/imap/settingspasswordrequester.cpp create mode 100644 resources/imap/settingspasswordrequester.h create mode 100644 resources/imap/setupserver.cpp create mode 100644 resources/imap/setupserver.h create mode 100644 resources/imap/setupserverview_desktop.ui create mode 100644 resources/imap/subscriptiondialog.cpp create mode 100644 resources/imap/subscriptiondialog.h create mode 100644 resources/imap/tests/CMakeLists.txt create mode 100644 resources/imap/tests/testsubscriptiondialog.cpp create mode 100644 resources/imap/tracer.cpp create mode 100644 resources/imap/tracer.h create mode 100644 resources/imap/uidnextattribute.cpp create mode 100644 resources/imap/uidnextattribute.h create mode 100644 resources/imap/uidvalidityattribute.cpp create mode 100644 resources/imap/uidvalidityattribute.h create mode 100644 resources/imap/wizard/CMakeLists.txt create mode 100755 resources/imap/wizard/Messages.sh create mode 100644 resources/imap/wizard/imapwizard.desktop create mode 100644 resources/imap/wizard/imapwizard.es create mode 100644 resources/imap/wizard/imapwizard.ui create mode 100644 resources/kalarm/CMakeLists.txt create mode 100755 resources/kalarm/Messages.sh create mode 100644 resources/kalarm/kalarm/CMakeLists.txt create mode 100644 resources/kalarm/kalarm/kalarmresource.cpp create mode 100644 resources/kalarm/kalarm/kalarmresource.desktop create mode 100644 resources/kalarm/kalarm/kalarmresource.h create mode 100644 resources/kalarm/kalarm/kalarmresource.kcfg create mode 100644 resources/kalarm/kalarm/settings.kcfgc create mode 100644 resources/kalarm/kalarmdir/CMakeLists.txt create mode 100644 resources/kalarm/kalarmdir/autoqpointer.h create mode 100644 resources/kalarm/kalarmdir/kalarmdirresource.cpp create mode 100644 resources/kalarm/kalarmdir/kalarmdirresource.desktop create mode 100644 resources/kalarm/kalarmdir/kalarmdirresource.h create mode 100644 resources/kalarm/kalarmdir/kalarmdirresource.kcfg create mode 100644 resources/kalarm/kalarmdir/settings.kcfgc create mode 100644 resources/kalarm/kalarmdir/settingsdialog.cpp create mode 100644 resources/kalarm/kalarmdir/settingsdialog.h create mode 100644 resources/kalarm/kalarmdir/settingsdialog.ui create mode 100644 resources/kalarm/shared/alarmtyperadiowidget.cpp create mode 100644 resources/kalarm/shared/alarmtyperadiowidget.h create mode 100644 resources/kalarm/shared/alarmtyperadiowidget.ui create mode 100644 resources/kalarm/shared/alarmtypewidget.cpp create mode 100644 resources/kalarm/shared/alarmtypewidget.h create mode 100644 resources/kalarm/shared/alarmtypewidget.ui create mode 100644 resources/kalarm/shared/kalarmresourcecommon.cpp create mode 100644 resources/kalarm/shared/kalarmresourcecommon.h create mode 100644 resources/kolab/64-apps-kolab.png create mode 100644 resources/kolab/CMakeLists.txt create mode 100644 resources/kolab/kolabaddtagtask.cpp create mode 100644 resources/kolab/kolabaddtagtask.h create mode 100644 resources/kolab/kolabchangeitemsrelationstask.cpp create mode 100644 resources/kolab/kolabchangeitemsrelationstask.h create mode 100644 resources/kolab/kolabchangeitemstagstask.cpp create mode 100644 resources/kolab/kolabchangeitemstagstask.h create mode 100644 resources/kolab/kolabchangetagtask.cpp create mode 100644 resources/kolab/kolabchangetagtask.h create mode 100644 resources/kolab/kolabhelpers.cpp create mode 100644 resources/kolab/kolabhelpers.h create mode 100644 resources/kolab/kolabmessagehelper.cpp create mode 100644 resources/kolab/kolabmessagehelper.h create mode 100644 resources/kolab/kolabrelationresourcetask.cpp create mode 100644 resources/kolab/kolabrelationresourcetask.h create mode 100644 resources/kolab/kolabremovetagtask.cpp create mode 100644 resources/kolab/kolabremovetagtask.h create mode 100644 resources/kolab/kolabresource.cpp create mode 100644 resources/kolab/kolabresource.desktop create mode 100644 resources/kolab/kolabresource.h create mode 100644 resources/kolab/kolabresourcestate.cpp create mode 100644 resources/kolab/kolabresourcestate.h create mode 100644 resources/kolab/kolabretrievecollectionstask.cpp create mode 100644 resources/kolab/kolabretrievecollectionstask.h create mode 100644 resources/kolab/kolabretrievetagstask.cpp create mode 100644 resources/kolab/kolabretrievetagstask.h create mode 100644 resources/kolab/kolabsettings.cpp create mode 100644 resources/kolab/kolabsettings.h create mode 100644 resources/kolab/tagchangehelper.cpp create mode 100644 resources/kolab/tagchangehelper.h create mode 100644 resources/kolab/tests/CMakeLists.txt create mode 100644 resources/kolab/tests/imaptestbase.cpp create mode 100644 resources/kolab/tests/imaptestbase.h create mode 100644 resources/kolab/tests/testchangeitemstagstask.cpp create mode 100644 resources/kolab/tests/testretrievetagstask.cpp create mode 100644 resources/kolab/tests/unittestenv/config-mysql-db.xml create mode 100644 resources/kolab/tests/unittestenv/config-mysql-fs.xml create mode 100644 resources/kolab/tests/unittestenv/config-postgresql-db.xml create mode 100644 resources/kolab/tests/unittestenv/config-postgresql-fs.xml create mode 100644 resources/kolab/tests/unittestenv/config-sqlite-db.xml create mode 100644 resources/kolab/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc create mode 100644 resources/kolab/tests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc create mode 100755 resources/kolab/tests/unittestenv/kdehome/share/config/kdebugrc create mode 100644 resources/kolab/tests/unittestenv/kdehome/share/config/kdedrc create mode 100644 resources/kolab/tests/unittestenv/kdehome/testdata-res1.xml create mode 100644 resources/kolab/tests/unittestenv/xdgconfig-mysql.db/akonadi/akonadiserverrc create mode 100644 resources/kolab/tests/unittestenv/xdgconfig-mysql.fs/akonadi/akonadiserverrc create mode 100644 resources/kolab/tests/unittestenv/xdgconfig-postgresql.db/akonadi/akonadiserverrc create mode 100644 resources/kolab/tests/unittestenv/xdgconfig-postgresql.fs/akonadi/akonadiserverrc create mode 100644 resources/kolab/tests/unittestenv/xdgconfig-sqlite.db/akonadi/akonadiserverrc create mode 100644 resources/kolab/updatemessagejob.cpp create mode 100644 resources/kolab/updatemessagejob.h create mode 100644 resources/kolab/wizard/CMakeLists.txt create mode 100644 resources/kolab/wizard/Messages.sh create mode 100644 resources/kolab/wizard/kolabwizard.desktop create mode 100644 resources/kolab/wizard/kolabwizard.es create mode 100644 resources/kolab/wizard/kolabwizard.ui create mode 100644 resources/kolab/wizard/kolabwizard2.ui create mode 100644 resources/maildir/CMakeLists.txt create mode 100644 resources/maildir/Messages.sh create mode 100644 resources/maildir/autotests/CMakeLists.txt create mode 100644 resources/maildir/autotests/maildir-empty.xml create mode 100644 resources/maildir/autotests/maildir-step1.xml create mode 100644 resources/maildir/autotests/maildir-step2.xml create mode 100644 resources/maildir/autotests/maildir.js create mode 100644 resources/maildir/autotests/maildir.xml create mode 100644 resources/maildir/autotests/maildir/.root.directory/.child1.directory/grandchild/cur/1237726881.6570.rfoxg!2,S create mode 100644 resources/maildir/autotests/maildir/.root.directory/.child1.directory/grandchild/new/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/.child1.directory/grandchild/tmp/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/child1/cur/1237726858.6570.dtdn4!2,S create mode 100644 resources/maildir/autotests/maildir/.root.directory/child1/cur/1237726875.6570.R4KOW!2,S create mode 100644 resources/maildir/autotests/maildir/.root.directory/child1/new/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/child1/tmp/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/child2/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/child2/cur/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/child2/new/.keep create mode 100644 resources/maildir/autotests/maildir/.root.directory/child2/tmp/.keep create mode 100644 resources/maildir/autotests/maildir/root/cur/1237726845.6570.BejQg!2,S create mode 100644 resources/maildir/autotests/maildir/root/new/.keep create mode 100644 resources/maildir/autotests/maildir/root/tmp/.keep create mode 100644 resources/maildir/autotests/synctest.cpp create mode 100644 resources/maildir/autotests/synctest.h create mode 100644 resources/maildir/autotests/testmail.mbox create mode 100644 resources/maildir/autotests/unittestenv/config.xml create mode 100644 resources/maildir/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc create mode 100644 resources/maildir/autotests/unittestenv/kdehome/share/config/akonadi_maildir_resource_0rc create mode 100644 resources/maildir/autotests/unittestenv/kdehome/share/config/kdebugrc create mode 100644 resources/maildir/autotests/unittestenv/kdehome/share/config/kdedrc create mode 100644 resources/maildir/autotests/unittestenv/kdehome/share/config/kwalletrc create mode 100644 resources/maildir/autotests/unittestenv/kdehome/share/config/qttestrc create mode 100644 resources/maildir/autotests/unittestenv/kdehome/testdata.xml create mode 100644 resources/maildir/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc create mode 100644 resources/maildir/autotests/unittestenv/xdglocal/.keep create mode 100644 resources/maildir/configdialog.cpp create mode 100644 resources/maildir/configdialog.h create mode 100644 resources/maildir/libmaildir/CMakeLists.txt create mode 100644 resources/maildir/libmaildir/autotests/CMakeLists.txt create mode 100644 resources/maildir/libmaildir/autotests/testmaildir.cpp create mode 100644 resources/maildir/libmaildir/autotests/testmaildir.h create mode 100644 resources/maildir/libmaildir/keycache.cpp create mode 100644 resources/maildir/libmaildir/keycache.h create mode 100644 resources/maildir/libmaildir/maildir.cpp create mode 100644 resources/maildir/libmaildir/maildir.h create mode 100644 resources/maildir/maildirresource.cpp create mode 100644 resources/maildir/maildirresource.desktop create mode 100644 resources/maildir/maildirresource.h create mode 100644 resources/maildir/maildirresource.kcfg create mode 100644 resources/maildir/main.cpp create mode 100644 resources/maildir/retrieveitemsjob.cpp create mode 100644 resources/maildir/retrieveitemsjob.h create mode 100644 resources/maildir/settings.kcfgc create mode 100644 resources/maildir/settings.ui create mode 100644 resources/maildir/wizard/CMakeLists.txt create mode 100644 resources/maildir/wizard/Messages.sh create mode 100644 resources/maildir/wizard/maildirwizard.desktop create mode 100644 resources/maildir/wizard/maildirwizard.es create mode 100644 resources/maildir/wizard/maildirwizard.ui create mode 100644 resources/mbox/CMakeLists.txt create mode 100644 resources/mbox/Messages.sh create mode 100644 resources/mbox/autotests/CMakeLists.txt create mode 100644 resources/mbox/autotests/deleteitemsattributetest.cpp create mode 100644 resources/mbox/autotests/deleteitemsattributetest.h create mode 100644 resources/mbox/compactpage.cpp create mode 100644 resources/mbox/compactpage.h create mode 100644 resources/mbox/compactpage.ui create mode 100644 resources/mbox/deleteditemsattribute.cpp create mode 100644 resources/mbox/deleteditemsattribute.h create mode 100644 resources/mbox/lockfilepage.ui create mode 100644 resources/mbox/lockmethodpage.cpp create mode 100644 resources/mbox/lockmethodpage.h create mode 100644 resources/mbox/mboxresource.cpp create mode 100644 resources/mbox/mboxresource.desktop create mode 100644 resources/mbox/mboxresource.h create mode 100644 resources/mbox/mboxresource.kcfg create mode 100644 resources/mbox/settings.kcfgc create mode 100644 resources/mbox/wizard/CMakeLists.txt create mode 100644 resources/mbox/wizard/Messages.sh create mode 100644 resources/mbox/wizard/mailboxwizard.desktop create mode 100644 resources/mbox/wizard/mailboxwizard.es create mode 100644 resources/mbox/wizard/mailboxwizard.ui create mode 100644 resources/mixedmaildir/CMakeLists.txt create mode 100644 resources/mixedmaildir/Messages.sh create mode 100644 resources/mixedmaildir/autotests/CMakeLists.txt create mode 100644 resources/mixedmaildir/autotests/collectioncreatetest.cpp create mode 100644 resources/mixedmaildir/autotests/collectiondeletetest.cpp create mode 100644 resources/mixedmaildir/autotests/collectionfetchtest.cpp create mode 100644 resources/mixedmaildir/autotests/collectionmodifytest.cpp create mode 100644 resources/mixedmaildir/autotests/collectionmovetest.cpp create mode 100644 resources/mixedmaildir/autotests/data/.dimap.index create mode 100644 resources/mixedmaildir/autotests/data/.maildir-tagged.index create mode 100644 resources/mixedmaildir/autotests/data/.maildir.index create mode 100644 resources/mixedmaildir/autotests/data/.mbox-tagged.index create mode 100644 resources/mixedmaildir/autotests/data/.mbox-unpurged.index create mode 100644 resources/mixedmaildir/autotests/data/.mbox.index create mode 100644 resources/mixedmaildir/autotests/data/README create mode 100644 resources/mixedmaildir/autotests/data/dimap/cur/1279980064.4595.LUBVK create mode 100644 resources/mixedmaildir/autotests/data/dimap/cur/1279980064.4595.RTmAd_2,S create mode 100644 resources/mixedmaildir/autotests/data/dimap/cur/1279980064.4595.g8PCJ create mode 100644 resources/mixedmaildir/autotests/data/dimap/cur/1279980064.4595.qs6V9_2,S create mode 100644 resources/mixedmaildir/autotests/data/dimap/new/.keep create mode 100644 resources/mixedmaildir/autotests/data/dimap/tmp/.keep create mode 100644 resources/mixedmaildir/autotests/data/maildir-tagged/cur/1279982188.18722.6qZsA_2,S create mode 100644 resources/mixedmaildir/autotests/data/maildir-tagged/cur/1279982188.18722.Xdz3R_2,S create mode 100644 resources/mixedmaildir/autotests/data/maildir-tagged/cur/1279982188.18722.f0l49_2,S create mode 100644 resources/mixedmaildir/autotests/data/maildir-tagged/cur/1279982188.18722.kwx1b_2,S create mode 100644 resources/mixedmaildir/autotests/data/maildir-tagged/new/.keep create mode 100644 resources/mixedmaildir/autotests/data/maildir-tagged/tmp/.keep create mode 100644 resources/mixedmaildir/autotests/data/maildir/cur/1279979617.4595.bwXSm create mode 100644 resources/mixedmaildir/autotests/data/maildir/cur/1279979618.4595.CStza_2,S create mode 100644 resources/mixedmaildir/autotests/data/maildir/cur/1279979618.4595.DUl0I_2,S create mode 100644 resources/mixedmaildir/autotests/data/maildir/cur/1279979618.4595.pY5ny create mode 100644 resources/mixedmaildir/autotests/data/maildir/new/.keep create mode 100644 resources/mixedmaildir/autotests/data/maildir/tmp/.keep create mode 100644 resources/mixedmaildir/autotests/data/mbox create mode 100644 resources/mixedmaildir/autotests/data/mbox-tagged create mode 100644 resources/mixedmaildir/autotests/data/mbox-unpurged create mode 100644 resources/mixedmaildir/autotests/itemcreatetest.cpp create mode 100644 resources/mixedmaildir/autotests/itemdeletetest.cpp create mode 100644 resources/mixedmaildir/autotests/itemfetchtest.cpp create mode 100644 resources/mixedmaildir/autotests/itemmodifytest.cpp create mode 100644 resources/mixedmaildir/autotests/itemmovetest.cpp create mode 100644 resources/mixedmaildir/autotests/storecompacttest.cpp create mode 100644 resources/mixedmaildir/autotests/templatemethodstest.cpp create mode 100644 resources/mixedmaildir/autotests/testdata.qrc create mode 100644 resources/mixedmaildir/autotests/testdatatest.cpp create mode 100644 resources/mixedmaildir/autotests/testdatautil.cpp create mode 100644 resources/mixedmaildir/autotests/testdatautil.h create mode 100644 resources/mixedmaildir/compactchangehelper.cpp create mode 100644 resources/mixedmaildir/compactchangehelper.h create mode 100644 resources/mixedmaildir/configdialog.cpp create mode 100644 resources/mixedmaildir/configdialog.h create mode 100644 resources/mixedmaildir/kmindexreader/CMakeLists.txt create mode 100644 resources/mixedmaildir/kmindexreader/autotests/CMakeLists.txt create mode 100644 resources/mixedmaildir/kmindexreader/autotests/TestIdxReader_data.h create mode 100644 resources/mixedmaildir/kmindexreader/autotests/data/.keep create mode 100644 resources/mixedmaildir/kmindexreader/autotests/testidxreader.cpp create mode 100644 resources/mixedmaildir/kmindexreader/autotests/testidxreader.h create mode 100644 resources/mixedmaildir/kmindexreader/kmindexreader.cpp create mode 100644 resources/mixedmaildir/kmindexreader/kmindexreader.h create mode 100644 resources/mixedmaildir/mixedmaildir_debug.cpp create mode 100644 resources/mixedmaildir/mixedmaildir_debug.h create mode 100644 resources/mixedmaildir/mixedmaildirresource.cpp create mode 100644 resources/mixedmaildir/mixedmaildirresource.desktop create mode 100644 resources/mixedmaildir/mixedmaildirresource.h create mode 100644 resources/mixedmaildir/mixedmaildirresource.kcfg create mode 100644 resources/mixedmaildir/mixedmaildirresource_debug.cpp create mode 100644 resources/mixedmaildir/mixedmaildirresource_debug.h create mode 100644 resources/mixedmaildir/mixedmaildirstore.cpp create mode 100644 resources/mixedmaildir/mixedmaildirstore.h create mode 100644 resources/mixedmaildir/retrieveitemsjob.cpp create mode 100644 resources/mixedmaildir/retrieveitemsjob.h create mode 100644 resources/mixedmaildir/settings.kcfgc create mode 100644 resources/mixedmaildir/settings.ui create mode 100644 resources/openxchange/CMakeLists.txt create mode 100644 resources/openxchange/Messages.sh create mode 100644 resources/openxchange/configdialog.cpp create mode 100644 resources/openxchange/configdialog.h create mode 100644 resources/openxchange/configdialog.ui create mode 100644 resources/openxchange/icons/128-apps-ox.png create mode 100644 resources/openxchange/icons/16-apps-ox.png create mode 100644 resources/openxchange/icons/32-apps-ox.png create mode 100644 resources/openxchange/icons/48-apps-ox.png create mode 100644 resources/openxchange/icons/64-apps-ox.png create mode 100644 resources/openxchange/icons/CMakeLists.txt create mode 100644 resources/openxchange/openxchangeresource.cpp create mode 100644 resources/openxchange/openxchangeresource.desktop create mode 100644 resources/openxchange/openxchangeresource.h create mode 100644 resources/openxchange/openxchangeresource.kcfg create mode 100644 resources/openxchange/oxa/connectiontestjob.cpp create mode 100644 resources/openxchange/oxa/connectiontestjob.h create mode 100644 resources/openxchange/oxa/contactutils.cpp create mode 100644 resources/openxchange/oxa/contactutils.h create mode 100644 resources/openxchange/oxa/davmanager.cpp create mode 100644 resources/openxchange/oxa/davmanager.h create mode 100644 resources/openxchange/oxa/davutils.cpp create mode 100644 resources/openxchange/oxa/davutils.h create mode 100644 resources/openxchange/oxa/folder.cpp create mode 100644 resources/openxchange/oxa/folder.h create mode 100644 resources/openxchange/oxa/foldercreatejob.cpp create mode 100644 resources/openxchange/oxa/foldercreatejob.h create mode 100644 resources/openxchange/oxa/folderdeletejob.cpp create mode 100644 resources/openxchange/oxa/folderdeletejob.h create mode 100644 resources/openxchange/oxa/foldermodifyjob.cpp create mode 100644 resources/openxchange/oxa/foldermodifyjob.h create mode 100644 resources/openxchange/oxa/foldermovejob.cpp create mode 100644 resources/openxchange/oxa/foldermovejob.h create mode 100644 resources/openxchange/oxa/folderrequestjob.cpp create mode 100644 resources/openxchange/oxa/folderrequestjob.h create mode 100644 resources/openxchange/oxa/foldersrequestdeltajob.cpp create mode 100644 resources/openxchange/oxa/foldersrequestdeltajob.h create mode 100644 resources/openxchange/oxa/foldersrequestjob.cpp create mode 100644 resources/openxchange/oxa/foldersrequestjob.h create mode 100644 resources/openxchange/oxa/folderutils.cpp create mode 100644 resources/openxchange/oxa/folderutils.h create mode 100644 resources/openxchange/oxa/incidenceutils.cpp create mode 100644 resources/openxchange/oxa/incidenceutils.h create mode 100644 resources/openxchange/oxa/object.cpp create mode 100644 resources/openxchange/oxa/object.h create mode 100644 resources/openxchange/oxa/objectcreatejob.cpp create mode 100644 resources/openxchange/oxa/objectcreatejob.h create mode 100644 resources/openxchange/oxa/objectdeletejob.cpp create mode 100644 resources/openxchange/oxa/objectdeletejob.h create mode 100644 resources/openxchange/oxa/objectmodifyjob.cpp create mode 100644 resources/openxchange/oxa/objectmodifyjob.h create mode 100644 resources/openxchange/oxa/objectmovejob.cpp create mode 100644 resources/openxchange/oxa/objectmovejob.h create mode 100644 resources/openxchange/oxa/objectrequestjob.cpp create mode 100644 resources/openxchange/oxa/objectrequestjob.h create mode 100644 resources/openxchange/oxa/objectsrequestdeltajob.cpp create mode 100644 resources/openxchange/oxa/objectsrequestdeltajob.h create mode 100644 resources/openxchange/oxa/objectsrequestjob.cpp create mode 100644 resources/openxchange/oxa/objectsrequestjob.h create mode 100644 resources/openxchange/oxa/objectutils.cpp create mode 100644 resources/openxchange/oxa/objectutils.h create mode 100644 resources/openxchange/oxa/oxerrors.cpp create mode 100644 resources/openxchange/oxa/oxerrors.h create mode 100644 resources/openxchange/oxa/oxutils.cpp create mode 100644 resources/openxchange/oxa/oxutils.h create mode 100644 resources/openxchange/oxa/updateusersjob.cpp create mode 100644 resources/openxchange/oxa/updateusersjob.h create mode 100644 resources/openxchange/oxa/user.cpp create mode 100644 resources/openxchange/oxa/user.h create mode 100644 resources/openxchange/oxa/useridrequestjob.cpp create mode 100644 resources/openxchange/oxa/useridrequestjob.h create mode 100644 resources/openxchange/oxa/users.cpp create mode 100644 resources/openxchange/oxa/users.h create mode 100644 resources/openxchange/oxa/usersrequestjob.cpp create mode 100644 resources/openxchange/oxa/usersrequestjob.h create mode 100644 resources/openxchange/settings.kcfgc create mode 100644 resources/pop3/CMakeLists.txt create mode 100644 resources/pop3/Messages.sh create mode 100644 resources/pop3/TODO create mode 100644 resources/pop3/accountdialog.cpp create mode 100644 resources/pop3/accountdialog.h create mode 100644 resources/pop3/autotests/CMakeLists.txt create mode 100644 resources/pop3/autotests/fakeserver/fakeserver.cpp create mode 100644 resources/pop3/autotests/fakeserver/fakeserver.h create mode 100644 resources/pop3/autotests/pop3test.cpp create mode 100644 resources/pop3/autotests/pop3test.h create mode 100644 resources/pop3/autotests/unittestenv/config.xml create mode 100644 resources/pop3/autotests/unittestenv/kdehome/share/apps/.keep create mode 100644 resources/pop3/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc create mode 100755 resources/pop3/autotests/unittestenv/kdehome/share/config/kdebugrc create mode 100644 resources/pop3/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc create mode 100644 resources/pop3/autotests/unittestenv/xdglocal/.keep create mode 100644 resources/pop3/jobs.cpp create mode 100644 resources/pop3/jobs.h create mode 100644 resources/pop3/metatype.h create mode 100644 resources/pop3/pop3resource.cpp create mode 100644 resources/pop3/pop3resource.desktop create mode 100644 resources/pop3/pop3resource.h create mode 100644 resources/pop3/popsettings.ui create mode 100644 resources/pop3/settings.cpp create mode 100644 resources/pop3/settings.h create mode 100644 resources/pop3/settings.kcfg create mode 100644 resources/pop3/settingsbase.kcfgc create mode 100644 resources/pop3/wizard/CMakeLists.txt create mode 100644 resources/pop3/wizard/Messages.sh create mode 100644 resources/pop3/wizard/pop3wizard.desktop create mode 100644 resources/pop3/wizard/pop3wizard.es create mode 100644 resources/pop3/wizard/pop3wizard.ui create mode 100644 resources/shared/CMakeLists.txt create mode 100644 resources/shared/filestore/CMakeLists.txt create mode 100644 resources/shared/filestore/Messages.sh create mode 100644 resources/shared/filestore/abstractlocalstore.cpp create mode 100644 resources/shared/filestore/abstractlocalstore.h create mode 100644 resources/shared/filestore/autotests/CMakeLists.txt create mode 100644 resources/shared/filestore/autotests/abstractlocalstoretest.cpp create mode 100644 resources/shared/filestore/collectioncreatejob.cpp create mode 100644 resources/shared/filestore/collectioncreatejob.h create mode 100644 resources/shared/filestore/collectiondeletejob.cpp create mode 100644 resources/shared/filestore/collectiondeletejob.h create mode 100644 resources/shared/filestore/collectionfetchjob.cpp create mode 100644 resources/shared/filestore/collectionfetchjob.h create mode 100644 resources/shared/filestore/collectionmodifyjob.cpp create mode 100644 resources/shared/filestore/collectionmodifyjob.h create mode 100644 resources/shared/filestore/collectionmovejob.cpp create mode 100644 resources/shared/filestore/collectionmovejob.h create mode 100644 resources/shared/filestore/entitycompactchangeattribute.cpp create mode 100644 resources/shared/filestore/entitycompactchangeattribute.h create mode 100644 resources/shared/filestore/itemcreatejob.cpp create mode 100644 resources/shared/filestore/itemcreatejob.h create mode 100644 resources/shared/filestore/itemdeletejob.cpp create mode 100644 resources/shared/filestore/itemdeletejob.h create mode 100644 resources/shared/filestore/itemfetchjob.cpp create mode 100644 resources/shared/filestore/itemfetchjob.h create mode 100644 resources/shared/filestore/itemmodifyjob.cpp create mode 100644 resources/shared/filestore/itemmodifyjob.h create mode 100644 resources/shared/filestore/itemmovejob.cpp create mode 100644 resources/shared/filestore/itemmovejob.h create mode 100644 resources/shared/filestore/job.cpp create mode 100644 resources/shared/filestore/job.h create mode 100644 resources/shared/filestore/session.cpp create mode 100644 resources/shared/filestore/session_p.h create mode 100644 resources/shared/filestore/sessionimpls.cpp create mode 100644 resources/shared/filestore/sessionimpls_p.h create mode 100644 resources/shared/filestore/storecompactjob.cpp create mode 100644 resources/shared/filestore/storecompactjob.h create mode 100644 resources/shared/filestore/storeinterface.h create mode 100644 resources/shared/singlefileresource/CMakeLists.txt create mode 100644 resources/shared/singlefileresource/Messages.sh create mode 100644 resources/shared/singlefileresource/autotests/CMakeLists.txt create mode 100644 resources/shared/singlefileresource/autotests/collectionannotationattributetest.cpp create mode 100644 resources/shared/singlefileresource/autotests/imapaclattributetest.cpp create mode 100644 resources/shared/singlefileresource/collectionannotationsattribute.cpp create mode 100644 resources/shared/singlefileresource/collectionannotationsattribute.h create mode 100644 resources/shared/singlefileresource/collectionflagsattribute.cpp create mode 100644 resources/shared/singlefileresource/collectionflagsattribute.h create mode 100644 resources/shared/singlefileresource/createandsettagsjob.cpp create mode 100644 resources/shared/singlefileresource/createandsettagsjob.h create mode 100644 resources/shared/singlefileresource/getcredentialsjob.cpp create mode 100644 resources/shared/singlefileresource/getcredentialsjob.h create mode 100644 resources/shared/singlefileresource/imapaclattribute.cpp create mode 100644 resources/shared/singlefileresource/imapaclattribute.h create mode 100644 resources/shared/singlefileresource/imapquotaattribute.cpp create mode 100644 resources/shared/singlefileresource/imapquotaattribute.h create mode 100644 resources/shared/singlefileresource/settingsdialog.ui create mode 100644 resources/shared/singlefileresource/singlefileresource.h create mode 100644 resources/shared/singlefileresource/singlefileresourcebase.cpp create mode 100644 resources/shared/singlefileresource/singlefileresourcebase.h create mode 100644 resources/shared/singlefileresource/singlefileresourceconfigdialog.h create mode 100644 resources/shared/singlefileresource/singlefileresourceconfigdialog.ui create mode 100644 resources/shared/singlefileresource/singlefileresourceconfigdialog_desktop.ui create mode 100644 resources/shared/singlefileresource/singlefileresourceconfigdialogbase.cpp create mode 100644 resources/shared/singlefileresource/singlefileresourceconfigdialogbase.h create mode 100644 resources/vcard/CMakeLists.txt create mode 100644 resources/vcard/Messages.sh create mode 100644 resources/vcard/autotests/CMakeLists.txt create mode 100644 resources/vcard/autotests/vcardtest-readonly.js create mode 100644 resources/vcard/autotests/vcardtest-readonly.xml create mode 100644 resources/vcard/autotests/vcardtest.js create mode 100644 resources/vcard/autotests/vcardtest.vcf create mode 100644 resources/vcard/autotests/vcardtest.xml create mode 100644 resources/vcard/settings.kcfgc create mode 100644 resources/vcard/vcardresource.cpp create mode 100644 resources/vcard/vcardresource.desktop create mode 100644 resources/vcard/vcardresource.h create mode 100644 resources/vcard/vcardresource.kcfg create mode 100644 resources/vcard/wizard/CMakeLists.txt create mode 100644 resources/vcard/wizard/Messages.sh create mode 100644 resources/vcard/wizard/vcardwizard.desktop create mode 100644 resources/vcard/wizard/vcardwizard.es.cmake create mode 100644 resources/vcard/wizard/vcardwizard.ui create mode 100644 resources/vcarddir/CMakeLists.txt create mode 100644 resources/vcarddir/Messages.sh create mode 100644 resources/vcarddir/dirsettingsdialog.cpp create mode 100644 resources/vcarddir/dirsettingsdialog.h create mode 100644 resources/vcarddir/settings.kcfgc create mode 100644 resources/vcarddir/vcarddirresource.cpp create mode 100644 resources/vcarddir/vcarddirresource.desktop create mode 100644 resources/vcarddir/vcarddirresource.h create mode 100644 resources/vcarddir/vcarddirresource.kcfg create mode 100644 resources/vcarddir/wizard/CMakeLists.txt create mode 100644 resources/vcarddir/wizard/Messages.sh create mode 100644 resources/vcarddir/wizard/vcarddirwizard.desktop create mode 100644 resources/vcarddir/wizard/vcarddirwizard.es.cmake create mode 100644 resources/vcarddir/wizard/vcarddirwizard.ui diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 00000000..20d53ee2 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "phabricator.uri" : "https://phabricator.kde.org/project/profile/34/", + "history.immutable" : true +} diff --git a/.emacs-dirvars b/.emacs-dirvars new file mode 100644 index 00000000..d8419e55 --- /dev/null +++ b/.emacs-dirvars @@ -0,0 +1,14 @@ +;; -*- emacs-lisp -*- +;; +;; This file is processed by the dirvars emacs package. Each variable +;; setting below is performed when this dirvars file is loaded. +;; +c-block-comment-prefix: "" +indent-tabs-mode: nil +tab-width: 8 +c-basic-offset: 2 +evaluate: (c-set-offset 'innamespace '0) +evaluate: (c-set-offset 'access-label '0) +;evaluate: (c-set-offset 'topmost-intro '+) +evaluate: (c-set-offset 'inline-open '+) +kdab-qt-version: 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cd88be43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Ignore the following files +*~ +*.[oa] +*.kdev4 +.kdev_include_paths +.swp.* +.*.swp +*.kate-swp +Makefile +*.moc +*.moc.cpp +autom4te.cache +*.diff +svn-commit.tmp +svn-commit.*.tmp +*.kdevelop.pcs +*.kdev4 +svnmerge-commit-message.txt +avail +Doxyfile +*.user +/build/ +CMakeLists.txt.user* + diff --git a/.krazy b/.krazy new file mode 100644 index 00000000..945ea5ad --- /dev/null +++ b/.krazy @@ -0,0 +1,6 @@ +SKIP /libkdepim-copy/ +EXTRA defines,kdebug,null,qenums,tipsandthis +STRICT super + +#kresources is pretty much dead +IGNORESUBS kresources diff --git a/.reviewboardrc b/.reviewboardrc new file mode 100644 index 00000000..4d23bcc7 --- /dev/null +++ b/.reviewboardrc @@ -0,0 +1,5 @@ +REVIEWBOARD_URL = "https://git.reviewboard.kde.org" +TARGET_GROUPS = "kdepim" +REPOSITORY = "kdepim-runtime" +TARGET_PEOPLE = "mlaurent" +BRANCH = "master" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..9280777b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,154 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(kdepim-runtime) + +############### KDEPIM-Runtime version ################ +# KDEPIM_RUNTIME_VERSION +# Version scheme: "x.y.z build". +# +# x is the version number. +# y is the major release number. +# z is the minor release number. +# +# "x.y.z" follow the kdelibs version kdepim is released with. +# +# If "z" is 0, it the version is "x.y" +# +# KDEPIM_RUNTIME_DEV_VERSION +# is empty for final versions. For development versions "build" is +# something like "pre", "alpha1", "alpha2", "beta1", "beta2", "rc1", "rc2". +# +# Examples in chronological order: +# +# 3.0 +# 3.0.1 +# 3.1 alpha1 +# 3.1 beta1 +# 3.1 beta2 +# 3.1 rc1 +# 3.1 +# 3.1.1 +# 3.2 pre +# 3.2 alpha1 + +if(NOT DEFINED KDEPIM_RUNTIME_DEV_VERSION) + set(KDEPIM_RUNTIME_DEV_VERSION "") +endif() + +set(KDEPIM_RUNTIME_VERSION_NUMBER "5.2.2") +set(KDEPIM_RUNTIME_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}${KDEPIM_RUNTIME_DEV_VERSION}") + +configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) + +find_package(ECM 5.19 REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${kdepim-runtime_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) + +include(ECMPackageConfigHelpers) +include(ECMSetupVersion) +include(FeatureSummary) +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(ECMInstallIcons) +include(ECMQtDeclareLoggingCategory) + +set(KF5_VERSION "5.19") +set(QT_REQUIRED_VERSION "5.5.0") + +set(KDEPIMLIBS_LIB_VERSION "5.2.0") + +set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}") +set(KDEPIMRUNTIME_LIB_SOVERSION "5") +set(AKONADI_VERSION "5.2.0") + +set(KCONTACTS_LIB_VERSION "5.2.0") +set(KCALENDARCORE_LIB_VERSION "5.2.0") +set(IDENTITYMANAGEMENT_LIB_VERSION "5.2.0") +set(KMAILTRANSPORT_LIB_VERSION "5.2.0") +set(CALENDARUTILS_LIB_VERSION "5.2.0") +set(KIMAP_LIB_VERSION "5.2.0") +set(KMBOX_LIB_VERSION "5.2.0") +set(AKONADICALENDAR_LIB_VERSION "5.2.0") +set(SYNDICATION_LIB_VERSION "5.2.0") +set(KONTACTINTERFACE_LIB_VERSION "5.2.0") +set(AKONADIKALARM_LIB_VERSION "5.2.0") +set(KMIME_LIB_VERSION "5.2.0") +set(XMLRPCCLIENT_LIB_VERSION "5.2.0") +set(KCONTACTS_LIB_VERSION "5.2.0") +set(AKONADIMIME_LIB_VERSION "5.2.0") +set(AKONADICONTACT_LIB_VERSION "5.2.0") +set(AKONADINOTE_LIB_VERSION "5.2.0") +set(AKONADISOCIALUTIL_LIB_VERSION "5.2.0") +set(KPIMTEXTEDIT_LIB_VERSION "5.2.0") + +set( SHARED_MIME_INFO_MINIMUM_VERSION "0.40" ) +find_package( SharedMimeInfo REQUIRED ) + + +find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Network Widgets Test XmlPatterns DBus) + +# QT5 package +find_package(Qt5WebKitWidgets ${QT_REQUIRED_VERSION} REQUIRED NO_MODULE) + +find_package(Qt5 OPTIONAL_COMPONENTS TextToSpeech) +if (NOT Qt5TextToSpeech_FOUND) + message(STATUS "Qt5TextToSpeech not found, speech feature will be disabled") +else() + add_definitions(-DHAVE_SPEECH) +endif() + + +# KF5 package +find_package(KF5KDELibs4Support ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5NotifyConfig ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5KIO ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5Kross ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) +find_packagE(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) # for KPluralHandlingSpinBox + +# KdepimLibs package +find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) +find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5MailTransport ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiContact ${AKONADICONTACT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5Contacts ${KCONTACTS_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AlarmCalendar ${AKONADIKALARM_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5CalendarUtils ${CALENDARUTILS_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5Mbox ${KMBOX_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5PimTextEdit ${KPIMTEXTEDIT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5IMAP ${KIMAP_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5Syndication ${SYNDICATION_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiNotes ${AKONADINOTE_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiSocialUtils ${AKONADISOCIALUTIL_LIB_VERSION} CONFIG REQUIRED) + +option(KDEPIM_RUN_ISOLATED_TESTS "Run the isolated tests." FALSE) + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +add_subdirectory(resources) +add_subdirectory(agents) +add_subdirectory(plugins) +add_subdirectory(defaultsetup) +add_subdirectory(kioslave) +add_subdirectory(migration) + + +## install the MIME type spec file for KDEPIM specific MIME types +install(FILES kdepim-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR}) +update_xdg_mimetypes(${KDE_INSTALL_MIMEDIR}) + + +install( FILES kdepim-runtime.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) + +feature_summary(WHAT ALL + INCLUDE_QUIET_PACKAGES + FATAL_ON_MISSING_REQUIRED_PACKAGES +) diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..5185fd3f --- /dev/null +++ b/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 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 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..2d2d780e --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 00000000..e8bc4ba7 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,13 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "kdepim-runtime") +set(CTEST_NIGHTLY_START_TIME "20:00:00 CET") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=kdepim-runtime") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/CTestCustom.cmake b/CTestCustom.cmake new file mode 100644 index 00000000..33852484 --- /dev/null +++ b/CTestCustom.cmake @@ -0,0 +1,31 @@ +# This file contains all the specific settings that will be used +# when running 'make Experimental' + +# Change the maximum warnings that will be displayed +# on the report page (default 50) +set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS 2000) + +# Warnings that will be ignored +set(CTEST_CUSTOM_WARNING_EXCEPTION + + "too big, try a different debug format" + "qlist.h.*increases required alignment of target type" + "qmap.h.*increases required alignment of target type" + "qhash.h.*increases required alignment of target type" + ".*warning.* is deprecated" + ) + +# Errors that will be ignored +set(CTEST_CUSTOM_ERROR_EXCEPTION + + "ICECC" + "Segmentation fault" + "Error 1 (ignored)" + "invoking macro kDebug argument 1" + "GConf Error" + "Client failed to connect to the D-BUS daemon" + "Failed to connect to socket" + ) + +# No coverage for these files +set(CTEST_CUSTOM_COVERAGE_EXCLUDE ".moc$" "moc_" "ui_" "ontologies") diff --git a/MIGRATE-CONFIG-APPS b/MIGRATE-CONFIG-APPS new file mode 100644 index 00000000..805013d1 --- /dev/null +++ b/MIGRATE-CONFIG-APPS @@ -0,0 +1,3 @@ +Migrate configrc done: +- agent/newmailnotifieragent: akonadi_newmailnotifier_agentrc, akonadi_newmailnotifier_agent.notifyrc +- agent/maildispatcher: maildispatcheragentrc, akonadi_maildispatcher_agent.notifyrc diff --git a/Mainpage.dox b/Mainpage.dox new file mode 100644 index 00000000..2b1bd767 --- /dev/null +++ b/Mainpage.dox @@ -0,0 +1,105 @@ +/** +\mainpage Akonadi, the KDE PIM storage framework + +These pages are a combination of design and api documentation. If you are +looking for general information go to the Overview section. +For detailed information and/or api-dox on any of the packages go to the +package main page, either from the menu on the left or from the Building +blocks section below. + + +\section akonadi_overview Overview + +- Design and Concepts + - \ref akonadi_design_communication +- \ref akonadi_usage +- Website +- Wiki +- \ref akonadi_todos + +\section akonadi_building_blocks Building Blocks + +- Domain-specific libraries: + - Contacts (KABC) + - MIME Messages (KMime) + - Events, todo items and journal entries (KCal) + - Core library (libakonadi) + +\authors Tobias König , Volker Krause + +\licenses \lgpl +*/ + + +/** +\page akonadi_design_communication Communication Schemas + +\section akonadi_design_communication_search Search + +The sequence diagrams below show how general communication is done: + +
+ \image html akonadi_client_search_small.png "Akonadi Communication Schema" +
+ + +\image latex akonadi_client_search.eps "Akonadi Communication Schema" height=5cm + +The item search request is probably the call which is used most often +by the clients (components or applications). This call enables the client +to search for a list of items of a given mime type which match a +given search criterion. + +In this case the client will contact the SearchProvider responsible for +the mime type, in order to retrieve the list of matching UIDs. The SearchProvider +already has a list of all available items of this mime type in its memory, so it +can search fast and use indices for optimization. + +To communicate mime type constraints in FETCH and LIST and their responses the +IMAP flags mechanism is used. Unknown flags should be ignored by non-Akonadi +IMAP clients, which keeps compatibility with mutt and regular KMail. + +Examples: +- List +\verbatim +0x8053c68 8 LIST "" "res1/foo/%" +0x8053c68 * LIST (\MimeTypes[text/calendar,directory/inode]) "/" "res1/foo/bar" +\endverbatim +- Fetch +\verbatim +0x8056310 7 UID FETCH 22 (UID RFC822.SIZE FLAGS BODY.PEEK[]) +0x8056310 * 1 FETCH (FLAGS (\Seen \MimeTypes[message/rfc822]) RFC822 {2450} From: Till Adam To: ... +\endverbatim + +\section akonadi_design_communication_agent Agent Handling +
+ \image html akonadi_agent_handling_small.png "Akonadi Agent Handling" +
+ + +\image latex akonadi_agent_handling.eps "Akonadi Agent Handling" height=4cm +*/ + +/** +\page akonadi_overview_uml Akonadi Overview + +This overview does not show a complete class or collaboration diagram, it is rather meant to show the +big picture. + +
+ \image html akonadi_overview_uml_small.png "Akonadi Overview" +
+ + + +*/ + + +// DOXYGEN_NAME=kdepim-runtime +// DOXYGEN_ENABLE=YES diff --git a/README b/README new file mode 100644 index 00000000..646778a3 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +The KDE project kdepim-runtime contains the Akonadi resources from kdepim which can be used without the applications in kdepim. + +Here is the online code-repostitory of kdepim-runtime: http://quickgit.kde.org/?p=kdepim-runtime.git&a=summary diff --git a/agents/.krazy b/agents/.krazy new file mode 100644 index 00000000..4152f9b2 --- /dev/null +++ b/agents/.krazy @@ -0,0 +1 @@ +SKIP /ontologies/ diff --git a/agents/CMakeLists.txt b/agents/CMakeLists.txt new file mode 100644 index 00000000..a4586ce8 --- /dev/null +++ b/agents/CMakeLists.txt @@ -0,0 +1,10 @@ +project(agents) + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +add_subdirectory( maildispatcher ) +add_subdirectory( newmailnotifier ) +add_subdirectory( migration ) +add_subdirectory( invitations ) + diff --git a/agents/Info.plist.template b/agents/Info.plist.template new file mode 100644 index 00000000..c39ddb95 --- /dev/null +++ b/agents/Info.plist.template @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + LSUIElement + 1 + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + + diff --git a/agents/Mainpage.dox b/agents/Mainpage.dox new file mode 100644 index 00000000..c99c6ac3 --- /dev/null +++ b/agents/Mainpage.dox @@ -0,0 +1,2 @@ +// DOXYGEN_NAME=Akonadi Agents +// DOXYGEN_ENABLE=YES diff --git a/agents/cmake/FindXsltproc.cmake b/agents/cmake/FindXsltproc.cmake new file mode 100644 index 00000000..45b46cfc --- /dev/null +++ b/agents/cmake/FindXsltproc.cmake @@ -0,0 +1,32 @@ +# Find xsltproc executable and provide a macro to generate D-Bus interfaces. +# +# The following variables are defined : +# XSLTPROC_EXECUTABLE - path to the xsltproc executable +# Xsltproc_FOUND - true if the program was found +# +find_program(XSLTPROC_EXECUTABLE xsltproc DOC "Path to the xsltproc executable") +mark_as_advanced(XSLTPROC_EXECUTABLE) + +if(XSLTPROC_EXECUTABLE) + set(Xsltproc_FOUND TRUE) + + # We depend on kdepimlibs, make sure it's found + if(NOT DEFINED KF5Akonadi_DATA_DIR) + find_package(KF5Akonadi REQUIRED) + endif() + + + # Macro to generate a D-Bus interface description from a KConfigXT file + macro(kcfg_generate_dbus_interface _kcfg _name) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + COMMAND ${XSLTPROC_EXECUTABLE} --stringparam interfaceName ${_name} + ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + > ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + DEPENDS ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + ) + endmacro() +endif() + diff --git a/agents/invitations/CMakeLists.txt b/agents/invitations/CMakeLists.txt new file mode 100644 index 00000000..be62d37d --- /dev/null +++ b/agents/invitations/CMakeLists.txt @@ -0,0 +1,28 @@ + + +set( invitationsagent_SRCS + invitationsagent.cpp + incidenceattribute.cpp +) + +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_invitations_agent\") + +add_executable(akonadi_invitations_agent ${invitationsagent_SRCS}) + +target_link_libraries(akonadi_invitations_agent + KF5::AkonadiCore + KF5::AkonadiMime + KF5::Mime + KF5::CalendarCore + KF5::AkonadiAgentBase +) + +if( APPLE ) + set_target_properties(akonadi_invitations_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_invitations_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.invitationsagent") + set_target_properties(akonadi_invitations_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Invitations Calendar") +endif () + + +install(TARGETS akonadi_invitations_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES invitationsagent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents") diff --git a/agents/invitations/Messages.sh b/agents/invitations/Messages.sh new file mode 100755 index 00000000..6f0436ae --- /dev/null +++ b/agents/invitations/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh + +$XGETTEXT *.cpp -o $podir/akonadi_invitations_agent.pot diff --git a/agents/invitations/incidenceattribute.cpp b/agents/invitations/incidenceattribute.cpp new file mode 100644 index 00000000..d3c277e7 --- /dev/null +++ b/agents/invitations/incidenceattribute.cpp @@ -0,0 +1,93 @@ +/* + Copyright (c) 2009 Sebastian Sauer + + 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 "incidenceattribute.h" + +#include +#include + +using namespace Akonadi; + +class IncidenceAttribute::Private +{ +public: + QString status; + Akonadi::Item::Id referenceId; + + explicit Private() : referenceId(-1) {} +}; + +IncidenceAttribute::IncidenceAttribute() + : Attribute(), d(new Private) +{ +} + +IncidenceAttribute::~IncidenceAttribute() +{ + delete d; +} + +QByteArray IncidenceAttribute::type() const +{ + static const QByteArray sType("incidence"); + return sType; +} + +Attribute *IncidenceAttribute::clone() const +{ + IncidenceAttribute *other = new IncidenceAttribute; + return other; +} + +QByteArray IncidenceAttribute::serialized() const +{ + QString data; + QTextStream out(&data); + out << d->status; + out << d->referenceId; + return data.toUtf8(); +} + +void IncidenceAttribute::deserialize(const QByteArray &data) +{ + QString s(QString::fromUtf8(data)); + QTextStream in(&s); + in >> d->status; + in >> d->referenceId; +} + +QString IncidenceAttribute::status() const +{ + return d->status; +} + +void IncidenceAttribute::setStatus(const QString &newstatus) const +{ + d->status = newstatus; +} + +Akonadi::Item::Id IncidenceAttribute::reference() const +{ + return d->referenceId; +} + +void IncidenceAttribute::setReference(Akonadi::Item::Id id) +{ + d->referenceId = id; +} diff --git a/agents/invitations/incidenceattribute.h b/agents/invitations/incidenceattribute.h new file mode 100644 index 00000000..21ccc0de --- /dev/null +++ b/agents/invitations/incidenceattribute.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2009 Sebastian Sauer + + 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 INCIDENCEATTRIBUTE_H +#define INCIDENCEATTRIBUTE_H + +#include +#include + +namespace Akonadi +{ + +class IncidenceAttribute : public Akonadi::Attribute +{ +public: + explicit IncidenceAttribute(); + ~IncidenceAttribute(); + + QByteArray type() const Q_DECL_OVERRIDE; + Attribute *clone() const Q_DECL_OVERRIDE; + + QByteArray serialized() const Q_DECL_OVERRIDE; + void deserialize(const QByteArray &data) Q_DECL_OVERRIDE; + + /** + * The status the invitation is in. + * + * One of; + * "new", "accepted", "tentative", "counter", "cancel", "reply", "delegated" + */ + QString status() const; + void setStatus(const QString &newstatus) const; + + /** + * The referenced item. This is used e.g. in the invitationagent to + * let users know where the original mail message is. + */ + Akonadi::Item::Id reference() const; + void setReference(Akonadi::Item::Id id); + +private: + class Private; + Private *const d; +}; + +} + +#endif diff --git a/agents/invitations/invitationsagent.cpp b/agents/invitations/invitationsagent.cpp new file mode 100644 index 00000000..9f0d4783 --- /dev/null +++ b/agents/invitations/invitationsagent.cpp @@ -0,0 +1,569 @@ +/* + Copyright 2009 Sebastian Sauer + + 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 "invitationsagent.h" + +#include "incidenceattribute.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Akonadi; + +class InvitationsCollectionRequestJob : public SpecialCollectionsRequestJob +{ +public: + InvitationsCollectionRequestJob(SpecialCollections *collection, InvitationsAgent *agent) + : SpecialCollectionsRequestJob(collection, agent) + { + setDefaultResourceType(QStringLiteral("akonadi_ical_resource")); + + QVariantMap options; + options.insert(QStringLiteral("Path"), QString(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("akonadi_invitations"))); + options.insert(QStringLiteral("Name"), i18n("Invitations")); + setDefaultResourceOptions(options); + + QMap displayNameMap; + displayNameMap.insert("invitations", i18n("Invitations")); + setTypes(displayNameMap.keys()); + setNameForTypeMap(displayNameMap); + + QMap iconNameMap; + iconNameMap.insert("invitations", QStringLiteral("folder")); + setIconForTypeMap(iconNameMap); + } + + virtual ~InvitationsCollectionRequestJob() + { + } +}; + +class InvitationsCollection : public SpecialCollections +{ +public: + + class Settings : public KCoreConfigSkeleton + { + public: + Settings() + { + setCurrentGroup(QStringLiteral("Invitations")); + addItemString(QStringLiteral("DefaultResourceId"), m_id, QString()); + } + + virtual ~Settings() + { + } + + private: + QString m_id; + }; + + InvitationsCollection(InvitationsAgent *agent) + : Akonadi::SpecialCollections(new Settings), m_agent(agent), sInvitationsType("invitations") + { + } + + virtual ~InvitationsCollection() + { + } + + void clear() + { + m_collection = Collection(); + } + + bool hasDefaultCollection() const + { + return SpecialCollections::hasDefaultCollection(sInvitationsType); + } + + Collection defaultCollection() const + { + if (!m_collection.isValid()) { + m_collection = SpecialCollections::defaultCollection(sInvitationsType); + } + + return m_collection; + } + + void registerDefaultCollection() + { + defaultCollection(); + registerCollection(sInvitationsType, m_collection); + } + + SpecialCollectionsRequestJob *reguestJob() const + { + InvitationsCollectionRequestJob *job = new InvitationsCollectionRequestJob(const_cast(this), + m_agent); + job->requestDefaultCollection(sInvitationsType); + + return job; + } + +private: + InvitationsAgent *m_agent; + const QByteArray sInvitationsType; + mutable Collection m_collection; +}; + +InvitationsAgentItem::InvitationsAgentItem(InvitationsAgent *agent, const Item &originalItem) + : QObject(agent), m_agent(agent), m_originalItem(originalItem) +{ +} + +InvitationsAgentItem::~InvitationsAgentItem() +{ +} + +void InvitationsAgentItem::add(const Item &item) +{ + qDebug(); + const Collection collection = m_agent->collection(); + Q_ASSERT(collection.isValid()); + + ItemCreateJob *job = new ItemCreateJob(item, collection, this); + connect(job, &InvitationsCollectionRequestJob::result, this, &InvitationsAgentItem::createItemResult); + + m_jobs << job; + + job->start(); +} + +void InvitationsAgentItem::createItemResult(KJob *job) +{ + ItemCreateJob *createJob = qobject_cast(job); + m_jobs.removeAll(createJob); + if (createJob->error()) { + qWarning() << "Failed to create new Item in invitations collection." << createJob->errorText(); + return; + } + + if (!m_jobs.isEmpty()) { + return; + } + + ItemFetchJob *fetchJob = new ItemFetchJob(m_originalItem, this); + connect(fetchJob, &ItemFetchJob::result, this, &InvitationsAgentItem::fetchItemDone); + fetchJob->start(); +} + +void InvitationsAgentItem::fetchItemDone(KJob *job) +{ + if (job->error()) { + qWarning() << "Failed to fetch Item in invitations collection." << job->errorText(); + return; + } + + ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob->items().count() == 1); + + Item modifiedItem = fetchJob->items().at(0); + Q_ASSERT(modifiedItem.isValid()); + + modifiedItem.setFlag(Akonadi::MessageFlags::HasInvitation); + ItemModifyJob *modifyJob = new ItemModifyJob(modifiedItem, this); + connect(modifyJob, &ItemModifyJob::result, this, &InvitationsAgentItem::modifyItemDone); + modifyJob->start(); +} + +void InvitationsAgentItem::modifyItemDone(KJob *job) +{ + if (job->error()) { + qWarning() << "Failed to modify Item in invitations collection." << job->errorText(); + return; + } + + //ItemModifyJob *mj = qobject_cast( job ); + //qDebug()<<"Job successful done."; +} + +InvitationsAgent::InvitationsAgent(const QString &id) + : AgentBase(id), AgentBase::ObserverV2() + , m_invitationsCollection(new InvitationsCollection(this)) +{ + qDebug(); + + changeRecorder()->setChangeRecordingEnabled(false); // behave like Monitor + changeRecorder()->itemFetchScope().fetchFullPayload(); + changeRecorder()->setMimeTypeMonitored(QStringLiteral("message/rfc822"), true); + //changeRecorder()->setCollectionMonitored( Collection::root(), true ); + + connect(this, &InvitationsAgent::reloadConfiguration, this, &InvitationsAgent::initStart); + QTimer::singleShot(0, this, &InvitationsAgent::initStart); +} + +InvitationsAgent::~InvitationsAgent() +{ + delete m_invitationsCollection; +} + +void InvitationsAgent::initStart() +{ + qDebug(); + + m_invitationsCollection->clear(); + if (m_invitationsCollection->hasDefaultCollection()) { + initDone(); + } else { + SpecialCollectionsRequestJob *job = m_invitationsCollection->reguestJob(); + connect(job, &InvitationsCollectionRequestJob::result, this, &InvitationsAgent::initDone); + job->start(); + } + + /* + KConfig config( "akonadi_invitations_agent" ); + KConfigGroup group = config.group( "General" ); + m_resourceId = group.readEntry( "DefaultCalendarAgent", "default_ical_resource" ); + newAgentCreated = false; + m_invitations = Akonadi::Collection(); + AgentInstance resource = AgentManager::self()->instance( m_resourceId ); + if ( resource.isValid() ) { + Q_EMIT status( AgentBase::Running, i18n( "Reading..." ) ); + QMetaObject::invokeMethod( this, "createAgentResult", Qt::QueuedConnection ); + } else { + Q_EMIT status( AgentBase::Running, i18n( "Creating..." ) ); + AgentType type = AgentManager::self()->type( QLatin1String( "akonadi_ical_resource" ) ); + AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type, this ); + connect(job, &InvitationsCollectionRequestJob::result, this, &InvitationsAgent::createAgentResult); + job->start(); + } + */ +} + +void InvitationsAgent::initDone(KJob *job) +{ + if (job) { + if (job->error()) { + qWarning() << "Failed to request default collection:" << job->errorText(); + return; + } + + m_invitationsCollection->registerDefaultCollection(); + } + + Q_ASSERT(m_invitationsCollection->defaultCollection().isValid()); + Q_EMIT status(AgentBase::Idle, i18n("Ready to dispatch invitations")); +} + +Collection InvitationsAgent::collection() +{ + return m_invitationsCollection->defaultCollection(); +} + +#if 0 +KIdentityManagement::IdentityManager *InvitationsAgent::identityManager() +{ + if (!m_IdentityManager) { + m_IdentityManager = new KIdentityManagement::IdentityManager(true /* readonly */, this); + } + return m_IdentityManager; +} +#endif + +void InvitationsAgent::configure(WId windowId) +{ + qDebug() << windowId; + /* + QWidget *parent = QWidget::find( windowId ); + QDialog *dialog = new QDialog( parent ); + QVBoxLayout *layout = new QVBoxLayout( dialog->mainWidget() ); + //layout->addWidget( ); + dialog->mainWidget()->setLayout( layout ); + */ + initStart(); //reload +} + +#if 0 +void InvitationsAgent::createAgentResult(KJob *job) +{ + qDebug(); + AgentInstance agent; + if (job) { + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to create resource: %1", job->errorString())); + return; + } + + AgentInstanceCreateJob *j = static_cast(job); + agent = j->instance(); + agent.setName(i18n("Invitations")); + m_resourceId = agent.identifier(); + + QDBusInterface conf(QString::fromLatin1("org.freedesktop.Akonadi.Resource.") + m_resourceId, + QString::fromLatin1("/Settings"), + QString::fromLatin1("org.kde.Akonadi.ICal.Settings")); + QDBusReply reply = conf.call(QString::fromLatin1("setPath"), + QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "akonadi_ical_resource"); + + if (!reply.isValid()) { + qWarning() << "dbus call failed, m_resourceId=" << m_resourceId; + Q_EMIT status(AgentBase::Broken, i18n("Failed to set the directory for invitations via D-Bus")); + AgentManager::self()->removeInstance(agent); + return; + } + + KConfig config("akonadi_invitations_agent"); + KConfigGroup group = config.group("General"); + group.writeEntry("DefaultCalendarAgent", m_resourceId); + + newAgentCreated = true; + agent.reconfigure(); + } else { + agent = AgentManager::self()->instance(m_resourceId); + Q_ASSERT(agent.isValid()); + } + + ResourceSynchronizationJob *j = new ResourceSynchronizationJob(agent, this); + connect(j, &AgentInstanceCreateJob::result, this, &InvitationsAgent::resourceSyncResult); + j->start(); +} + +void InvitationsAgent::resourceSyncResult(KJob *job) +{ + qDebug(); + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to synchronize collection: %1", job->errorString())); + if (newAgentCreated) { + AgentManager::self()->removeInstance(AgentManager::self()->instance(m_resourceId)); + } + return; + } + CollectionFetchJob *fjob = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive, this); + fjob->fetchScope().setContentMimeTypes(QStringList() << "text/calendar"); + fjob->fetchScope().setResource(m_resourceId); + connect(fjob, &CollectionFetchJob::result, this, &InvitationsAgent::collectionFetchResult); + fjob->start(); +} + +void InvitationsAgent::collectionFetchResult(KJob *job) +{ + qDebug(); + + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to fetch collection: %1", job->errorString())); + if (newAgentCreated) { + AgentManager::self()->removeInstance(AgentManager::self()->instance(m_resourceId)); + } + return; + } + + CollectionFetchJob *fj = static_cast(job); + + if (newAgentCreated) { + // if the agent was just created then there is exactly one collection already + // and we just use that one. + Q_ASSERT(fj->collections().count() == 1); + m_invitations = fj->collections().at(0); + initDone(); + return; + } + + KConfig config("akonadi_invitations_agent"); + KConfigGroup group = config.group("General"); + const QString collectionId = group.readEntry("DefaultCalendarCollection", QString()); + if (!collectionId.isEmpty()) { + // look if the collection is still there. It may the case that there exists such + // a collection with the defined collectionId but that this is not a valid one + // and therefore not in the resultset. + const int id = collectionId.toInt(); + foreach (const Collection &c, fj->collections()) { + if (c.id() == id) { + m_invitations = c; + initDone(); + return; + } + } + } + + // we need to create a new collection and use that one... + Collection c; + c.setName("invitations"); + c.setParent(Collection::root()); + Q_ASSERT(!m_resourceId.isNull()); + c.setResource(m_resourceId); + c.setContentMimeTypes(QStringList() + << "text/calendar" + << "application/x-vnd.akonadi.calendar.event" + << "application/x-vnd.akonadi.calendar.todo" + << "application/x-vnd.akonadi.calendar.journal" + << "application/x-vnd.akonadi.calendar.freebusy"); + CollectionCreateJob *cj = new CollectionCreateJob(c, this); + connect(cj, &CollectionCreateJob::result, this, &InvitationsAgent::collectionCreateResult); + cj->start(); +} + +void InvitationsAgent::collectionCreateResult(KJob *job) +{ + qDebug(); + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to create collection: %1", job->errorString())); + if (newAgentCreated) { + AgentManager::self()->removeInstance(AgentManager::self()->instance(m_resourceId)); + } + return; + } + CollectionCreateJob *j = static_cast(job); + m_invitations = j->collection(); + initDone(); +} +#endif + +Item InvitationsAgent::handleContent(const QString &vcal, + const KCalCore::MemoryCalendar::Ptr &calendar, + const Item &item) +{ + KCalCore::ICalFormat format; + KCalCore::ScheduleMessage::Ptr message = format.parseScheduleMessage(calendar, vcal); + if (!message) { + qWarning() << "Invalid invitation:" << vcal; + return Item(); + } + + qDebug() << "id=" << item.id() << "remoteId=" << item.remoteId() << "vcal=" << vcal; + + KCalCore::Incidence::Ptr incidence = message->event().staticCast(); + Q_ASSERT(incidence); + + IncidenceAttribute *attr = new IncidenceAttribute; + attr->setStatus(QStringLiteral("new")); //TODO + //attr->setFrom( message->from()->asUnicodeString() ); + attr->setReference(item.id()); + + Item newItem; + newItem.setMimeType(incidence->mimeType()); + newItem.addAttribute(attr); + newItem.setPayload(KCalCore::Incidence::Ptr(incidence->clone())); + return newItem; +} + +void InvitationsAgent::itemAdded(const Item &item, const Collection &collection) +{ + qDebug() << item.id() << collection; + Q_UNUSED(collection); + + if (!m_invitationsCollection->defaultCollection().isValid()) { + qDebug() << "No default collection found"; + return; + } + + if (collection.isVirtual()) { + return; + } + + if (!item.hasPayload()) { + qDebug() << "Item has no payload"; + return; + } + + KMime::Message::Ptr message = item.payload(); + + //TODO check if we are the sender and need to ignore the message... + //const QString sender = message->sender()->asUnicodeString(); + //if ( identityManager()->thatIsMe( sender ) ) return; + + KCalCore::MemoryCalendar::Ptr calendar(new KCalCore::MemoryCalendar(KSystemTimeZones::local())); + if (message->contentType()->isMultipart()) { + qDebug() << "message is multipart:" << message->attachments().size(); + + InvitationsAgentItem *it = Q_NULLPTR; + foreach (KMime::Content *content, message->contents()) { + + KMime::Headers::ContentType *ct = content->contentType(); + Q_ASSERT(ct); + qDebug() << "Mimetype of the body part is " << ct->mimeType(); + if (ct->mimeType() != "text/calendar") { + continue; + } + + Item newItem = handleContent(QLatin1String(content->body()), calendar, item); + if (!newItem.hasPayload()) { + qDebug() << "new item has no payload"; + continue; + } + + if (!it) { + it = new InvitationsAgentItem(this, item); + } + + it->add(newItem); + } + } else { + qDebug() << "message is not multipart"; + + KMime::Headers::ContentType *ct = message->contentType(); + Q_ASSERT(ct); + qDebug() << "Mimetype of the body is " << ct->mimeType(); + if (ct->mimeType() != "text/calendar") { + return; + } + + qDebug() << "Message has an invitation in the body, processing"; + + Item newItem = handleContent(QLatin1String(message->body()), calendar, item); + if (!newItem.hasPayload()) { + qDebug() << "new item has no payload"; + return; + } + + InvitationsAgentItem *it = new InvitationsAgentItem(this, item); + it->add(newItem); + } +} + +AKONADI_AGENT_MAIN(InvitationsAgent) + diff --git a/agents/invitations/invitationsagent.desktop b/agents/invitations/invitationsagent.desktop new file mode 100644 index 00000000..e5000d30 --- /dev/null +++ b/agents/invitations/invitationsagent.desktop @@ -0,0 +1,97 @@ +[Desktop Entry] +Name=Invitations Dispatcher Agent +Name[bs]=Agent za raspodjelu poziva +Name[ca]=Agent distribuïdor d'invitacions +Name[ca@valencia]=Agent distribuïdor d'invitacions +Name[cs]=Agent odesílatele pozvánek +Name[da]=Invitationsafsendingsagent +Name[de]=Agent zur Einladungs-Auslieferung +Name[el]=Πράκτορας αποστολής προσκλήσεων +Name[en_GB]=Invitations Dispatcher Agent +Name[es]=Agente despachador de invitaciones +Name[et]=Kutsete edastamise agent +Name[fi]=Kutsunlähetysagentti +Name[fr]=Agent de diffusions d'invitations +Name[gl]=Axente de Despacho de Convites +Name[hu]=Meghívófeladó ügynök +Name[ia]=Agente Distributor de Invitationes +Name[it]=Agente per la consegna degli inviti +Name[ja]=正体ディスパッチャーエージェント +Name[kk]=Шақыру реттеуш агенті +Name[km]=ការ​អញ្ជើញ​​ភ្នាក់ងារ​​កម្មវិធី​បញ្ជូន +Name[ko]=초대장 가져오기 에이전트 +Name[lt]=Laiškų gijų išdėstymo agentas +Name[lv]=Ielūgumu nosūtīšanas aģents +Name[nb]=Agent for invitasjonssendinger +Name[nds]=Inladenverdeel-Hölper +Name[nl]=Uitnodigingen-verspreidingsagent +Name[pl]=Agent wysyłania zaproszeń +Name[pt]=Agente de Despacho de Convites +Name[pt_BR]=Agente de encaminhamento de convites +Name[ro]=Agent de remitere a invitațiilor +Name[ru]=Агент диспетчера приглашений +Name[sk]=Agent spracovania pozvánok +Name[sl]=Posrednik za razpošiljanje povabil +Name[sr]=Агент отпремања позивница +Name[sr@ijekavian]=Агент отпремања позивница +Name[sr@ijekavianlatin]=Agent otpremanja pozivnica +Name[sr@latin]=Agent otpremanja pozivnica +Name[sv]=Modul för inbjudningssändning +Name[tr]=Davetiye Dağıtıcı Programı +Name[uk]=Агент розподілу запрошень +Name[x-test]=xxInvitations Dispatcher Agentxx +Name[zh_CN]=邀请签发代理 +Name[zh_TW]=邀請配送代理程式 +Comment=Dispatches invitations from your calendar +Comment[bs]=Raspoređuje pozive iz vašeg kalendara +Comment[ca]=Distribueix invitacions des del calendari +Comment[ca@valencia]=Distribueix invitacions des del calendari +Comment[da]=Udsender invitationer fra din kalender +Comment[de]=Verschickt Einladungen aus Ihren Kalender +Comment[el]=Διανέμει προσκλήσεις από το ημερολόγιό σας +Comment[en_GB]=Dispatches invitations from your calendar +Comment[es]=Remite invitaciones desde su calendario +Comment[et]=Kutsete edastamine sinu kalendrist +Comment[fi]=Lähettää kutsuja kalenteristasi +Comment[fr]=Diffuse les invitations à partir de votre agenda +Comment[gl]=Xestiona invitacións do calendario. +Comment[hu]=Meghívásokat kézbesít a naptárából +Comment[ia]=Expedi invitationes ex tu calendario +Comment[it]=Consegna inviti dal tuo calendario +Comment[kk]=Күнтізбеңіздегі шақыруларды үлестіру +Comment[ko]=달력에서 초대장을 가져옴 +Comment[lt]=Išsiunčia pakvietimus iš Jūsų kalendoriaus +Comment[nb]=Sender ut invitasjoner fra din kalender +Comment[nds]=Verdeelt Inladen ut Dien Kalenner +Comment[nl]=Brengt uitnodigingen uit uw agenda naar elders +Comment[pl]=Rozsyła zaproszenia z twojego kalendarza +Comment[pt]=Trata dos convites do seu calendário +Comment[pt_BR]=Encaminhamento de convites do seu calendário +Comment[ro]=Remite invitații din calendar +Comment[ru]=Рассылает приглашения из календаря +Comment[sk]=Vybavuje pozvánky z vášho kalendára +Comment[sl]=Odpošlje povabila z vašega koledarja +Comment[sr]=Отпрема позивнице из вашег календара +Comment[sr@ijekavian]=Отпрема позивнице из вашег календара +Comment[sr@ijekavianlatin]=Otprema pozivnice iz vašeg kalendara +Comment[sr@latin]=Otprema pozivnice iz vašeg kalendara +Comment[sv]=Skickar inbjudningar från kalendern +Comment[tr]=Takviminizdeki davetleri yollar +Comment[uk]=Роповсюджує запрошення на основі даних календаря +Comment[x-test]=xxDispatches invitations from your calendarxx +Comment[zh_CN]=从您的日历发送邀请 +Comment[zh_TW]=從您的行事曆中分配邀請 +Type=AkonadiAgent +Exec=akonadi_invitations_agent +Icon=mail-folder-outbox + +X-Akonadi-Identifier=akonadi_invitations_agent +#X-Akonadi-Capabilities=Unique,Autostart,NoConfig +X-Akonadi-Capabilities=NoConfig + +#X-Akonadi-MimeTypes=message/rfc822 +#X-Akonadi-MimeTypes=text/calendar +X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.event +#X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.todo +#X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.journal +#X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.freebusy diff --git a/agents/invitations/invitationsagent.h b/agents/invitations/invitationsagent.h new file mode 100644 index 00000000..4d35c50d --- /dev/null +++ b/agents/invitations/invitationsagent.h @@ -0,0 +1,101 @@ +/* + Copyright 2009 Sebastian Sauer + + 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 INVITATIONSAGENT_H +#define INVITATIONSAGENT_H + +#include + +#include +#include +#include +#include + +#include + +class KJob; + +class InvitationsAgent; +class InvitationsCollection; + +class InvitationsAgentItem : public QObject +{ + Q_OBJECT + +public: + InvitationsAgentItem(InvitationsAgent *a, const Akonadi::Item &originalItem); + virtual ~InvitationsAgentItem(); + void add(const Akonadi::Item &newItem); + +private Q_SLOTS: + void createItemResult(KJob *job); + void fetchItemDone(KJob *); + void modifyItemDone(KJob *job); + +private: + InvitationsAgent *m_agent; + const Akonadi::Item m_originalItem; + QList m_jobs; +}; + +class InvitationsAgent : public Akonadi::AgentBase, public Akonadi::AgentBase::ObserverV2 +{ + Q_OBJECT + +public: + explicit InvitationsAgent(const QString &id); + virtual ~InvitationsAgent(); + + Akonadi::Collection collection(); + +public Q_SLOTS: + void configure(WId windowId) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void initStart(); + void initDone(KJob *job = Q_NULLPTR); + +private: + Akonadi::Item handleContent(const QString &vcal, + const KCalCore::MemoryCalendar::Ptr &calendar, + const Akonadi::Item &item); + + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) Q_DECL_OVERRIDE; + + /* + virtual void itemChanged( const Akonadi::Item &item, const QSet &partIdentifiers ); + virtual void itemRemoved( const Akonadi::Item &item ); + virtual void collectionAdded( const Akonadi::Collection &collection, const Akonadi::Collection &parent ); + virtual void collectionChanged( const Akonadi::Collection &collection ); + virtual void collectionRemoved( const Akonadi::Collection &collection ); + + virtual void itemMoved( const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination ); + virtual void itemLinked( const Akonadi::Item &item, const Akonadi::Collection &collection ); + virtual void itemUnlinked( const Akonadi::Item &item, const Akonadi::Collection &collection ); + virtual void collectionMoved( const Akonadi::Collection &collection, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination ); + virtual void collectionChanged( const Akonadi::Collection &collection, const QSet &changedAttributes ); + */ + +private: + QString m_resourceId; + InvitationsCollection *m_invitationsCollection; + Akonadi::Collection m_collection; +}; + +#endif // MAILDISPATCHERAGENT_H diff --git a/agents/maildispatcher/CMakeLists.txt b/agents/maildispatcher/CMakeLists.txt new file mode 100644 index 00000000..a2213c87 --- /dev/null +++ b/agents/maildispatcher/CMakeLists.txt @@ -0,0 +1,58 @@ + +if (BUILD_TESTING) + add_subdirectory( autotests ) +endif() + +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_maildispatcher_agent\") + +set( maildispatcheragent_SRCS + maildispatcheragent.cpp + outboxqueue.cpp + sendjob.cpp + sentactionhandler.cpp + storeresultjob.cpp +) + +ecm_qt_declare_logging_category(maildispatcheragent_SRCS HEADER maildispatcher_debug.h IDENTIFIER MAILDISPATCHER_LOG CATEGORY_NAME log_maildispatcher) + +ki18n_wrap_ui(maildispatcheragent_SRCS settings.ui) +kconfig_add_kcfg_files(maildispatcheragent_SRCS settings.kcfgc) +kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/maildispatcheragent.kcfg org.kde.Akonadi.MailDispatcher.Settings) +qt5_add_dbus_adaptor(maildispatcheragent_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.MailDispatcher.Settings.xml settings.h Settings +) +qt5_add_dbus_adaptor( maildispatcheragent_SRCS + org.freedesktop.Akonadi.MailDispatcherAgent.xml maildispatcheragent.h MailDispatcherAgent +) + +if (RUNTIME_PLUGINS_STATIC) + add_definitions(-DMAIL_SERIALIZER_PLUGIN_STATIC) +endif () + +add_executable(akonadi_maildispatcher_agent ${maildispatcheragent_SRCS}) + +if (RUNTIME_PLUGINS_STATIC) + target_link_libraries(akonadi_maildispatcher_agent akonadi_serializer_mail) +endif () + +if( APPLE ) + set_target_properties(akonadi_maildispatcher_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_maildispatcher_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.maildispatcher") + set_target_properties(akonadi_maildispatcher_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Maildispatcher") +endif () + +target_link_libraries(akonadi_maildispatcher_agent + KF5::AkonadiCore + KF5::AkonadiMime + KF5::Mime + KF5::MailTransport + KF5::AkonadiAgentBase + KF5::NotifyConfig + KF5::DBusAddons + KF5::I18n + KF5::Notifications +) + +install( TARGETS akonadi_maildispatcher_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) +install( FILES maildispatcheragent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) +install( FILES akonadi_maildispatcher_agent.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) diff --git a/agents/maildispatcher/Messages.sh b/agents/maildispatcher/Messages.sh new file mode 100755 index 00000000..87206060 --- /dev/null +++ b/agents/maildispatcher/Messages.sh @@ -0,0 +1,4 @@ +#! /bin/sh +$EXTRACTRC *.ui *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/akonadi_maildispatcher_agent.pot +rm -f rc.cpp diff --git a/agents/maildispatcher/TODO b/agents/maildispatcher/TODO new file mode 100644 index 00000000..d45568ea --- /dev/null +++ b/agents/maildispatcher/TODO @@ -0,0 +1,16 @@ +* Once MoveJobs work, enable and test moving to sent-mail. Here is why it + doesn't work currently: Akonadi::Monitor sends itemMoved(...), but it is + not handled in AgentBase::Observer, and so it never gets "processed", and + is stuck in the Monitor/ChangeRecorder forever. (I.e. the resource will + not get any new notifications.) This has to be fixed in Akonadi, but that + means adding ObserverV2 or something. +* Figure out which / whether error strings should be i18n'd +* Should probably use progressMessage instead of statusMessage, but it seems + to be unimplemented in AgentInstance, and only a stub in AgentBase. +* Do something about timeouts. +* Test aborting and progress reporting for resource-based transports. + +Bugs: +* Incorrect size reporting in itemChanged() from Monitor. Leads to displaying + >100% progress. + (found with: offline, queue some, online, abort, clearerror) diff --git a/agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc b/agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc new file mode 100644 index 00000000..0ca9716c --- /dev/null +++ b/agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc @@ -0,0 +1,197 @@ +[Global] +IconName=mail-folder-outbox +Comment=KDE e-mail client +Comment[ast]=Veceru de corréu KDE +Comment[bg]=Пощенски клиент за KDE +Comment[bs]=KDE klijent elektronske pošte +Comment[ca]=Client de correu electrònic pel KDE +Comment[ca@valencia]=Client de correu electrònic pel KDE +Comment[cs]=Klient KDE pro čtení elektronické pošty +Comment[da]=KDE e-mail-klient +Comment[de]=E-Mail-Programm für KDE +Comment[el]=Πελάτης ηλ.ταχυδρομείου KDE +Comment[en_GB]=KDE e-mail client +Comment[es]=Cliente de correo de KDE +Comment[et]=KDE e-posti klient +Comment[fi]=KDE:n sähköpostiohjelma +Comment[fr]=Logiciel KDE de courrier électronique +Comment[ga]=Cliant Ríomhphoist KDE +Comment[gl]=Cliente de correo do KDE +Comment[hu]=KDE e-mail kliens +Comment[ia]=Programma KDE de e-posta +Comment[it]=Programma KDE di posta elettronica +Comment[ja]=KDE メールクライアント +Comment[kk]=KDE эл.пошта клиенті +Comment[km]=កម្មវិធី​អ៊ីមែល​របស់ KDE +Comment[ko]=KDE 이메일 클라이언트 +Comment[lt]=KDE el. pašto klientas +Comment[lv]=KDE e-pasta klients +Comment[mr]=केडीई इ-मेल ग्राहक +Comment[nb]=KDE E-postklient +Comment[nds]=Nettpostprogramm för KDE +Comment[nl]=KDE e-mailclient +Comment[pa]=KDE ਈ-ਮੇਲ ਕਲਾਇਟ +Comment[pl]=Program pocztowy KDE +Comment[pt]=Cliente de e-mail do KDE +Comment[pt_BR]=Cliente de e-mail do KDE +Comment[ro]=Program de poștă electronică pentru KDE +Comment[ru]=Почтовый клиент KDE +Comment[sk]=KDE poštový klient +Comment[sl]=KDE-jev poštni odjemalec +Comment[sr]=КДЕ клијент е‑поште +Comment[sr@ijekavian]=КДЕ клијент е‑поште +Comment[sr@ijekavianlatin]=KDE klijent e‑pošte +Comment[sr@latin]=KDE klijent e‑pošte +Comment[sv]=KDE E-postklient +Comment[tr]=KDE e-posta istemcisi +Comment[uk]=Поштовий клієнт KDE +Comment[x-test]=xxKDE e-mail clientxx +Comment[zh_CN]=KDE 邮件客户端 +Comment[zh_TW]=KDE 收發信軟體 +Name=KDE Mail +Name[bg]=KDE поща +Name[bs]=KDE Mail +Name[ca]=Correu del KDE +Name[ca@valencia]=Correu del KDE +Name[cs]=Pošta v KDE +Name[da]=KDE Mail +Name[de]=KDE E-Mail +Name[el]=Αλληλογραφία KDE +Name[en_GB]=KDE Mail +Name[es]=Correo de KDE +Name[et]=KDE e-post +Name[fa]=نامه‌ی کی‌دی‌ای +Name[fi]=KDE Mail +Name[fr]=Messagerie KDE +Name[ga]=Ríomhphost KDE +Name[gl]=Correo do KDE +Name[hu]=KDE Mail +Name[ia]=Posta KDE +Name[it]=KDE Mail +Name[ja]=KDE Mail +Name[kk]=KDE поштасы +Name[km]=សំបុត្រ​របស់ KDE +Name[ko]=KDE 메일 +Name[lt]=KDE paštas +Name[lv]=KDE pasts +Name[mr]=केडीई मेल +Name[nb]=KDE Mail +Name[nds]=KDE-Nettpost +Name[nl]=KDE E-mail +Name[pa]=KDE ਮੇਲ +Name[pl]=Poczta KDE +Name[pt]=Correio do KDE +Name[pt_BR]=E-mail do KDE +Name[ro]=Poșta KDE +Name[ru]=Почта KDE +Name[sk]=KDE Mail +Name[sl]=KDE-jeva pošta +Name[sr]=КДЕ пошта +Name[sr@ijekavian]=КДЕ пошта +Name[sr@ijekavianlatin]=KDE pošta +Name[sr@latin]=KDE pošta +Name[sv]=KDE:s e-postprogram +Name[tr]=KDE E-Posta +Name[uk]=Пошта KDE +Name[x-test]=xxKDE Mailxx +Name[zh_CN]=KDE 电子邮件 +Name[zh_TW]=KDE 郵件軟體 + +[Event/emailsent] +Name=E-mail successfully sent +Name[bg]=Е-пощата е изпратена успешно +Name[bs]=E-mail uspješno poslan +Name[ca]=Correu electrònic enviat correctament +Name[ca@valencia]=Correu electrònic enviat correctament +Name[cs]=E-mail úspěšně odeslán +Name[da]=E-mail sendt +Name[de]=E-Mail erfolgreich gesendet +Name[el]=Η αλληλογραφία στάλθηκε με επιτυχία +Name[en_GB]=E-mail successfully sent +Name[es]=Correo enviado satisfactoriamente +Name[et]=E-kiri saadeti edukalt ära +Name[fa]=ای‌میل با موفقیت ارسال شد +Name[fi]=Sähköpostin lähetys onnistui +Name[fr]=Courrier électronique envoyé avec succès +Name[gl]=Enviouse a mensaxe sen problemas +Name[hu]=Az e-mail sikeresen elküldve +Name[ia]=E-posta inviate successosemente +Name[it]=Messaggio di posta inviato con successo +Name[kk]=Эл.пошта сәтті жіберілді +Name[km]=បាន​ផ្ញើ​អ៊ីមែល​ដោយ​ជោគជ័យ +Name[ko]=이메일을 성공적으로 전송함 +Name[lt]=El. laiškas sėkmingai išsiųstas +Name[lv]=E-pasta veiksmīgi nosūtīts +Name[nb]=E.post vellykket sendt +Name[nds]=Nettbreef mit Spood loosstüert +Name[nl]=E-mailbericht met succes verzonden +Name[pa]=ਈਮੇਲ ਠੀਕ ਤਰ੍ਹਾਂ ਭੇਜੀ ਗਈ +Name[pl]=Poczta została wysłana pomyślnie +Name[pt]=O e-mail foi enviado com sucesso +Name[pt_BR]=E-mail enviado com sucesso +Name[ro]=Scrisoare expediată cu succes +Name[ru]=Почта успешно отправлена +Name[sk]=E-mail úspešne odoslaný +Name[sl]=E-pošta uspešno poslana +Name[sr]=Е‑пошта је успешно послата +Name[sr@ijekavian]=Е‑пошта је успешно послата +Name[sr@ijekavianlatin]=E‑pošta je uspešno poslata +Name[sr@latin]=E‑pošta je uspešno poslata +Name[sv]=E-post skickad med lyckat resultat +Name[tr]=E-posta başarıyla gönderildi +Name[ug]=ئېلخەت مۇۋەپپەقىيەتلىك يوللاندى +Name[uk]=Пошту успішно надіслано +Name[x-test]=xxE-mail successfully sentxx +Name[zh_CN]=邮件已成功发送 +Name[zh_TW]=電子郵件已成功傳送 +Action=Popup +IconName=mail-folder-outbox + +[Event/sendingfailed] +Name=E-mail sending failed +Name[bg]=Грешка при изпращане на е-пощата +Name[bs]=Slanje elektronske pošte neuspjelo +Name[ca]=Ha fallat en enviar el correu electrònic +Name[ca@valencia]=Ha fallat en enviar el correu electrònic +Name[cs]=Odesílání e-mailu selhalo +Name[da]=Afsendelse af e-mail mislykkedes +Name[de]=Fehler beim Versenden der E-Mail +Name[el]=Η αποστολή της αλληλογραφίας απέτυχε +Name[en_GB]=E-mail sending failed +Name[es]=Fallo en el envío del correo +Name[et]=E-kirja saatmine nurjus +Name[fa]=ارسال ای‌میل شکست خورد +Name[fi]=Sähköpostin lähetys epäonnistui +Name[fr]=Erreur lors de l'envoi du courrier électronique +Name[gl]=Produciuse un fallo ao enviar o correo +Name[hu]=Az e-mail küldése sikertelen +Name[ia]=Expedition de e-posta falleva +Name[it]=Invio del messaggio di posta non riuscito +Name[kk]=Эл.поштаны жолдау жаңылысы +Name[km]=បាន​បរាជ័យ​ក្នុង​ការ​ផ្ញើ​អ៊ីមែល +Name[ko]=이메일 전송 실패 +Name[lt]=El. pašto siuntimas nepavyko +Name[lv]=E-pasta sūtīšana neizdevās +Name[nb]=E-postsending mislyktes +Name[nds]=Nettbreef lett sik nich loosstüern +Name[nl]=Verzenden van e-mail is mislukt +Name[pa]=ਈਮੇਲ ਭੇਜਣ ਲਈ ਫੇਲ੍ਹ +Name[pl]=Poczta nie została wysłana pomyślnie +Name[pt]=O envio do e-mail foi mal-sucedido +Name[pt_BR]=Falha no envio do e-mail +Name[ro]=Expedierea scrisorii a eșuat +Name[ru]=Не удалось отправить почту +Name[sk]=Poslanie správy zlyhalo +Name[sl]=Pošiljanje e-pošte ni uspelo +Name[sr]=Слање е‑поште није успело +Name[sr@ijekavian]=Слање е‑поште није успело +Name[sr@ijekavianlatin]=Slanje e‑pošte nije uspelo +Name[sr@latin]=Slanje e‑pošte nije uspelo +Name[sv]=Misslyckades skicka brev +Name[tr]=E-posta gönderimi başarısız oldu +Name[uk]=Спроба надсилання повідомлення зазнала невдачі +Name[x-test]=xxE-mail sending failedxx +Name[zh_CN]=发送邮件失败 +Name[zh_TW]=傳送信件失敗 +Action=Popup +IconName=mail-mark-junk diff --git a/agents/maildispatcher/autotests/CMakeLists.txt b/agents/maildispatcher/autotests/CMakeLists.txt new file mode 100644 index 00000000..52f03c82 --- /dev/null +++ b/agents/maildispatcher/autotests/CMakeLists.txt @@ -0,0 +1,48 @@ +include(ECMMarkAsTest) + +find_package(Qt5Test CONFIG REQUIRED) + +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +# Stolen from kdepimlibs/akonadi/tests +macro(add_akonadi_isolated_test _source) + get_filename_component(_targetName ${_source} NAME_WE) + set(_srcList ${_source} ) + + add_executable(${_targetName} ${_srcList}) + ecm_mark_as_test(maildispatcher-${_targetName}) + target_link_libraries(${_targetName} + Qt5::Test + KF5::AkonadiCore + KF5::AkonadiMime + KF5::MailTransport + KF5::Mime + KF5::I18n + KF5::ConfigGui + Qt5::DBus + Qt5::Widgets + ) + + # based on kde4_add_unit_test + if (WIN32) + get_target_property( _loc ${_targetName} LOCATION ) + set(_executable ${_loc}.bat) + else () + set(_executable ${EXECUTABLE_OUTPUT_PATH}/${_targetName}) + endif () + if (UNIX) + set(_executable ${_executable}.shell) + endif () + + find_program(_testrunner akonaditest) + + if (KDEPIM_RUN_ISOLATED_TESTS) + add_test( maildispatcheragent-${_targetName} ${_testrunner} -c ${CMAKE_CURRENT_SOURCE_DIR}/unittestenv/config.xml ${_executable} ) + endif () +endmacro(add_akonadi_isolated_test) + + + +add_akonadi_isolated_test( aborttest.cpp ) +add_akonadi_isolated_test( dupetest.cpp ) + diff --git a/agents/maildispatcher/autotests/TODO b/agents/maildispatcher/autotests/TODO new file mode 100644 index 00000000..1cdff105 --- /dev/null +++ b/agents/maildispatcher/autotests/TODO @@ -0,0 +1,6 @@ +* test online / offline +* figure out why ksyscoca is started (it's not the wallet apparently) +* aborttest: test aborting when there is >1 message queued +* test the various SendBehaviours and DispatchModes +* dupetest -> probably faster and more effective if I just use random intervals + of time between queuing messages diff --git a/agents/maildispatcher/autotests/aborttest.cpp b/agents/maildispatcher/autotests/aborttest.cpp new file mode 100644 index 00000000..5c667b46 --- /dev/null +++ b/agents/maildispatcher/autotests/aborttest.cpp @@ -0,0 +1,239 @@ +/* + Copyright 2009 Constantin Berzan + + 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 "aborttest.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define SPAM_ADDRESS "idanoka@gmail.com" +// NOTE: This test relies on a large SMTP message taking long enough to deliver, +// for it to call abort. So we need a valid receiver and a not-too-fast connection. +#define MESSAGE_MB 1 + +using namespace Akonadi; +using namespace KMime; +using namespace MailTransport; + +void AbortTest::initTestCase() +{ + QVERIFY(Control::start()); + QTest::qWait(1000); + + qRegisterMetaType(); + qRegisterMetaType(); + + // Get the outbox and clear it. + SpecialMailCollectionsRequestJob *rjob = new SpecialMailCollectionsRequestJob(this); + rjob->requestDefaultCollection(SpecialMailCollections::Outbox); + QSignalSpy spy(rjob, SIGNAL(result(KJob*))); + spy.wait(0); + outbox = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Outbox); + QVERIFY(outbox.isValid()); + ItemDeleteJob *djob = new ItemDeleteJob(outbox); + djob->exec(); // may give error if outbox empty + + // Verify transports. + akoTid = TransportManager::self()->defaultTransportId(); + Transport *t = TransportManager::self()->transportById(akoTid); + QVERIFY(t); + QCOMPARE(t->type(), int(Transport::EnumType::Akonadi)); + QList tids = TransportManager::self()->transportIds(); + tids.removeAll(akoTid); + QCOMPARE(tids.count(), 1); + smtpTid = tids.first(); + t = TransportManager::self()->transportById(smtpTid); + QVERIFY(t); + QCOMPARE(t->type(), int(Transport::EnumType::SMTP)); + + // Set sink collection. + t = TransportManager::self()->transportById(akoTid); + const QString rid = t->host(); + const AgentInstance agent = AgentManager::self()->instance(rid); + QVERIFY(agent.isValid()); + CollectionPathResolver *resolver = new CollectionPathResolver(QStringLiteral("sink"), this); + QVERIFY(resolver->exec()); + sink = Collection(resolver->collection()); + QVERIFY(sink.isValid()); + QDBusInterface conf(QLatin1String("org.freedesktop.Akonadi.Resource.") + rid, + QStringLiteral("/Settings"), QStringLiteral("org.kde.Akonadi.MailTransportDummy.Settings")); + QVERIFY(conf.isValid()); + QDBusReply reply = conf.call(QStringLiteral("setSink"), sink.id()); + QVERIFY(reply.isValid()); + agent.reconfigure(); + + // Watch sink collection. + monitor = new Monitor(this); + monitor->setCollectionMonitored(sink); +} + +void AbortTest::testAbort() +{ + // Get the MDA interface. + DispatcherInterface iface; + QVERIFY(iface.dispatcherInstance().isValid()); + QVERIFY(iface.dispatcherInstance().isOnline()); + + // Create a large message. + qDebug() << "Building message."; + Message::Ptr msg = Message::Ptr(new Message); + QByteArray line(70, 'a'); + line.append("\n"); + QByteArray content("\n"); + for (int i = 0; i < MESSAGE_MB * 1024 * 1024 / line.length() + 10; i++) { + content.append(line); + } + QVERIFY(content.length() > MESSAGE_MB * 1024 * 1024); // >10MiB + msg->setContent(content); + + // Queue the message. + qDebug() << "Queuing message."; + MessageQueueJob *qjob = new MessageQueueJob(this); + qjob->setMessage(msg); + qjob->transportAttribute().setTransportId(smtpTid); + // default dispatch mode + // default sent-mail collection + qjob->addressAttribute().setFrom(QStringLiteral("naiba")); + qjob->addressAttribute().setTo(QStringList() << QStringLiteral(SPAM_ADDRESS)); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + AKVERIFYEXEC(qjob); + + // Wait for the MDA to begin dispatching. + for (int ds = 0; iface.dispatcherInstance().status() == AgentInstance::Idle; ds++) { + QTest::qWait(100); + if (ds % 10 == 0) { + qDebug() << "Waiting for the MDA to begin dispatching." << ds / 10 << "seconds elapsed."; + } + + QVERIFY2(ds <= 100, "Timeout"); + } + QTest::qWait(100); + + // Tell the MDA to abort. + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Running); + iface.dispatcherInstance().abortCurrentTask(); + for (int ds = 0; iface.dispatcherInstance().status() != AgentInstance::Idle; ds++) { + QTest::qWait(100); + if (ds % 10 == 0) { + qDebug() << "Waiting for the MDA to become idle after aborting." << ds / 10 << "seconds elapsed."; + } + + QVERIFY2(ds <= 100, "Timeout"); + } + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + + // Verify that item has an ErrorAttribute. + ItemFetchJob *fjob = new ItemFetchJob(outbox); + fjob->fetchScope().fetchAllAttributes(); + AKVERIFYEXEC(fjob); + QCOMPARE(fjob->items().count(), 1); + Item item = fjob->items().at(0); + QVERIFY(item.hasAttribute()); + ErrorAttribute *eA = item.attribute(); + qDebug() << "Stored error:" << eA->message(); + + // "Fix" the item and send again, this time with the default (Akonadi) transport. + item.removeAttribute(); + item.clearFlag(Akonadi::MessageFlags::HasError); + item.setFlag(Akonadi::MessageFlags::Queued); + TransportAttribute *newTA = new TransportAttribute(akoTid); + item.addAttribute(newTA); + ItemModifyJob *cjob = new ItemModifyJob(item); + QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))); + AKVERIFYEXEC(cjob); + + // Verify that the item got sent. + for (int ds = 0; addSpy->isEmpty(); ds++) { + QTest::qWait(100); + if (ds % 10 == 0) { + qDebug() << "Waiting for an item to be sent." << ds / 10 << "seconds elapsed."; + } + + QVERIFY2(ds <= 100, "Timeout"); + } + QCOMPARE(addSpy->count(), 1); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); +} + +void AbortTest::testAbortWhileIdle() +{ + // Get the MDA interface. + DispatcherInterface iface; + QVERIFY(iface.dispatcherInstance().isValid()); + QVERIFY(iface.dispatcherInstance().isOnline()); + + // Abort thin air. + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + iface.dispatcherInstance().abortCurrentTask(); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + + // Queue a message (to check that subsequent messages are being sent). + QVERIFY(monitor); + QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))); + Message::Ptr msg = Message::Ptr(new Message); + msg->setContent("\ntestAbortWhileIdle"); + MessageQueueJob *qjob = new MessageQueueJob(this); + qjob->setMessage(msg); + qjob->transportAttribute().setTransportId(akoTid); + // default dispatch mode + // default sent-mail collection + qjob->addressAttribute().setFrom(QStringLiteral("naiba")); + qjob->addressAttribute().setTo(QStringList() << QStringLiteral("dracu")); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + AKVERIFYEXEC(qjob); + + // Verify that the item got sent. + for (int s = 0; addSpy->isEmpty(); s++) { + QTest::qWait(1000); + QVERIFY2(s <= 10, "Timeout"); + } + QCOMPARE(addSpy->count(), 1); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); +} + +QTEST_AKONADIMAIN(AbortTest) + diff --git a/agents/maildispatcher/autotests/aborttest.h b/agents/maildispatcher/autotests/aborttest.h new file mode 100644 index 00000000..3235a965 --- /dev/null +++ b/agents/maildispatcher/autotests/aborttest.h @@ -0,0 +1,54 @@ +/* + Copyright 2009 Constantin Berzan + + 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 ABORTTEST_H +#define ABORTTEST_H + +#include + +#include + +namespace Akonadi +{ +class Monitor; +} + +/** + This attempts to send a large message, then aborts it, then tries to send + it again and verify that it succeeds. + */ +class AbortTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testAbort(); + void testAbortWhileIdle(); + +private: + int akoTid; + int smtpTid; + Akonadi::Collection outbox; + Akonadi::Collection sink; + Akonadi::Monitor *monitor; + +}; + +#endif diff --git a/agents/maildispatcher/autotests/dupetest.cpp b/agents/maildispatcher/autotests/dupetest.cpp new file mode 100644 index 00000000..a1f4b671 --- /dev/null +++ b/agents/maildispatcher/autotests/dupetest.cpp @@ -0,0 +1,220 @@ +/* + Copyright 2009 Constantin Berzan + + 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 "dupetest.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static const int TIMEOUT_SECONDS = 60; +static const int MAXCOUNT = 99; // must be 2-digit! + +using namespace Akonadi; +using namespace KMime; +using namespace MailTransport; + +void DupeTest::initTestCase() +{ + QVERIFY(Control::start()); + QTest::qWait(1000); // give the MDA time to start + + qRegisterMetaType(); + qRegisterMetaType(); + + // we need a default Akonadi transport + int tid = TransportManager::self()->defaultTransportId(); + Transport *t = TransportManager::self()->transportById(tid); + QVERIFY(t); + QCOMPARE(t->type(), int(Transport::EnumType::Akonadi)); + + // set the sink collection + const QString rid = t->host(); + const AgentInstance agent = AgentManager::self()->instance(rid); + QVERIFY(agent.isValid()); + CollectionPathResolver *resolver = new CollectionPathResolver(QStringLiteral("sink"), this); + QVERIFY(resolver->exec()); + sink = Collection(resolver->collection()); + QVERIFY(sink.isValid()); + QDBusInterface conf(QLatin1String("org.freedesktop.Akonadi.Resource.") + rid, + QStringLiteral("/Settings"), QStringLiteral("org.kde.Akonadi.MailTransportDummy.Settings")); + QVERIFY(conf.isValid()); + QDBusReply reply = conf.call(QStringLiteral("setSink"), sink.id()); + QVERIFY(reply.isValid()); + agent.reconfigure(); + + // set up monitor + monitor = new Monitor(this); + monitor->setCollectionMonitored(sink); + monitor->itemFetchScope().fetchFullPayload(); +} + +void DupeTest::testDupes_data() +{ + QTest::addColumn("message"); // the prefix of the message to send (-msg## is appended) + QTest::addColumn("count"); // how many copies to send + QTest::addColumn("delay"); // number of ms to wait before sending next copy + + QTest::newRow("1-nodelay") << "\n1-nodelay" << 1 << 0; + QTest::newRow("2-nodelay") << "\n2-nodelay" << 2 << 0; + QTest::newRow("5-nodelay") << "\n5-nodelay" << 5 << 0; + QTest::newRow("10-nodelay") << "\n10-nodelay" << 10 << 0; + QTest::newRow("20-nodelay") << "\n20-nodelay" << 20 << 0; + QTest::newRow("50-nodelay") << "\n50-nodelay" << 50 << 0; + QTest::newRow("99-nodelay") << "\n99-nodelay" << 99 << 0; + QTest::newRow("2-veryshortdelay") << "\n2-veryshortdelay" << 2 << 20; + QTest::newRow("5-veryshortdelay") << "\n5-veryshortdelay" << 5 << 20; + QTest::newRow("10-veryshortdelay") << "\n10-veryshortdelay" << 10 << 20; + QTest::newRow("20-veryshortdelay") << "\n20-veryshortdelay" << 20 << 20; + QTest::newRow("50-veryshortdelay") << "\n50-veryshortdelay" << 50 << 20; + QTest::newRow("99-veryshortdelay") << "\n99-veryshortdelay" << 99 << 20; + QTest::newRow("2-shortdelay") << "\n2-shortdelay" << 2 << 100; + QTest::newRow("5-shortdelay") << "\n5-shortdelay" << 5 << 100; + QTest::newRow("10-shortdelay") << "\n10-shortdelay" << 10 << 100; + QTest::newRow("20-shortdelay") << "\n20-shortdelay" << 20 << 100; + QTest::newRow("50-shortdelay") << "\n50-shortdelay" << 50 << 100; + QTest::newRow("99-shortdelay") << "\n99-shortdelay" << 99 << 99; + QTest::newRow("2-longdelay") << "\n2-longdelay" << 2 << 1000; + QTest::newRow("5-longdelay") << "\n5-longdelay" << 5 << 1000; + QTest::newRow("5-verylongdelay") << "\n5-verylongdelay" << 5 << 4000; + Q_ASSERT(99 <= MAXCOUNT); + Q_ASSERT(MAXCOUNT < 100); // 2-digit + + // TODO I'm not sure more items means a better test + // TODO test large items + // TODO test modifying items while they are being sent... +} + +void DupeTest::testDupes() +{ + QFETCH(QString, message); + QFETCH(int, count); + QFETCH(int, delay); + + // clean sink + ItemFetchJob *fjob = new ItemFetchJob(sink, this); + AKVERIFYEXEC(fjob); + if (fjob->items().count() > 0) { + // this test is needed because ItemDeleteJob gives error if no items are found + ItemDeleteJob *djob = new ItemDeleteJob(sink, this); + AKVERIFYEXEC(djob); + } + fjob = new ItemFetchJob(sink, this); + AKVERIFYEXEC(fjob); + QCOMPARE(fjob->items().count(), 0); + + // queue messages + Q_ASSERT(monitor); + QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))); + qDebug() << "Queuing" << count << "messages..."; + for (int i = 0; i < count; i++) { + //qDebug() << "Queuing message" << i + 1 << "of" << count; + + Message::Ptr msg = Message::Ptr(new Message); + msg->setContent(QStringLiteral("%1-msg%2\n").arg(message).arg(i + 1, 2, 10, QLatin1Char('0')).toLatin1()); + + MessageQueueJob *job = new MessageQueueJob(this); + job->setMessage(msg); + job->transportAttribute().setTransportId(TransportManager::self()->defaultTransportId()); + // default dispatch mode + // default sent-mail collection + job->addressAttribute().setFrom(QStringLiteral("naiba")); + job->addressAttribute().setTo(QStringList() << QStringLiteral("dracu")); + //AKVERIFYEXEC( job ); + job->start(); + QTest::qWait(delay); + } + qDebug() << "Queued" << count << "messages."; + + // wait for the MDA to send them + int seconds = 0; + while (true) { + seconds++; + QTest::qWait(1000); + qDebug() << seconds << "seconds elapsed." << addSpy->count() << "messages got to sink."; + if (addSpy->count() >= count) { + break; + } + +#if 0 + if (seconds >= TIMEOUT_SECONDS) { + qDebug() << "Timeout, gdb master!"; + QTest::qWait(1000 * 1000); + } +#endif + QVERIFY2(seconds < TIMEOUT_SECONDS, "Timeout"); + } + + // TODO I should verify that the MDA has actually finished its work and has an empty queue + QTest::qWait(2000); + + // verify what has been sent + fjob = new ItemFetchJob(sink, this); + fjob->fetchScope().fetchFullPayload(); + AKVERIFYEXEC(fjob); + const Item::List items = fjob->items(); + int found[ MAXCOUNT ]; + for (int i = 0; i < count; i++) { + found[i] = 0; + } + for (int i = 0; i < items.count(); i++) { + QVERIFY(items[i].hasPayload()); + Message::Ptr msg = items[i].payload(); + const QByteArray content = msg->encodedContent(); + //qDebug() << "i" << i << "content" << content; + int loc = content.indexOf("-msg"); + QVERIFY(loc >= 0); + bool ok; + int who = content.mid(loc + 4, 2).toInt(&ok); + QVERIFY(ok); + //qDebug() << "identified msg" << who; + QVERIFY(who > 0 && who <= count); + found[ who - 1 ]++; + } + for (int i = 0; i < count; i++) { + if (found[i] > 1) { + qDebug() << "found duplicate message" << i + 1 << "(" << found[i] << "times )"; + } else if (found[i] < 1) { + qDebug() << "didn't find message" << i + 1; + } + QCOMPARE(found[i], 1); + } + QCOMPARE(addSpy->count(), count); + QCOMPARE(items.count(), count); +} + +QTEST_AKONADIMAIN(DupeTest) + diff --git a/agents/maildispatcher/autotests/dupetest.h b/agents/maildispatcher/autotests/dupetest.h new file mode 100644 index 00000000..b78516df --- /dev/null +++ b/agents/maildispatcher/autotests/dupetest.h @@ -0,0 +1,48 @@ +/* + Copyright 2009 Constantin Berzan + + 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 DUPETEST_H +#define DUPETEST_H + +#include + +#include +#include + +/** + This queues a bunch of messages very quickly one after the other, lets the + MDA send them via the dummy mailtransport resource, and then verify that the + correct number of messages has been sent. + */ +class DupeTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testDupes_data(); + void testDupes(); + +private: + Akonadi::Collection sink; + Akonadi::Monitor *monitor; + +}; + +#endif diff --git a/agents/maildispatcher/autotests/unittestenv/config.xml b/agents/maildispatcher/autotests/unittestenv/config.xml new file mode 100644 index 00000000..248ebb58 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/config.xml @@ -0,0 +1,7 @@ + + kdehome + xdgconfig + xdglocal + akonadi_knut_resource + akonadi_mailtransport_dummy_resource + diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc new file mode 100644 index 00000000..1cac492a --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc @@ -0,0 +1,3 @@ +[ProcessedDefaults] +defaultaddressbook=done +defaultcalendar=done diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc new file mode 100644 index 00000000..fb457b18 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc @@ -0,0 +1,4 @@ +[General] +DataFile[$e]=$KDEHOME/testdata.xml +FileWatchingEnabled=false + diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc new file mode 100644 index 00000000..6d160492 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc @@ -0,0 +1,2 @@ +[General] +Sink=123 diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc new file mode 100644 index 00000000..32317f74 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc @@ -0,0 +1,110 @@ +[0] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5250] +InfoOutput=2 + +[5251] +InfoOutput=2 + +[5252] +InfoOutput=2 + +[5253] +InfoOutput=2 + +[5254] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5255] +InfoOutput=2 + +[5256] +InfoOutput=2 + +[5257] +InfoOutput=2 + +[5258] +InfoOutput=2 + +[5259] +InfoOutput=2 + +[5260] +InfoOutput=2 + +[5261] +InfoOutput=2 + +[5262] +InfoOutput=2 + +[5263] +InfoOutput=2 + +[5264] +InfoOutput=2 + +[5265] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5266] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5295] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5324] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[7129] +InfoOutput=2 diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc new file mode 100644 index 00000000..41d17814 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc @@ -0,0 +1,3 @@ +[General] +CheckSycoca=false +CheckFileStamps=false diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc new file mode 100644 index 00000000..8ba29ca1 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc @@ -0,0 +1,2 @@ +[Wallet] +Enabled=false diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports new file mode 100644 index 00000000..13ac7b09 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports @@ -0,0 +1,23 @@ +[$Version] +update_info=mailtransports.upd:initial-kmail-migration,mailtransports.upd:initial-knode-migration + +[General] +default-transport=666 + +[Transport 666] +host=akonadi_mailtransport_dummy_resource_0 +id=666 +name=Dummy Akonadi Transport +type=Akonadi + +[Transport 549190884] +auth=true +encryption=SSL +host=smtp.gmail.com +id=549190884 +name=idanoka2-stored +password=ᄒᄡᄚᄆᄒᄏᄊᆱᄎᆲᆱ +port=465 +storepass=true +user=idanoka2@gmail.com + diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc new file mode 100644 index 00000000..2e2f28ea --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc @@ -0,0 +1,2 @@ +[Notification Messages] +WalletMigrate=false diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml b/agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml new file mode 100644 index 00000000..8cf871b9 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml @@ -0,0 +1,4 @@ + + + + diff --git a/agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc b/agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc new file mode 100644 index 00000000..7f738ce2 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc @@ -0,0 +1,4 @@ +[%General] + +[Search] +Manager=Dummy diff --git a/agents/maildispatcher/autotests/unittestenv/xdglocal/.keep b/agents/maildispatcher/autotests/unittestenv/xdglocal/.keep new file mode 100644 index 00000000..2f1d07a9 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/xdglocal/.keep @@ -0,0 +1 @@ +force git to include this file diff --git a/agents/maildispatcher/maildispatcheragent.cpp b/agents/maildispatcher/maildispatcheragent.cpp new file mode 100644 index 00000000..65b17bb6 --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.cpp @@ -0,0 +1,361 @@ +/* + Copyright 2008 Ingo Klöcker + Copyright 2009 Constantin Berzan + + 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 "maildispatcheragent.h" + +//#include "configdialog.h" +#include "maildispatcheragentadaptor.h" +#include "outboxqueue.h" +#include "sendjob.h" +#include "sentactionhandler.h" +#include "settings.h" +#include "settingsadaptor.h" + +#include +#include +#include + +#include +#include "maildispatcher_debug.h" +#include +#include +#include +#include + +#include +#include + +#ifdef MAIL_SERIALIZER_PLUGIN_STATIC + +Q_IMPORT_PLUGIN(akonadi_serializer_mail) +#endif + +using namespace Akonadi; + +class MailDispatcherAgent::Private +{ +public: + Private(MailDispatcherAgent *parent) + : q(parent), + queue(Q_NULLPTR), + currentJob(Q_NULLPTR), + aborting(false), + sendingInProgress(false), + sentAnything(false), + errorOccurred(false), + sentItemsSize(0), + sentActionHandler(Q_NULLPTR) + { + } + + ~Private() + { + } + + MailDispatcherAgent *const q; + + OutboxQueue *queue; + SendJob *currentJob; + Item currentItem; + bool aborting; + bool sendingInProgress; + bool sentAnything; + bool errorOccurred; + qulonglong sentItemsSize; + SentActionHandler *sentActionHandler; + + // Q_SLOTS: + void abort(); + void dispatch(); + void itemFetched(const Item &item); + void queueError(const QString &message); + void sendPercent(KJob *job, unsigned long percent); + void sendResult(KJob *job); + void emitStatusReady(); +}; + +void MailDispatcherAgent::Private::abort() +{ + if (!q->isOnline()) { + qCDebug(MAILDISPATCHER_LOG) << "Offline. Ignoring call."; + return; + } + + if (aborting) { + qCDebug(MAILDISPATCHER_LOG) << "Already aborting."; + return; + } + + if (!sendingInProgress && queue->isEmpty()) { + qCDebug(MAILDISPATCHER_LOG) << "MDA is idle."; + Q_ASSERT(q->status() == AgentBase::Idle); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Aborting..."; + aborting = true; + if (currentJob) { + currentJob->abort(); + } + // Further SendJobs will mark remaining items in the queue as 'aborted'. + } +} + +void MailDispatcherAgent::Private::dispatch() +{ + Q_ASSERT(queue); + + if (!q->isOnline() || sendingInProgress) { + qCDebug(MAILDISPATCHER_LOG) << "Offline or busy. See you later."; + return; + } + + if (!queue->isEmpty()) { + if (!sentAnything) { + sentAnything = true; + sentItemsSize = 0; + Q_EMIT q->percent(0); + } + Q_EMIT q->status(AgentBase::Running, + i18np("Sending messages (1 item in queue)...", + "Sending messages (%1 items in queue)...", queue->count())); + qCDebug(MAILDISPATCHER_LOG) << "Attempting to dispatch the next message."; + sendingInProgress = true; + queue->fetchOne(); // will trigger itemFetched + } else { + qCDebug(MAILDISPATCHER_LOG) << "Empty queue."; + if (aborting) { + // Finished marking messages as 'aborted'. + aborting = false; + sentAnything = false; + Q_EMIT q->status(AgentBase::Idle, i18n("Sending canceled.")); + QTimer::singleShot(3000, q, SLOT(emitStatusReady())); + } else { + if (sentAnything) { + // Finished sending messages in queue. + sentAnything = false; + Q_EMIT q->percent(100); + Q_EMIT q->status(AgentBase::Idle, i18n("Finished sending messages.")); + + if (!errorOccurred) { + KNotification *notify = new KNotification(QStringLiteral("emailsent")); + notify->setComponentName(QStringLiteral("akonadi_maildispatcher_agent")); + notify->setTitle(i18nc("Notification title when email was sent", "E-Mail Successfully Sent")); + notify->setText(i18nc("Notification when the email was sent", "Your E-Mail has been sent successfully.")); + notify->sendEvent(); + } + } else { + // Empty queue. + Q_EMIT q->status(AgentBase::Idle, i18n("No items in queue.")); + } + QTimer::singleShot(3000, q, SLOT(emitStatusReady())); + } + + errorOccurred = false; + } +} + +MailDispatcherAgent::MailDispatcherAgent(const QString &id) + : AgentBase(id), + d(new Private(this)) +{ + Kdelibs4ConfigMigrator migrate(QStringLiteral("maildispatcheragent")); + migrate.setConfigFiles(QStringList() << QStringLiteral("maildispatcheragentrc") << QStringLiteral("akonadi_maildispatcher_agent.notifyrc")); + migrate.migrate(); + + qCDebug(MAILDISPATCHER_LOG) << "maildispatcheragent: At your service, sir!"; +#ifdef KDEPIM_STATIC_LIBS + ___MailTransport____INIT(); +#endif + + new SettingsAdaptor(Settings::self()); + new MailDispatcherAgentAdaptor(this); + + KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/Settings"), + Settings::self(), QDBusConnection::ExportAdaptors); + + KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/MailDispatcherAgent"), + this, QDBusConnection::ExportAdaptors); + KDBusConnectionPool::threadConnection().registerService(QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent")); + + d->queue = new OutboxQueue(this); + connect(d->queue, SIGNAL(newItems()), + this, SLOT(dispatch())); + connect(d->queue, SIGNAL(itemReady(Akonadi::Item)), + this, SLOT(itemFetched(Akonadi::Item))); + connect(d->queue, SIGNAL(error(QString)), + this, SLOT(queueError(QString))); + connect(this, SIGNAL(itemProcessed(Akonadi::Item,bool)), + d->queue, SLOT(itemProcessed(Akonadi::Item,bool))); + connect(this, SIGNAL(abortRequested()), + this, SLOT(abort())); + + d->sentActionHandler = new SentActionHandler(this); + + setNeedsNetwork(true); +} + +MailDispatcherAgent::~MailDispatcherAgent() +{ + delete d; +} + +void MailDispatcherAgent::configure(WId windowId) +{ + Q_UNUSED(windowId); + KNotifyConfigWidget::configure(Q_NULLPTR); +} + +void MailDispatcherAgent::doSetOnline(bool online) +{ + Q_ASSERT(d->queue); + if (online) { + qCDebug(MAILDISPATCHER_LOG) << "Online. Dispatching messages."; + Q_EMIT status(AgentBase::Idle, i18n("Online, sending messages in queue.")); + QTimer::singleShot(0, this, SLOT(dispatch())); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Offline."; + Q_EMIT status(AgentBase::Idle, i18n("Offline, message sending suspended.")); + + // TODO: This way, the OutboxQueue will continue to react to changes in + // the outbox, but the MDA will just not send anything. Is this what we + // want? + } + + AgentBase::doSetOnline(online); +} + +void MailDispatcherAgent::Private::itemFetched(const Item &item) +{ + qCDebug(MAILDISPATCHER_LOG) << "Fetched item" << item.id() << "; creating SendJob."; + Q_ASSERT(sendingInProgress); + Q_ASSERT(!currentItem.isValid()); + currentItem = item; + Q_ASSERT(currentJob == 0); + Q_EMIT q->itemDispatchStarted(); + + currentJob = new SendJob(item, q); + if (aborting) { + currentJob->setMarkAborted(); + } + + q->status(AgentBase::Running, i18nc("Message with given subject is being sent.", "Sending: %1", + item.payload()->subject()->asUnicodeString())); + + connect(currentJob, SIGNAL(result(KJob*)), + q, SLOT(sendResult(KJob*))); + connect(currentJob, SIGNAL(percent(KJob*,ulong)), + q, SLOT(sendPercent(KJob*,ulong))); + + currentJob->start(); +} + +void MailDispatcherAgent::Private::queueError(const QString &message) +{ + Q_EMIT q->error(message); + errorOccurred = true; + // FIXME figure out why this does not set the status to Broken, etc. +} + +void MailDispatcherAgent::Private::sendPercent(KJob *job, unsigned long) +{ + Q_ASSERT(sendingInProgress); + Q_ASSERT(job == currentJob); + // The progress here is actually the TransportJob, not the entire SendJob, + // because the post-job doesn't report progress. This should be fine, + // since the TransportJob is the lengthiest operation. + + // Give the transport 80% of the weight, and move-to-sendmail 20%. + const double transportWeight = 0.8; + + const int percent = 100 * (sentItemsSize + job->processedAmount(KJob::Bytes) * transportWeight) + / (sentItemsSize + currentItem.size() + queue->totalSize()); + + qCDebug(MAILDISPATCHER_LOG) << "sentItemsSize" << sentItemsSize + << "this job processed" << job->processedAmount(KJob::Bytes) + << "queue totalSize" << queue->totalSize() + << "total total size (sent+current+queue)" << (sentItemsSize + currentItem.size() + queue->totalSize()) + << "new percentage" << percent << "old percentage" << q->progress(); + + if (percent != q->progress()) { + // The progress can decrease too, if messages got added to the queue. + Q_EMIT q->percent(percent); + } + + // It is possible that the number of queued messages has changed. + Q_EMIT q->status(AgentBase::Running, + i18np("Sending messages (1 item in queue)...", + "Sending messages (%1 items in queue)...", 1 + queue->count())); +} + +void MailDispatcherAgent::Private::sendResult(KJob *job) +{ + Q_ASSERT(sendingInProgress); + Q_ASSERT(job == currentJob); + currentJob->disconnect(q); + currentJob = Q_NULLPTR; + + Q_ASSERT(currentItem.isValid()); + sentItemsSize += currentItem.size(); + Q_EMIT q->itemProcessed(currentItem, !job->error()); + + const Akonadi::Item sentItem = currentItem; + currentItem = Item(); + + if (job->error()) { + // The SendJob gave the item an ErrorAttribute, so we don't have to + // do anything. + qCDebug(MAILDISPATCHER_LOG) << "Sending failed. error:" << job->errorString(); + + KNotification *notify = new KNotification(QStringLiteral("sendingfailed")); + notify->setComponentName(QStringLiteral("akonadi_maildispatcher_agent")); + notify->setTitle(i18nc("Notification title when email sending failed", "E-Mail Sending Failed")); + notify->setText(job->errorString()); + notify->sendEvent(); + + errorOccurred = true; + } else { + qCDebug(MAILDISPATCHER_LOG) << "Sending succeeded."; + + // handle possible sent actions + const MailTransport::SentActionAttribute *attribute = sentItem.attribute(); + if (attribute) { + foreach (const MailTransport::SentActionAttribute::Action &action, attribute->actions()) { + sentActionHandler->runAction(action); + } + } + } + + // dispatch next message + sendingInProgress = false; + QTimer::singleShot(0, q, SLOT(dispatch())); +} + +void MailDispatcherAgent::Private::emitStatusReady() +{ + if (q->status() == AgentBase::Idle) { + // If still idle after aborting, clear 'aborted' status. + Q_EMIT q->status(AgentBase::Idle, i18n("Ready to dispatch messages.")); + } +} + +#ifndef KDEPIM_PLUGIN_AGENT +AKONADI_AGENT_MAIN(MailDispatcherAgent) +#endif + +#include "moc_maildispatcheragent.cpp" diff --git a/agents/maildispatcher/maildispatcheragent.desktop b/agents/maildispatcher/maildispatcheragent.desktop new file mode 100644 index 00000000..5fb619e0 --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.desktop @@ -0,0 +1,93 @@ +[Desktop Entry] +Name=Mail Dispatcher Agent +Name[bs]=Dispačer mail agent +Name[ca]=Agent distribuïdor de correu +Name[ca@valencia]=Agent distribuïdor de correu +Name[cs]=Agent odesílatele zpráv +Name[da]=Mailafsendingsagent (MDA) +Name[de]=Agent zur Nachrichten-Auslieferung +Name[el]=Πράκτορας αποστολής αλληλογραφίας +Name[en_GB]=Mail Dispatcher Agent +Name[es]=Agente despachador de correo +Name[et]=Kirjade edastamise agent +Name[fi]=Postinvälitysagentti +Name[fr]=Agent de diffusion de messages +Name[gl]=Axente de Despacho de Correo +Name[hu]=Levélfeladó ügynök +Name[ia]=Agente Distributor de Posta +Name[it]=Agente per la consegna della posta +Name[ja]=メール送信エージェント +Name[kk]=Пошта реттеуш агенті +Name[km]=ភ្នាក់ងារ​កម្មវិធី​បញ្ជូន​សំបុត្រ +Name[ko]=메일 가져오기 마법사 +Name[lt]=Laiškų gijų išdėstymo agentas +Name[lv]=Pasta nosūtīšanas aģents +Name[nb]=Agent for e-postsending +Name[nds]=Nettpostverdeel-Hölper +Name[nl]=Agent voor het verzenden van e-mail +Name[pa]=ਮੇਲ ਡਿਸਪੈਚਰ ਏਜੰਟ +Name[pl]=Agent przyjmowania poczty +Name[pt]=Agente de Despacho do Correio +Name[pt_BR]=Agente de encaminhamento de e-mails +Name[ro]=Agent de livrare a mesajelor +Name[ru]=Агент почтового диспетчера +Name[sk]=Agent spracovania pošty +Name[sl]=Posrednik za razpošiljanje pošte +Name[sr]=Агент отпремања поште +Name[sr@ijekavian]=Агент отпремања поште +Name[sr@ijekavianlatin]=Agent otpremanja pošte +Name[sr@latin]=Agent otpremanja pošte +Name[sv]=E-postsändningsmodul +Name[tr]=E-posta Yönetim Aracı +Name[ug]=Mail Dispatcher Agent +Name[uk]=Агент розподілу пошти +Name[x-test]=xxMail Dispatcher Agentxx +Name[zh_CN]=邮件签发代理 +Name[zh_TW]=郵件配送代理程式 +Comment=Dispatches email messages +Comment[bs]=Raspoređuje E-mail poruke +Comment[ca]=Distribueix missatges de correu electrònic +Comment[ca@valencia]=Distribueix missatges de correu electrònic +Comment[cs]=Odesílá e-mailové zprávy +Comment[da]=Udsender e-mails +Comment[de]=Verteilt E-Mail-Nachrichten +Comment[el]=Διανέμει μηνύματα ηλ. ταχυδρομείου +Comment[en_GB]=Dispatches email messages +Comment[es]=Remite mensajes de correo +Comment[et]=Kirjade edastamine +Comment[fi]=Lähettää sähköpostiviestejä +Comment[fr]=Diffuse les courriers électroniques +Comment[gl]=Xestiona mensaxes de correo. +Comment[hu]=E-mail üzeneteket kézbesít +Comment[ia]=Expedi messages de e-posta +Comment[it]=Invia messaggi di posta elettronica +Comment[kk]=Пошта хаттарын үлестіру +Comment[ko]=이메일 메시지를 가져옴 +Comment[lt]=Išsiunčia el. laiškus +Comment[nb]=Sender ut e-postmeldinger +Comment[nds]=Verdeelt Nettpost +Comment[nl]=Verstuurt e-mailberichten +Comment[pl]=Rozsyła wiadomości pocztowe +Comment[pt]=Trata das mensagens de e-mail +Comment[pt_BR]=Encaminhamento de e-mails +Comment[ro]=Remite scrisori electronice +Comment[ru]=Рассылает почтовые сообщения +Comment[sk]=Vybavuje e-mailové správy +Comment[sl]=Odpošlje e-poštna sporočila +Comment[sr]=Отпрема е‑поштанске поруке +Comment[sr@ijekavian]=Отпрема е‑поштанске поруке +Comment[sr@ijekavianlatin]=Otprema e‑poštanske poruke +Comment[sr@latin]=Otprema e‑poštanske poruke +Comment[sv]=Skickar brev med e-post +Comment[tr]=E-posta mesajlarını yollar +Comment[uk]=Розповсюджує повідомлення електронної пошти +Comment[x-test]=xxDispatches email messagesxx +Comment[zh_CN]=发送电子邮件 +Comment[zh_TW]=分配電子郵件訊息 +Type=AkonadiAgent +Exec=akonadi_maildispatcher_agent +Icon=mail-folder-outbox + +X-Akonadi-MimeTypes=message/rfc822 +X-Akonadi-Capabilities=Unique,Autostart +X-Akonadi-Identifier=akonadi_maildispatcher_agent diff --git a/agents/maildispatcher/maildispatcheragent.h b/agents/maildispatcher/maildispatcheragent.h new file mode 100644 index 00000000..3d944a83 --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.h @@ -0,0 +1,76 @@ +/* + Copyright 2008 Ingo Klöcker + Copyright 2009 Constantin Berzan + + 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 MAILDISPATCHERAGENT_H +#define MAILDISPATCHERAGENT_H + +#include + +namespace Akonadi +{ +class Item; +} + +/** + * @short This agent dispatches mail put into the outbox collection. + */ +class MailDispatcherAgent : public Akonadi::AgentBase +{ + Q_OBJECT + + Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Akonadi.MailDispatcherAgent") + +public: + explicit MailDispatcherAgent(const QString &id); + ~MailDispatcherAgent(); + +public Q_SLOTS: + void configure(WId windowId) Q_DECL_OVERRIDE; + +Q_SIGNALS: + /** + * Emitted when the MDA has attempted to send an item. + */ + void itemProcessed(const Akonadi::Item &item, bool result); + + /** + * Emitted when the MDA has begun processing an item + */ + Q_SCRIPTABLE void itemDispatchStarted(); + +protected: + void doSetOnline(bool online) Q_DECL_OVERRIDE; + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void abort()) + Q_PRIVATE_SLOT(d, void dispatch()) + Q_PRIVATE_SLOT(d, void itemFetched(const Akonadi::Item &)) + Q_PRIVATE_SLOT(d, void queueError(const QString &)) + Q_PRIVATE_SLOT(d, void sendPercent(KJob *, unsigned long)) + Q_PRIVATE_SLOT(d, void sendResult(KJob *)) + Q_PRIVATE_SLOT(d, void emitStatusReady()) + //@endcond +}; + +#endif // MAILDISPATCHERAGENT_H diff --git a/agents/maildispatcher/maildispatcheragent.kcfg b/agents/maildispatcher/maildispatcheragent.kcfg new file mode 100644 index 00000000..922fd13c --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.kcfg @@ -0,0 +1,18 @@ + + + + + + + -1 + + + + -1 + + + diff --git a/agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml b/agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml new file mode 100644 index 00000000..917af22c --- /dev/null +++ b/agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/agents/maildispatcher/outboxqueue.cpp b/agents/maildispatcher/outboxqueue.cpp new file mode 100644 index 00000000..7a2c2cfb --- /dev/null +++ b/agents/maildispatcher/outboxqueue.cpp @@ -0,0 +1,455 @@ +/* + Copyright (c) 2009 Constantin Berzan + + 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 "outboxqueue.h" + +#include +#include +#include + +#include "maildispatcher_debug.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace Akonadi; +using namespace MailTransport; + +static const int OUTBOX_DISCOVERY_RETRIES = 3; // number of times we try to find or create the outbox +static const int OUTBOX_DISCOVERY_WAIT_TIME = 5000; // number of ms to wait before retrying + +/** + * @internal + */ +class OutboxQueue::Private +{ +public: + Private(OutboxQueue *qq) + : q(qq), + outbox(-1), + monitor(Q_NULLPTR), + futureTimer(Q_NULLPTR), + totalSize(0), + outboxDiscoveryRetries(0) + { + } + + OutboxQueue *const q; + + Collection outbox; + Monitor *monitor; + QList queue; + QSet futureItems; // keeps track of items removed in the meantime + QMultiMap futureMap; + QTimer *futureTimer; + qulonglong totalSize; + int outboxDiscoveryRetries; + +#if 0 + // If an item is modified externally between the moment we pass it to + // the MDA and the time the MDA marks it as sent, then we will get + // itemChanged() and may mistakenly re-add the item to the queue. + // So we ignore the item that we pass to the MDA, until the MDA finishes + // sending it. + Item currentItem; +#endif + // HACK: The above is not enough. + // Apparently change notifications are delayed sometimes (???) + // and we re-add an item long after it was sent. So keep a list of sent + // items. + // TODO debug and figure out why this happens. + QSet ignore; + + void initQueue(); + void addIfComplete(const Item &item); + + // Q_SLOTS: + void checkFuture(); + void collectionFetched(KJob *job); + void itemFetched(KJob *job); + void localFoldersChanged(); + void localFoldersRequestResult(KJob *job); + void itemAdded(const Item &item); + void itemChanged(const Item &item); + void itemMoved(const Item &item, const Collection &source, const Collection &dest); + void itemRemoved(const Item &item); + void itemProcessed(const Item &item, bool result); +}; + +void OutboxQueue::Private::initQueue() +{ + totalSize = 0; + queue.clear(); + + qCDebug(MAILDISPATCHER_LOG) << "Fetching items in collection" << outbox.id(); + ItemFetchJob *job = new ItemFetchJob(outbox); + job->fetchScope().fetchAllAttributes(); + job->fetchScope().fetchFullPayload(false); + connect(job, SIGNAL(result(KJob*)), q, SLOT(collectionFetched(KJob*))); +} + +void OutboxQueue::Private::addIfComplete(const Item &item) +{ + if (ignore.contains(item.id())) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is ignored."; + return; + } + + if (queue.contains(item)) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "already in queue!"; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute Address."; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute DispatchMode."; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute SentBehaviour."; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute Transport."; + return; + } + + if (!item.hasFlag(Akonadi::MessageFlags::Queued)) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "has no '$QUEUED' flag."; + return; + } + + const DispatchModeAttribute *dispatchModeAttribute = item.attribute(); + Q_ASSERT(dispatchModeAttribute); + if (dispatchModeAttribute->dispatchMode() == DispatchModeAttribute::Manual) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is queued to be sent manually."; + return; + } + + const TransportAttribute *transportAttribute = item.attribute(); + Q_ASSERT(transportAttribute); + if (transportAttribute->transport() == Q_NULLPTR) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "has invalid transport."; + return; + } + + const SentBehaviourAttribute *sentBehaviourAttribute = item.attribute(); + Q_ASSERT(sentBehaviourAttribute); + if (sentBehaviourAttribute->sentBehaviour() == SentBehaviourAttribute::MoveToCollection && + !sentBehaviourAttribute->moveToCollection().isValid()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "has invalid sent-mail collection."; + return; + } + + // This check requires fetchFullPayload. -> slow (?) + /* + if ( !item.hasPayload() ) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have KMime::Message::Ptr payload."; + return; + } + */ + + if (dispatchModeAttribute->dispatchMode() == DispatchModeAttribute::Automatic && + dispatchModeAttribute->sendAfter().isValid() && + dispatchModeAttribute->sendAfter() > QDateTime::currentDateTime()) { + // All the above was OK, so accept it for the future. + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is accepted to be sent in the future."; + futureMap.insert(dispatchModeAttribute->sendAfter(), item); + Q_ASSERT(!futureItems.contains(item)); + futureItems.insert(item); + checkFuture(); + return; + } + + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is accepted into the queue (size" << item.size() << ")."; + Q_ASSERT(!queue.contains(item)); + totalSize += item.size(); + queue.append(item); + Q_EMIT q->newItems(); +} + +void OutboxQueue::Private::checkFuture() +{ + qCDebug(MAILDISPATCHER_LOG) << "The future is here." << futureMap.count() << "items in futureMap."; + Q_ASSERT(futureTimer); + futureTimer->stop(); + // By default, re-check in one hour. + futureTimer->setInterval(60 * 60 * 1000); + + // Check items in ascending order of date. + while (!futureMap.isEmpty()) { + QMap::iterator it = futureMap.begin(); + qCDebug(MAILDISPATCHER_LOG) << "Item with due date" << it.key(); + if (it.key() > QDateTime::currentDateTime()) { + const int secs = QDateTime::currentDateTime().secsTo(it.key()) + 1; + qCDebug(MAILDISPATCHER_LOG) << "Future, in" << secs << "seconds."; + Q_ASSERT(secs >= 0); + if (secs < 60 * 60) { + futureTimer->setInterval(secs * 1000); + } + break; // all others are in the future too + } + if (!futureItems.contains(it.value())) { + qCDebug(MAILDISPATCHER_LOG) << "Item disappeared."; + } else { + qCDebug(MAILDISPATCHER_LOG) << "Due date is here. Queuing."; + addIfComplete(it.value()); + futureItems.remove(it.value()); + } + futureMap.erase(it); + } + + qCDebug(MAILDISPATCHER_LOG) << "Timer set to checkFuture again in" << futureTimer->interval() / 1000 << "seconds" + << "(that is" << futureTimer->interval() / 1000 / 60 << "minutes)."; + + futureTimer->start(); +} + +void OutboxQueue::Private::collectionFetched(KJob *job) +{ + if (job->error()) { + qCWarning(MAILDISPATCHER_LOG) << "Failed to fetch outbox collection. Queue will be empty until the outbox changes."; + return; + } + + const ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob); + qCDebug(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << "items."; + + foreach (const Item &item, fetchJob->items()) { + addIfComplete(item); + } +} + +void OutboxQueue::Private::itemFetched(KJob *job) +{ + if (job->error()) { + qCDebug(MAILDISPATCHER_LOG) << "Error fetching item:" << job->errorString() << ". Trying next item in queue."; + q->fetchOne(); + } + + const ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob); + if (fetchJob->items().count() != 1) { + qCDebug(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << ", expected 1. Trying next item in queue."; + q->fetchOne(); + } + + if (!fetchJob->items().isEmpty()) { + Q_EMIT q->itemReady(fetchJob->items().at(0)); + } +} + +void OutboxQueue::Private::localFoldersChanged() +{ + // Called on startup, and whenever the local folders change. + + if (SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::Outbox)) { + // Outbox is ready, init the queue from it. + const Collection collection = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Outbox); + Q_ASSERT(collection.isValid()); + + if (outbox != collection) { + monitor->setCollectionMonitored(outbox, false); + monitor->setCollectionMonitored(collection, true); + outbox = collection; + qCDebug(MAILDISPATCHER_LOG) << "Changed outbox to" << outbox.id(); + initQueue(); + } + } else { + // Outbox is not ready. Request it, since otherwise we will not know when + // new messages appear. + // (Note that we are a separate process, so we get no notification when + // MessageQueueJob requests the Outbox.) + monitor->setCollectionMonitored(outbox, false); + outbox = Collection(-1); + + SpecialMailCollectionsRequestJob *job = new SpecialMailCollectionsRequestJob(q); + job->requestDefaultCollection(SpecialMailCollections::Outbox); + connect(job, SIGNAL(result(KJob*)), q, SLOT(localFoldersRequestResult(KJob*))); + + qCDebug(MAILDISPATCHER_LOG) << "Requesting outbox folder."; + job->start(); + } + + // make sure we have a place to dump the sent mails as well + if (!SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { + SpecialMailCollectionsRequestJob *job = new SpecialMailCollectionsRequestJob(q); + job->requestDefaultCollection(SpecialMailCollections::SentMail); + + qCDebug(MAILDISPATCHER_LOG) << "Requesting sent-mail folder"; + job->start(); + } +} + +void OutboxQueue::Private::localFoldersRequestResult(KJob *job) +{ + if (job->error()) { + // We tried to create the outbox, but that failed. This could be because some + // other process, the mail app, for example, tried to create it at the + // same time. So try again, once or twice, but wait a little in between, longer + // each time. If we still haven't managed to create it after a few retries, + // error hard. + + if (++outboxDiscoveryRetries <= OUTBOX_DISCOVERY_RETRIES) { + const int timeout = OUTBOX_DISCOVERY_WAIT_TIME * outboxDiscoveryRetries; + qCWarning(MAILDISPATCHER_LOG) << "Failed to get outbox folder. Retrying in: " << timeout; + QTimer::singleShot(timeout, q, SLOT(localFoldersChanged())); + } else { + qCWarning(MAILDISPATCHER_LOG) << "Failed to get outbox folder. Giving up.";; + Q_EMIT q->error(i18n("Could not access the outbox folder (%1).", job->errorString())); + } + return; + } + + localFoldersChanged(); +} + +void OutboxQueue::Private::itemAdded(const Item &item) +{ + addIfComplete(item); +} + +void OutboxQueue::Private::itemChanged(const Item &item) +{ + addIfComplete(item); + // TODO: if the item is moved out of the outbox, will I get itemChanged? +} + +void OutboxQueue::Private::itemMoved(const Item &item, const Collection &source, const Collection &destination) +{ + if (source == outbox) { + itemRemoved(item); + } else if (destination == outbox) { + addIfComplete(item); + } +} + +void OutboxQueue::Private::itemRemoved(const Item &removedItem) +{ + // @p item has size=0, so get the size from our own copy. + const int index = queue.indexOf(removedItem); + if (index == -1) { + // Item was not in queue at all. + return; + } + + Item item(queue.takeAt(index)); + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "(size" << item.size() << ") was removed from the queue."; + totalSize -= item.size(); + + futureItems.remove(removedItem); +} + +void OutboxQueue::Private::itemProcessed(const Item &item, bool result) +{ + Q_ASSERT(ignore.contains(item.id())); + if (!result) { + // Give the user a chance to re-send the item if it failed. + ignore.remove(item.id()); + } +} + +OutboxQueue::OutboxQueue(QObject *parent) + : QObject(parent), + d(new Private(this)) +{ + d->monitor = new Monitor(this); + d->monitor->itemFetchScope().fetchAllAttributes(); + d->monitor->itemFetchScope().fetchFullPayload(false); + connect(d->monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), + this, SLOT(itemAdded(Akonadi::Item))); + connect(d->monitor, SIGNAL(itemChanged(Akonadi::Item,QSet)), + this, SLOT(itemChanged(Akonadi::Item))); + connect(d->monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), + this, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))); + connect(d->monitor, SIGNAL(itemRemoved(Akonadi::Item)), + this, SLOT(itemRemoved(Akonadi::Item))); + + connect(SpecialMailCollections::self(), SIGNAL(defaultCollectionsChanged()), this, SLOT(localFoldersChanged())); + d->localFoldersChanged(); + + d->futureTimer = new QTimer(this); + connect(d->futureTimer, SIGNAL(timeout()), this, SLOT(checkFuture())); + d->futureTimer->start(60 * 60 * 1000); // 1 hour +} + +OutboxQueue::~OutboxQueue() +{ + delete d; +} + +bool OutboxQueue::isEmpty() const +{ + return d->queue.isEmpty(); +} + +int OutboxQueue::count() const +{ + if (d->queue.count() == 0) { + // TODO Is this asking for too much? + Q_ASSERT(d->totalSize == 0); + } + return d->queue.count(); +} + +qulonglong OutboxQueue::totalSize() const +{ + return d->totalSize; +} + +void OutboxQueue::fetchOne() +{ + if (isEmpty()) { + qCDebug(MAILDISPATCHER_LOG) << "Empty queue."; + return; + } + + const Item item = d->queue.takeFirst(); + + d->totalSize -= item.size(); + Q_ASSERT(!d->ignore.contains(item.id())); + d->ignore.insert(item.id()); + + ItemFetchJob *job = new ItemFetchJob(item); + job->fetchScope().fetchAllAttributes(); + job->fetchScope().fetchFullPayload(); + connect(job, SIGNAL(result(KJob*)), this, SLOT(itemFetched(KJob*))); +} + +#include "moc_outboxqueue.cpp" diff --git a/agents/maildispatcher/outboxqueue.h b/agents/maildispatcher/outboxqueue.h new file mode 100644 index 00000000..721c5b43 --- /dev/null +++ b/agents/maildispatcher/outboxqueue.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2009 Constantin Berzan + + 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 OUTBOXQUEUE_H +#define OUTBOXQUEUE_H + +#include +#include + +#include + +class KJob; + +/** + * @short Monitors the outbox collection and provides a queue of messages for the MDA to send. + */ +class OutboxQueue : public QObject +{ + Q_OBJECT + friend class MailDispatcherAgent; + +public: + /** + * Creates a new outbox queue. + * + * @param parent The parent object. + */ + explicit OutboxQueue(QObject *parent = Q_NULLPTR); + + /** + * Destroys the outbox queue. + */ + virtual ~OutboxQueue(); + + /** + * Returns whether the queue is empty. + */ + bool isEmpty() const; + + /** + * Returns the number of items in the queue. + */ + int count() const; + + /** + * Returns the size (in bytes) of all items in the queue. + */ + qulonglong totalSize() const; + + /** + * Fetches an item and emits itemReady() when done. + */ + void fetchOne(); + +Q_SIGNALS: + void itemReady(const Akonadi::Item &item); + void newItems(); + void error(const QString &error); + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void checkFuture()) + Q_PRIVATE_SLOT(d, void collectionFetched(KJob *)) + Q_PRIVATE_SLOT(d, void itemFetched(KJob *)) + Q_PRIVATE_SLOT(d, void localFoldersChanged()) + Q_PRIVATE_SLOT(d, void localFoldersRequestResult(KJob *)) + Q_PRIVATE_SLOT(d, void itemAdded(Akonadi::Item)) + Q_PRIVATE_SLOT(d, void itemChanged(Akonadi::Item)) + Q_PRIVATE_SLOT(d, void itemMoved(Akonadi::Item, Akonadi::Collection, Akonadi::Collection)) + Q_PRIVATE_SLOT(d, void itemRemoved(Akonadi::Item)) + Q_PRIVATE_SLOT(d, void itemProcessed(Akonadi::Item, bool)) + //@endcond +}; + +#endif diff --git a/agents/maildispatcher/sendjob.cpp b/agents/maildispatcher/sendjob.cpp new file mode 100644 index 00000000..d996c6ed --- /dev/null +++ b/agents/maildispatcher/sendjob.cpp @@ -0,0 +1,486 @@ +/* + Copyright (c) 2009 Constantin Berzan + + 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 "sendjob.h" + +#include "storeresultjob.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "maildispatcher_debug.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Akonadi; +using namespace KMime; +using namespace MailTransport; + +/** + * Private class that helps to provide binary compatibility between releases. + * @internal + */ +class SendJob::Private +{ +public: + Private(const Item &itm, SendJob *qq) + : q(qq), + item(itm), + currentJob(Q_NULLPTR), + interface(Q_NULLPTR), + mailfilterInterface(Q_NULLPTR), + aborting(false) + { + } + + SendJob *const q; + Item item; + KJob *currentJob; + QString resourceId; + QDBusInterface *interface; + QDBusInterface *mailfilterInterface; + bool aborting; + + void doAkonadiTransport(); + void doTraditionalTransport(); + void doPostJob(bool transportSuccess, const QString &transportMessage); + void storeResult(bool success, const QString &message = QString()); + void abortPostJob(); + bool filterItem(int filterset); + + // slots + void doTransport(); + void transportPercent(KJob *job, unsigned long percent); + void transportResult(KJob *job); + void resourceProgress(const AgentInstance &instance); + void resourceResult(qlonglong itemId, int result, const QString &message); + void postJobResult(KJob *job); + void doEmitResult(KJob *job); + void slotSentMailCollectionFetched(KJob *job); +}; + +void SendJob::Private::doTransport() +{ + qCDebug(MAILDISPATCHER_LOG) << "Transporting message."; + + if (aborting) { + qCDebug(MAILDISPATCHER_LOG) << "Marking message as aborted."; + q->setError(UserDefinedError); + q->setErrorText(i18n("Message sending aborted.")); + storeResult(false, i18n("Message sending aborted.")); + return; + } + + // Is it an Akonadi transport or a traditional one? + const TransportAttribute *transportAttribute = item.attribute(); + Q_ASSERT(transportAttribute); + if (!transportAttribute->transport()) { + storeResult(false, i18n("Could not initiate message transport. Possibly invalid transport.")); + return; + } + + const TransportType type = transportAttribute->transport()->transportType(); + if (!type.isValid()) { + storeResult(false, i18n("Could not send message. Invalid transport.")); + return; + } + + if (!filterItem(8)) { //BeforeOutbound + return; + } + + if (type.type() == Transport::EnumType::Akonadi) { + // Send the item directly to the resource that will send it. + resourceId = transportAttribute->transport()->host(); + doAkonadiTransport(); + } else { + // Use a traditional transport job. + doTraditionalTransport(); + } +} + +void SendJob::Private::doAkonadiTransport() +{ + Q_ASSERT(!resourceId.isEmpty()); + Q_ASSERT(interface == 0); + + interface = new QDBusInterface( + QLatin1String("org.freedesktop.Akonadi.Resource.") + resourceId, + QStringLiteral("/Transport"), QStringLiteral("org.freedesktop.Akonadi.Resource.Transport"), + KDBusConnectionPool::threadConnection(), q); + + if (!interface->isValid()) { + storeResult(false, i18n("Failed to get D-Bus interface of resource %1.", resourceId)); + delete interface; + interface = Q_NULLPTR; + return; + } + + // Signals. + QObject::connect(AgentManager::self(), SIGNAL(instanceProgressChanged(Akonadi::AgentInstance)), + q, SLOT(resourceProgress(Akonadi::AgentInstance))); + QObject::connect(interface, SIGNAL(transportResult(qlonglong,int,QString)), + q, SLOT(resourceResult(qlonglong,int,QString))); + + // Start sending. + const QDBusReply reply = interface->call(QStringLiteral("send"), item.id()); + if (!reply.isValid()) { + storeResult(false, i18n("Invalid D-Bus reply from resource %1.", resourceId)); + return; + } +} + +void SendJob::Private::doTraditionalTransport() +{ + const TransportAttribute *transportAttribute = item.attribute(); + TransportJob *job = TransportManager::self()->createTransportJob(transportAttribute->transportId()); + + Q_ASSERT(job); + Q_ASSERT(currentJob == 0); + + currentJob = job; + + // Message. + Q_ASSERT(item.hasPayload()); + const Message::Ptr message = item.payload(); + bool needAssemble = false; + if (message->hasHeader("Bcc")) { + message->removeHeader("Bcc"); + needAssemble = true; + } + if (message->hasHeader("X-KMail-Identity")) { + message->removeHeader("X-KMail-Identity"); + needAssemble = true; + } + if (message->hasHeader("X-KMail-Dictionary")) { + message->removeHeader("X-KMail-Dictionary"); + needAssemble = true; + } + + if (needAssemble) { + message->assemble(); + } + const QByteArray content = message->encodedContent(true) + "\r\n"; + Q_ASSERT(!content.isEmpty()); + + // Addresses. + const AddressAttribute *addressAttribute = item.attribute(); + Q_ASSERT(addressAttribute); + + job->setData(content); + job->setSender(addressAttribute->from()); + job->setTo(addressAttribute->to()); + job->setCc(addressAttribute->cc()); + job->setBcc(addressAttribute->bcc()); + + // Signals. + connect(job, SIGNAL(result(KJob*)), + q, SLOT(transportResult(KJob*))); + connect(job, SIGNAL(percent(KJob*,ulong)), + q, SLOT(transportPercent(KJob*,ulong))); + + job->start(); +} + +void SendJob::Private::transportPercent(KJob *job, unsigned long) +{ + Q_ASSERT(currentJob == job); + qCDebug(MAILDISPATCHER_LOG) << "Processed amount" << job->processedAmount(KJob::Bytes) + << "total amount" << job->totalAmount(KJob::Bytes); + + q->setTotalAmount(KJob::Bytes, job->totalAmount(KJob::Bytes)); // Is not set at the time of start(). + q->setProcessedAmount(KJob::Bytes, job->processedAmount(KJob::Bytes)); +} + +void SendJob::Private::transportResult(KJob *job) +{ + Q_ASSERT(currentJob == job); + currentJob = Q_NULLPTR; + doPostJob(!job->error(), job->errorString()); +} + +void SendJob::Private::resourceProgress(const AgentInstance &instance) +{ + if (!interface) { + // We might have gotten a very late signal. + qCWarning(MAILDISPATCHER_LOG) << "called but no resource job running!"; + return; + } + + if (instance.identifier() == resourceId) { + // This relies on the resource's progress representing the progress of + // sending this item. + q->setPercent(instance.progress()); + } +} + +void SendJob::Private::resourceResult(qlonglong itemId, int result, + const QString &message) +{ + Q_UNUSED(itemId); + Q_ASSERT(interface); + delete interface; // So that abort() knows the transport job is over. + interface = Q_NULLPTR; + + const TransportResourceBase::TransportResult transportResult = + static_cast(result); + + const bool success = (transportResult == TransportResourceBase::TransportSucceeded); + + Q_ASSERT(itemId == item.id()); + doPostJob(success, message); +} + +void SendJob::Private::abortPostJob() +{ + // We were unlucky and LocalFolders is recreating its stuff right now. + // We will not wait for it. + qCWarning(MAILDISPATCHER_LOG) << "Default sent mail collection unavailable, not moving the mail after sending."; + q->setError(UserDefinedError); + q->setErrorText(i18n("Default sent-mail folder unavailable. Keeping message in outbox.")); + storeResult(false, q->errorString()); +} + +void SendJob::Private::doPostJob(bool transportSuccess, const QString &transportMessage) +{ + qCDebug(MAILDISPATCHER_LOG) << "success" << transportSuccess << "message" << transportMessage; + + if (!transportSuccess) { + qCDebug(MAILDISPATCHER_LOG) << "Error transporting."; + q->setError(UserDefinedError); + + const QString error = aborting ? i18n("Message transport aborted.") + : i18n("Failed to transport message."); + + q->setErrorText(error + QLatin1Char(' ') + transportMessage); + storeResult(false, q->errorString()); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Success transporting."; + + // Delete or move to sent-mail. + const SentBehaviourAttribute *attribute = item.attribute(); + Q_ASSERT(attribute); + + if (attribute->sentBehaviour() == SentBehaviourAttribute::Delete) { + qCDebug(MAILDISPATCHER_LOG) << "Deleting item from outbox."; + currentJob = new ItemDeleteJob(item); + QObject::connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(postJobResult(KJob*))); + } else { + if (attribute->sentBehaviour() == SentBehaviourAttribute::MoveToDefaultSentCollection) { + if (SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { + currentJob = new ItemMoveJob(item, SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail), q); + QObject::connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(postJobResult(KJob*))); + } else { + abortPostJob(); + } + } else { + qCDebug(MAILDISPATCHER_LOG) << "sentBehaviour=" << attribute->sentBehaviour() << "using collection from attribute"; + currentJob = new CollectionFetchJob(attribute->moveToCollection(), Akonadi::CollectionFetchJob::Base); + QObject::connect(currentJob, SIGNAL(result(KJob*)), + q, SLOT(slotSentMailCollectionFetched(KJob*))); + + } + } + } +} + +bool SendJob::Private::filterItem(int filterset) +{ + Q_ASSERT(mailfilterInterface == 0); + + // TODO: create on stack + mailfilterInterface = new QDBusInterface( + QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent"), + QStringLiteral("/MailFilterAgent"), QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent"), + KDBusConnectionPool::threadConnection(), q); + + if (!mailfilterInterface->isValid()) { + storeResult(false, i18n("Failed to get D-Bus interface of mailfilteragent.")); + delete mailfilterInterface; + mailfilterInterface = Q_NULLPTR; + return false; + } + + //Outbound = 0x2 + const QDBusReply reply = mailfilterInterface->call(QStringLiteral("filterItem"), item.id(), filterset, QString()); + if (!reply.isValid()) { + storeResult(false, i18n("Invalid D-Bus reply from mailfilteragent")); + delete mailfilterInterface; + mailfilterInterface = Q_NULLPTR; + return false; + } + + delete mailfilterInterface; + mailfilterInterface = Q_NULLPTR; + return true; +} + +void SendJob::Private::slotSentMailCollectionFetched(KJob *job) +{ + Akonadi::Collection fetchCol; + bool ok = false; + if (!job->error()) { + const CollectionFetchJob *const fetchJob = qobject_cast(job); + if (!fetchJob->collections().isEmpty()) { + fetchCol = fetchJob->collections().at(0); + ok = true; + } + } + if (!ok) { + if (!SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { + abortPostJob(); + return; + } + fetchCol = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail); + } + currentJob = new ItemMoveJob(item, fetchCol, q); + QObject::connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(postJobResult(KJob*))); +} + +void SendJob::Private::postJobResult(KJob *job) +{ + Q_ASSERT(currentJob == job); + currentJob = Q_NULLPTR; + const SentBehaviourAttribute *attribute = item.attribute(); + Q_ASSERT(attribute); + + if (job->error()) { + qCDebug(MAILDISPATCHER_LOG) << "Error deleting or moving to sent-mail."; + + QString errorString; + switch (attribute->sentBehaviour()) { + case SentBehaviourAttribute::Delete: + errorString = + i18n("Sending succeeded, but failed to remove the message from the outbox."); + break; + default: + errorString = + i18n("Sending succeeded, but failed to move the message to the sent-mail folder."); + break; + } + q->setError(UserDefinedError); + q->setErrorText(errorString + QLatin1Char(' ') + job->errorString()); + storeResult(false, q->errorString()); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Success deleting or moving to sent-mail."; + if (!filterItem(2)) { //Outbound + return; + } + if (attribute->sentBehaviour() == SentBehaviourAttribute::Delete) { + q->emitResult(); + } else { + storeResult(true); + } + } +} + +void SendJob::Private::storeResult(bool success, const QString &message) +{ + qCDebug(MAILDISPATCHER_LOG) << "success" << success << "message" << message; + + Q_ASSERT(currentJob == 0); + currentJob = new StoreResultJob(item, success, message); + connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(doEmitResult(KJob*))); +} + +void SendJob::Private::doEmitResult(KJob *job) +{ + Q_ASSERT(currentJob == job); + currentJob = Q_NULLPTR; + + if (job->error()) { + qCWarning(MAILDISPATCHER_LOG) << "Error storing result."; + q->setError(UserDefinedError); + q->setErrorText(q->errorString() + QLatin1Char(' ') + i18n("Failed to store result in item.") + QLatin1Char(' ') + job->errorString()); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Success storing result."; + // It is still possible that the transport failed. + StoreResultJob *srJob = static_cast(job); + if (!srJob->success()) { + q->setError(UserDefinedError); + q->setErrorText(srJob->message()); + } + } + + q->emitResult(); +} + +SendJob::SendJob(const Item &item, QObject *parent) + : KJob(parent), + d(new Private(item, this)) +{ +} + +SendJob::~SendJob() +{ + delete d; +} + +void SendJob::start() +{ + QTimer::singleShot(0, this, SLOT(doTransport())); +} + +void SendJob::setMarkAborted() +{ + Q_ASSERT(!d->aborting); + d->aborting = true; +} + +void SendJob::abort() +{ + setMarkAborted(); + + if (dynamic_cast(d->currentJob)) { + qCDebug(MAILDISPATCHER_LOG) << "Abort called, active transport job."; + // Abort transport. + d->currentJob->kill(KJob::EmitResult); + } else if (d->interface != Q_NULLPTR) { + qCDebug(MAILDISPATCHER_LOG) << "Abort called, propagating to resource."; + // Abort resource doing transport. + AgentInstance instance = AgentManager::self()->instance(d->resourceId); + instance.abortCurrentTask(); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Abort called, but no transport job is active."; + // Either transport has not started, in which case doTransport will + // mark the item as aborted, or the item has already been sent, in which + // case there is nothing we can do. + } +} + +#include "moc_sendjob.cpp" diff --git a/agents/maildispatcher/sendjob.h b/agents/maildispatcher/sendjob.h new file mode 100644 index 00000000..b2137ba9 --- /dev/null +++ b/agents/maildispatcher/sendjob.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2009 Constantin Berzan + + 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 SENDJOB_H +#define SENDJOB_H + +#include + +namespace Akonadi +{ +class Item; +} + +/** + * @short A job to send a mail + * + * This class takes a prevalidated Item with all the required attributes, + * sends it using MailTransport, and then stores the result of the sending + * operation in the item. + */ +class SendJob : public KJob +{ + Q_OBJECT + +public: + /** + * Creates a new send job. + * + * @param item The item to send. + * @param parent The parent object. + */ + explicit SendJob(const Akonadi::Item &item, QObject *parent = Q_NULLPTR); + + /** + * Destroys the send job. + */ + virtual ~SendJob(); + + /** + * Starts the job. + */ + void start() Q_DECL_OVERRIDE; + + /** + * If this function is called before the job is started, the SendJob will + * just mark the item as aborted, instead of sending it. + * Do not call this function more than once. + */ + void setMarkAborted(); + + /** + * Aborts sending the item. + * + * This will give the item an ErrorAttribute of "aborted". + * (No need to call setMarkAborted() if you call abort().) + */ + void abort(); + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void doTransport()) + Q_PRIVATE_SLOT(d, void transportPercent(KJob *, unsigned long)) + Q_PRIVATE_SLOT(d, void transportResult(KJob *)) + Q_PRIVATE_SLOT(d, void resourceProgress(const Akonadi::AgentInstance &)) + Q_PRIVATE_SLOT(d, void resourceResult(qlonglong, int, const QString &)) + Q_PRIVATE_SLOT(d, void postJobResult(KJob *)) + Q_PRIVATE_SLOT(d, void doEmitResult(KJob *)) + Q_PRIVATE_SLOT(d, void slotSentMailCollectionFetched(KJob *)) + //@endcond +}; + +#endif diff --git a/agents/maildispatcher/sentactionhandler.cpp b/agents/maildispatcher/sentactionhandler.cpp new file mode 100644 index 00000000..6073a31c --- /dev/null +++ b/agents/maildispatcher/sentactionhandler.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Tobias Koenig + + 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 "sentactionhandler.h" + +#include +#include +#include +#include "maildispatcher_debug.h" + +using namespace MailTransport; + +SentActionHandler::SentActionHandler(QObject *parent) + : QObject(parent) +{ +} + +void SentActionHandler::runAction(const SentActionAttribute::Action &action) +{ + if (action.type() == SentActionAttribute::Action::MarkAsReplied || + action.type() == SentActionAttribute::Action::MarkAsForwarded) { + + const Akonadi::Item item(action.value().toLongLong()); + Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); + connect(job, &Akonadi::ItemFetchJob::result, this, &SentActionHandler::itemFetchResult); + job->setProperty("type", static_cast(action.type())); + } +} + +void SentActionHandler::itemFetchResult(KJob *job) +{ + if (job->error()) { + qCWarning(MAILDISPATCHER_LOG) << job->errorText(); + return; + } + + Akonadi::ItemFetchJob *fetchJob = qobject_cast(job); + if (fetchJob->items().isEmpty()) { + return; + } + + Akonadi::Item item = fetchJob->items().at(0); + + const SentActionAttribute::Action::Type type = static_cast(job->property("type").toInt()); + if (type == SentActionAttribute::Action::MarkAsReplied) { + item.setFlag(Akonadi::MessageFlags::Replied); + } else if (type == SentActionAttribute::Action::MarkAsForwarded) { + item.setFlag(Akonadi::MessageFlags::Forwarded); + } + + Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item); + modifyJob->setIgnorePayload(true); +} + diff --git a/agents/maildispatcher/sentactionhandler.h b/agents/maildispatcher/sentactionhandler.h new file mode 100644 index 00000000..91a14191 --- /dev/null +++ b/agents/maildispatcher/sentactionhandler.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Tobias Koenig + + 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 SENTACTIONHANDLER_H +#define SENTACTIONHANDLER_H + +#include + +#include + +class KJob; + +class SentActionHandler : public QObject +{ + Q_OBJECT + +public: + explicit SentActionHandler(QObject *parent = Q_NULLPTR); + + void runAction(const MailTransport::SentActionAttribute::Action &action); + +private Q_SLOTS: + void itemFetchResult(KJob *job); +}; + +#endif diff --git a/agents/maildispatcher/settings.kcfgc b/agents/maildispatcher/settings.kcfgc new file mode 100644 index 00000000..8c813ddb --- /dev/null +++ b/agents/maildispatcher/settings.kcfgc @@ -0,0 +1,8 @@ +File=maildispatcheragent.kcfg +ClassName=Settings +Mutators=true +ItemAccessors=true +SetUserTexts=true +Singleton=true +#IncludeFiles= +GlobalEnums=true diff --git a/agents/maildispatcher/settings.ui b/agents/maildispatcher/settings.ui new file mode 100644 index 00000000..e1868bb4 --- /dev/null +++ b/agents/maildispatcher/settings.ui @@ -0,0 +1,77 @@ + + + Till Adam <adam@kde.org> + ConfigDialog + + + + 0 + 0 + 400 + 250 + + + + Mail Dispatcher Agent Settings + + + + + + Select the collection to be used as outbox: + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + Select the collection to move sent messages into: + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + Qt::Vertical + + + + 20 + 13 + + + + + + + + + Akonadi::CollectionRequester + QFrame +
akonadi/collectionrequester.h
+ 1 +
+
+ + +
diff --git a/agents/maildispatcher/storeresultjob.cpp b/agents/maildispatcher/storeresultjob.cpp new file mode 100644 index 00000000..6f056818 --- /dev/null +++ b/agents/maildispatcher/storeresultjob.cpp @@ -0,0 +1,141 @@ +/* + Copyright (c) 2009 Constantin Berzan + + 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 "storeresultjob.h" + +#include +#include +#include +#include +#include "maildispatcher_debug.h" +#include +#include +#include + +using namespace Akonadi; +using namespace MailTransport; + +/** + * @internal + */ +class StoreResultJob::Private +{ +public: + Private(StoreResultJob *qq) + : q(qq), success(false) + { + } + + StoreResultJob *const q; + Item item; + bool success; + QString message; + + // Q_SLOTS: + void fetchDone(KJob *job); + void modifyDone(KJob *job); +}; + +void StoreResultJob::Private::fetchDone(KJob *job) +{ + if (job->error()) { + return; + } + + qCDebug(MAILDISPATCHER_LOG); + + const ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob); + if (fetchJob->items().count() != 1) { + qCritical() << "Fetched" << fetchJob->items().count() << "items, expected 1."; + q->setError(Unknown); + q->setErrorText(i18n("Failed to fetch item.")); + q->commit(); + return; + } + + // Store result in item. + Item item = fetchJob->items().at(0); + if (success) { + item.clearFlag(Akonadi::MessageFlags::Queued); + item.setFlag(Akonadi::MessageFlags::Sent); + item.setFlag(Akonadi::MessageFlags::Seen); + item.removeAttribute(); + } else { + item.setFlag(Akonadi::MessageFlags::HasError); + ErrorAttribute *errorAttribute = new ErrorAttribute(message); + item.addAttribute(errorAttribute); + + // If dispatch failed, set the DispatchModeAttribute to Manual. + // Otherwise, the user will *never* be able to try sending the mail again, + // as Send Queued Messages will ignore it. + if (item.hasAttribute()) { + item.attribute()->setDispatchMode(MailTransport::DispatchModeAttribute::Manual); + } else { + item.addAttribute(new DispatchModeAttribute(MailTransport::DispatchModeAttribute::Manual)); + } + } + + ItemModifyJob *modifyJob = new ItemModifyJob(item, q); + QObject::connect(modifyJob, SIGNAL(result(KJob*)), q, SLOT(modifyDone(KJob*))); +} + +void StoreResultJob::Private::modifyDone(KJob *job) +{ + if (job->error()) { + return; + } + + qCDebug(MAILDISPATCHER_LOG); + + q->commit(); +} + +StoreResultJob::StoreResultJob(const Item &item, bool success, const QString &message, QObject *parent) + : TransactionSequence(parent), + d(new Private(this)) +{ + d->item = item; + d->success = success; + d->message = message; +} + +StoreResultJob::~StoreResultJob() +{ + delete d; +} + +void StoreResultJob::doStart() +{ + // Fetch item in case it was modified elsewhere. + ItemFetchJob *job = new ItemFetchJob(d->item, this); + connect(job, SIGNAL(result(KJob*)), this, SLOT(fetchDone(KJob*))); +} + +bool StoreResultJob::success() const +{ + return d->success; +} + +QString StoreResultJob::message() const +{ + return d->message; +} + +#include "moc_storeresultjob.cpp" diff --git a/agents/maildispatcher/storeresultjob.h b/agents/maildispatcher/storeresultjob.h new file mode 100644 index 00000000..34269912 --- /dev/null +++ b/agents/maildispatcher/storeresultjob.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2009 Constantin Berzan + + 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 STORERESULTJOB_H +#define STORERESULTJOB_H + +#include + +#include + +namespace Akonadi +{ +class Item; +} + +/** + * This class stores the result of a StoreResultJob in an item. + * First, it removes the 'queued' flag. + * After that, if the result was success, it stores the 'sent' flag. + * If the result was failure, it stores the 'error' flag and an ErrorAttribute. + */ +class StoreResultJob : public Akonadi::TransactionSequence +{ + Q_OBJECT + +public: + /** + * Creates a new store result job. + * + * @param item The item to store. + * @param success Whether the mail could be dispatched or not. + * @param message An error message in case the mail could not be dispatched. + * @param parent The parent object. + */ + explicit StoreResultJob(const Akonadi::Item &item, bool success, const QString &message, QObject *parent = Q_NULLPTR); + + /** + * Destroys the store result job. + */ + virtual ~StoreResultJob(); + + bool success() const; + QString message() const; + +protected: + // reimpl from TransactionSequence + void doStart() Q_DECL_OVERRIDE; + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void fetchDone(KJob *job)) + Q_PRIVATE_SLOT(d, void modifyDone(KJob *job)) + //@endcond +}; + +#endif diff --git a/agents/migration/CMakeLists.txt b/agents/migration/CMakeLists.txt new file mode 100644 index 00000000..ca14b87d --- /dev/null +++ b/agents/migration/CMakeLists.txt @@ -0,0 +1,43 @@ + +include_directories( + ${kdepim-runtime_SOURCE_DIR}/migration + ${kdepim-runtime_SOURCE_DIR} +) + +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_migration_agent\") + +kde_enable_exceptions() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +set(migrationagent_SRCS + migrationagent.cpp + migrationstatuswidget.cpp + migrationexecutor.cpp + migrationscheduler.cpp + autotests/dummymigrator.cpp +) + +add_executable(akonadi_migration_agent ${migrationagent_SRCS}) + +if( APPLE ) + set_target_properties(akonadi_migration_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_migration_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.migrationagent") + set_target_properties(akonadi_migration_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Migrationagent") +endif () + +target_link_libraries(akonadi_migration_agent + gidmigration + KF5::AkonadiCore + KF5::AkonadiAgentBase + KF5::Contacts + KF5::WindowSystem + KF5::JobWidgets +) + +install(TARGETS akonadi_migration_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES migrationagent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}//akonadi/agents") + +if(BUILD_TESTING) + add_subdirectory(autotests) +endif() diff --git a/agents/migration/Messages.sh b/agents/migration/Messages.sh new file mode 100755 index 00000000..dd6f739c --- /dev/null +++ b/agents/migration/Messages.sh @@ -0,0 +1,2 @@ +#! /bin/sh +$XGETTEXT *.cpp -o $podir/akonadi_migration_agent.pot diff --git a/agents/migration/autotests/CMakeLists.txt b/agents/migration/autotests/CMakeLists.txt new file mode 100644 index 00000000..7bda437f --- /dev/null +++ b/agents/migration/autotests/CMakeLists.txt @@ -0,0 +1,12 @@ + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +add_executable(schedulertest schedulertest.cpp ../migrationscheduler.cpp ../migrationexecutor.cpp) +target_link_libraries(schedulertest + gidmigration + KF5::AkonadiCore + Qt5::Test +) +add_test(schedulertest schedulertest) diff --git a/agents/migration/autotests/dummymigrator.cpp b/agents/migration/autotests/dummymigrator.cpp new file mode 100644 index 00000000..1d4a1f05 --- /dev/null +++ b/agents/migration/autotests/dummymigrator.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#include "dummymigrator.h" +#include +#include + +DummyMigrator::DummyMigrator(const QString &identifier) + : MigratorBase(QLatin1String("dummymigrator") + identifier, QString(), QString()) +{} + +QString DummyMigrator::displayName() const +{ + return QStringLiteral("dummymigrator"); +} + +void DummyMigrator::startWork() +{ + qDebug(); + QTimer::singleShot(10000, this, &DummyMigrator::onTimerElapsed); +} + +void DummyMigrator::onTimerElapsed() +{ + qDebug(); + setMigrationState(Complete); +} + +bool DummyMigrator::shouldAutostart() const +{ + return true; +} + +bool DummyMigrator::canStart() +{ + return true; +} + +void DummyMigrator::pause() +{ + qDebug(); + MigratorBase::pause(); +} + +void DummyMigrator::abort() +{ + qDebug(); + MigratorBase::abort(); +} + diff --git a/agents/migration/autotests/dummymigrator.h b/agents/migration/autotests/dummymigrator.h new file mode 100644 index 00000000..30a7ec77 --- /dev/null +++ b/agents/migration/autotests/dummymigrator.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#ifndef DUMMYMIGRATOR_H +#define DUMMYMIGRATOR_H + +#include + +/** + * Dummy migrator that simply completes after 10s and always autostarts. + * Add to the scheduler to play with the migrationagent. + */ +class DummyMigrator : public MigratorBase +{ + Q_OBJECT +public: + explicit DummyMigrator(const QString &identifier); + + QString displayName() const Q_DECL_OVERRIDE; + void startWork() Q_DECL_OVERRIDE; + + bool shouldAutostart() const Q_DECL_OVERRIDE; + bool canStart() Q_DECL_OVERRIDE; + void pause() Q_DECL_OVERRIDE; + + void abort() Q_DECL_OVERRIDE; +private Q_SLOTS: + void onTimerElapsed(); +}; + +#endif \ No newline at end of file diff --git a/agents/migration/autotests/schedulertest.cpp b/agents/migration/autotests/schedulertest.cpp new file mode 100644 index 00000000..11d849cf --- /dev/null +++ b/agents/migration/autotests/schedulertest.cpp @@ -0,0 +1,286 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#include +#include +#include +#include + +#include "../migrationscheduler.h" +#include + +Q_DECLARE_METATYPE(QModelIndex) + +class Testmigrator: public MigratorBase +{ + Q_OBJECT +public: + explicit Testmigrator(const QString &identifier, QObject *parent = Q_NULLPTR): + MigratorBase(QLatin1String("testmigrator") + identifier, QString(), QString(), parent), mAutostart(false) + {} + + QString displayName() const Q_DECL_OVERRIDE + { + return QStringLiteral("name"); + } + + void startWork() Q_DECL_OVERRIDE + {} + + void abort() Q_DECL_OVERRIDE { + setMigrationState(Aborted); + } + + virtual void complete() + { + setMigrationState(Complete); + } + + bool shouldAutostart() const Q_DECL_OVERRIDE + { + return mAutostart; + } + + void pause() Q_DECL_OVERRIDE { + setMigrationState(Paused); + } + + void resume() Q_DECL_OVERRIDE { + setMigrationState(InProgress); + } + + bool mAutostart; +}; + +class TestJobTracker : public KJobTrackerInterface +{ +public: + TestJobTracker() : mPercent(0) + {} + + void registerJob(KJob *job) Q_DECL_OVERRIDE { + KJobTrackerInterface::registerJob(job); + mJobs << job; + } + + void unregisterJob(KJob *job) Q_DECL_OVERRIDE { + mJobs.removeAll(job); + } + + void finished(KJob *job) Q_DECL_OVERRIDE { + mJobs.removeAll(job); + } + + void percent(KJob *job, long unsigned int percent) Q_DECL_OVERRIDE { + Q_UNUSED(job); + mPercent = percent; + } + + QList mJobs; + int mPercent; +}; + +class SchedulerTest: public QObject +{ + Q_OBJECT +private Q_SLOTS: + + void initTestcase() + { + qRegisterMetaType(); + } + + void testInsertRow() + { + MigrationScheduler scheduler; + QAbstractItemModel &model(scheduler.model()); + + QCOMPARE(model.rowCount(), 0); + + QSignalSpy rowsInsertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); + QVERIFY(rowsInsertedSpy.isValid()); + + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(rowsInsertedSpy.count(), 1); + + QVERIFY(model.index(0, 0).isValid()); + QVERIFY(!model.index(1, 0).isValid()); + + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id2")))); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(rowsInsertedSpy.count(), 2); + } + + void testDisplayName() + { + MigrationScheduler scheduler; + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + QAbstractItemModel &model(scheduler.model()); + QCOMPARE(model.data(model.index(0, 0)).toString(), QStringLiteral("name")); + } + + void testStartStop() + { + MigrationScheduler scheduler; + QSharedPointer migrator(new Testmigrator(QStringLiteral("id"))); + scheduler.addMigrator(migrator); + + scheduler.start(migrator->identifier()); + QCOMPARE(migrator->migrationState(), MigratorBase::InProgress); + + scheduler.abort(migrator->identifier()); + QCOMPARE(migrator->migrationState(), MigratorBase::Aborted); + } + + void testNoDuplicates() + { + MigrationScheduler scheduler; + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + QAbstractItemModel &model(scheduler.model()); + QCOMPARE(model.rowCount(), 1); + } + + void testMigrationStateChanged() + { + MigrationScheduler scheduler; + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id1")))); + QSharedPointer migrator(new Testmigrator(QStringLiteral("id2"))); + scheduler.addMigrator(migrator); + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id3")))); + QAbstractItemModel &model(scheduler.model()); + + QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + QVERIFY(spy.isValid()); + migrator->start(); + + QCOMPARE(spy.count(), 1); + const QVariantList args = spy.takeFirst(); + QCOMPARE(args.at(0).value().row(), 1); + QCOMPARE(args.at(1).value().row(), 1); + } + + void testRunMultiple() + { + MigrationScheduler scheduler; + + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + scheduler.addMigrator(m1); + + QSharedPointer m2(new Testmigrator(QStringLiteral("id2"))); + scheduler.addMigrator(m2); + + scheduler.start(m1->identifier()); + scheduler.start(m2->identifier()); + + QCOMPARE(m1->migrationState(), MigratorBase::InProgress); + QCOMPARE(m2->migrationState(), MigratorBase::InProgress); + } + + void testRunAutostart() + { + MigrationScheduler scheduler; + + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + + QSharedPointer m2(new Testmigrator(QStringLiteral("id2"))); + m2->mAutostart = true; + scheduler.addMigrator(m2); + + QCOMPARE(m1->migrationState(), MigratorBase::InProgress); + qDebug() << m2->migrationState(); + QCOMPARE(m2->migrationState(), MigratorBase::None); + m1->complete(); + QCOMPARE(m2->migrationState(), MigratorBase::InProgress); + + } + + void testJobTracker() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + + QCOMPARE(jobTracker.mJobs.size(), 1); + + m1->complete(); + + //Give the job some time to delete itself + QTest::qWait(500); + + QCOMPARE(jobTracker.mJobs.size(), 0); + } + + void testSuspend() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + jobTracker.mJobs.first()->suspend(); + QCOMPARE(m1->migrationState(), MigratorBase::Paused); + jobTracker.mJobs.first()->resume(); + QCOMPARE(m1->migrationState(), MigratorBase::InProgress); + } + + /* + * Even if the migrator doesn't implement suspend, the executor suspends after completing the current job and waits with starting the second job. + */ + void testJobFinishesDuringSuspend() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + QSharedPointer m2(new Testmigrator(QStringLiteral("id2"))); + m2->mAutostart = true; + scheduler.addMigrator(m2); + jobTracker.mJobs.first()->suspend(); + m1->complete(); + QCOMPARE(m2->migrationState(), MigratorBase::None); + jobTracker.mJobs.first()->resume(); + QCOMPARE(m2->migrationState(), MigratorBase::InProgress); + } + + void testProgressReporting() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + QCOMPARE(jobTracker.mPercent, 0); + m1->complete(); + QCOMPARE(jobTracker.mPercent, 100); + } + +}; + +QTEST_MAIN(SchedulerTest) + +#include "schedulertest.moc" diff --git a/agents/migration/migrationagent.cpp b/agents/migration/migrationagent.cpp new file mode 100644 index 00000000..71e9af10 --- /dev/null +++ b/agents/migration/migrationagent.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ +#include "migrationagent.h" + +#include "migrationstatuswidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Akonadi +{ + +MigrationAgent::MigrationAgent(const QString &id) + : AgentBase(id), + mScheduler(new KUiServerJobTracker) +{ + KLocalizedString::setApplicationDomain("akonadi_migration_agent"); + mScheduler.addMigrator(QSharedPointer(new GidMigrator(KContacts::Addressee::mimeType()))); +} + +void MigrationAgent::configure(WId windowId) +{ + QDialog *dlg = new QDialog(); + QVBoxLayout *topLayout = new QVBoxLayout; + dlg->setLayout(topLayout); + + MigrationStatusWidget *widget = new MigrationStatusWidget(mScheduler, dlg); + topLayout->addWidget(widget); + dlg->setAttribute(Qt::WA_DeleteOnClose); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject); + topLayout->addWidget(buttonBox); + + dlg->setWindowTitle(i18nc("Title of the window that shows the status of the migration agent and offers controls to start/stop individual migration jobs.", "Migration Status")); + dlg->resize(600, 300); + + if (windowId) { +#ifndef Q_OS_WIN + KWindowSystem::setMainWindow(dlg, windowId); +#else + KWindowSystem::setMainWindow(dlg, (HWND)windowId); +#endif + } + dlg->show(); +} + +} + +AKONADI_AGENT_MAIN(Akonadi::MigrationAgent) + diff --git a/agents/migration/migrationagent.desktop b/agents/migration/migrationagent.desktop new file mode 100644 index 00000000..299271c2 --- /dev/null +++ b/agents/migration/migrationagent.desktop @@ -0,0 +1,45 @@ +[Desktop Entry] +Name=Migration Agent +Name[bs]=Migracijski agent +Name[ca]=Agent de migració +Name[ca@valencia]=Agent de migració +Name[da]=Migreringsagent +Name[de]=Migrations-Assistent +Name[el]=Πράκτορας μεταφοράς +Name[en_GB]=Migration Agent +Name[es]=Agente de migración +Name[et]=Migreerimisagent +Name[fi]=Siirtoagentti +Name[fr]=Agent de migration +Name[gl]=Axente de migración +Name[hu]=Költöztető ügynök +Name[ia]=Agente de migration +Name[it]=Agente di migrazione +Name[kk]=Көшіп ауысу агенті +Name[ko]=이전 마법사 +Name[lt]=Migravimo agentas +Name[nb]=Migreringsagent +Name[nds]=Ümwannelhölper +Name[nl]=Migratie-agent +Name[pl]=Agent migracji +Name[pt]=Agente de Migração +Name[pt_BR]=Agente de Migração +Name[ru]=Агент переноса данных +Name[sk]=Agent migrácie +Name[sl]=Posrednik za selitev +Name[sr]=Агент селидбе +Name[sr@ijekavian]=Агент селидбе +Name[sr@ijekavianlatin]=Agent selidbe +Name[sr@latin]=Agent selidbe +Name[sv]=Överföringsmodul +Name[tr]=Taşıma Yardımcısı +Name[uk]=Агент перенесення +Name[x-test]=xxMigration Agentxx +Name[zh_CN]=迁移助手 +Name[zh_TW]=移植代理程式 +Type=AkonadiAgent +Exec=akonadi_migration_agent +Icon=system-reboot + +X-Akonadi-Capabilities=Unique,Autostart +X-Akonadi-Identifier=akonadi_migration_agent diff --git a/agents/migration/migrationagent.h b/agents/migration/migrationagent.h new file mode 100644 index 00000000..690cdabe --- /dev/null +++ b/agents/migration/migrationagent.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#ifndef MIGRATIONAGENT_H +#define MIGRATIONAGENT_H + +#include +#include "migrationscheduler.h" + +namespace Akonadi +{ + +class MigrationAgent : public AgentBase, public AgentBase::ObserverV2 +{ + Q_OBJECT +public: + explicit MigrationAgent(const QString &id); + void configure(WId windowId) Q_DECL_OVERRIDE; +private: + MigrationScheduler mScheduler; +}; + +} + +#endif diff --git a/agents/migration/migrationexecutor.cpp b/agents/migration/migrationexecutor.cpp new file mode 100644 index 00000000..fa2c5323 --- /dev/null +++ b/agents/migration/migrationexecutor.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ +#include "migrationexecutor.h" + +#include + +MigrationExecutor::MigrationExecutor() + : KJob(), + mSuspended(false), + mTotalAmount(0), + mAlreadyProcessed(0) +{ + setCapabilities(Suspendable); +} + +void MigrationExecutor::start() +{ + setPercent(0); + Q_EMIT description(this, i18nc("User visible name of ongoing Akonadi migration jobs", "PIM Maintenance")); +} + +void MigrationExecutor::add(const QSharedPointer &migrator) +{ + mTotalAmount++; + mQueue.enqueue(migrator.toWeakRef()); + executeNext(); +} + +void MigrationExecutor::executeNext() +{ + if (mCurrentMigrator || mSuspended) { + return; + } + QSharedPointer migrator; + while (!migrator && !mQueue.isEmpty()) { + mCurrentMigrator = mQueue.dequeue(); + migrator = mCurrentMigrator.toStrongRef(); + } + if (migrator) { + Q_EMIT infoMessage(this, i18nc("PIM-Maintenance is in progress.", "In progress...")); + connect(migrator.data(), &MigratorBase::stoppedProcessing, this, &MigrationExecutor::onStoppedProcessing); + migrator->start(); + } else { + // Reset the notification status, otherwise we get notification "In progress...[finished]" + // without any description that it's related to PIM-Maintenance + Q_EMIT infoMessage(this, i18n("PIM Maintenance")); + emitResult(); + } +} + +void MigrationExecutor::onStoppedProcessing() +{ + mAlreadyProcessed++; + Q_ASSERT(mTotalAmount > 0); + //TODO: setProcessedAmount would be better, but we need support for suitable units first (there's only files, folders, bytes). + setPercent(mAlreadyProcessed * 100.0 / mTotalAmount); + mCurrentMigrator.clear(); + executeNext(); +} + +bool MigrationExecutor::doSuspend() +{ + if (mCurrentMigrator) { + QSharedPointer migrator = mCurrentMigrator.toStrongRef(); + if (migrator) { + migrator->pause(); + } else { + mCurrentMigrator.clear(); + } + } + Q_EMIT infoMessage(this, i18nc("PIM-Maintenance is paused.", "Paused.")); + mSuspended = true; + return true; +} + +bool MigrationExecutor::doResume() +{ + mSuspended = false; + if (mCurrentMigrator) { + QSharedPointer migrator = mCurrentMigrator.toStrongRef(); + if (migrator) { + migrator->resume(); + } else { + mCurrentMigrator.clear(); + } + } + executeNext(); + return true; +} + diff --git a/agents/migration/migrationexecutor.h b/agents/migration/migrationexecutor.h new file mode 100644 index 00000000..2e50e893 --- /dev/null +++ b/agents/migration/migrationexecutor.h @@ -0,0 +1,61 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#ifndef MIGRATIONEXECUTOR_H +#define MIGRATIONEXECUTOR_H + +#include +#include +#include +#include + +/** + * An executor can contain multiple jobs that are scheduled by the executor. + * + * The executor is responsible for starting/pausing/stopping the individual migrators. + * + * This job is used to give overall progress information and start/stop controls to KUIServer via KUiServerJobTracker. + */ +class MigrationExecutor : public KJob +{ + Q_OBJECT +public: + MigrationExecutor(); + void add(const QSharedPointer &); + void start() Q_DECL_OVERRIDE; + +protected: + bool doResume() Q_DECL_OVERRIDE; + bool doSuspend() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onStoppedProcessing(); + void executeNext(); + +private: + QQueue< QWeakPointer > mQueue; + QWeakPointer mCurrentMigrator; + bool mSuspended; + int mTotalAmount; + int mAlreadyProcessed; +}; + +#endif diff --git a/agents/migration/migrationscheduler.cpp b/agents/migration/migrationscheduler.cpp new file mode 100644 index 00000000..cf12c17b --- /dev/null +++ b/agents/migration/migrationscheduler.cpp @@ -0,0 +1,298 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#include "migrationscheduler.h" + +#include +#include +#include +#include + +#include "migrationexecutor.h" + +void LogModel::message(MigratorBase::MessageType type, const QString &msg) +{ + switch (type) { + case MigratorBase::Success: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Skip: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-ok")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Info: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-information")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Warning: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-warning")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Error: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-error")), msg); + item->setEditable(false); + appendRow(item); + break; + } + default: + qCritical() << "unknown type " << type; + } +} + +Row::Row(const QSharedPointer &migrator, MigratorModel &model) + : QObject(), + mMigrator(migrator), + mModel(model) +{ + connect(migrator.data(), &MigratorBase::stateChanged, this, &Row::stateChanged); + connect(migrator.data(), SIGNAL(progress(int)), this, SLOT(progress(int))); +} + +bool Row::operator==(const Row &other) const +{ + return mMigrator->identifier() == other.mMigrator->identifier(); +} + +void Row::stateChanged(MigratorBase::MigrationState /*newState*/) +{ + mModel.columnChanged(*this, MigratorModel::State); +} + +void Row::progress(int /*prog*/) +{ + mModel.columnChanged(*this, MigratorModel::Progress); +} + +int MigratorModel::positionOf(const Row &row) +{ + int pos = 0; + foreach (const QSharedPointer &r, mMigrators) { + if (row == *r) { + return pos; + } + pos++; + } + return -1; +} + +void MigratorModel::columnChanged(const Row &row, int col) +{ + const int p = positionOf(row); + Q_ASSERT(p >= 0); + if (p >= 0) { + const QModelIndex idx = index(p, col); + Q_EMIT dataChanged(idx, idx); + } +} + +bool MigratorModel::addMigrator(const QSharedPointer &m) +{ + if (migrator(m->identifier())) { + qWarning() << "Model already contains a migrator with the identifier: " << m; + return false; + } + const int pos = mMigrators.size(); + beginInsertRows(QModelIndex(), pos, pos); + mMigrators.append(QSharedPointer(new Row(m, *this))); + endInsertRows(); + return true; +} + +int MigratorModel::columnCount(const QModelIndex &/*parent*/) const +{ + return ColumnCount; +} + +int MigratorModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mMigrators.size(); + } + return 0; +} + +QModelIndex MigratorModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row >= rowCount(parent) || row < 0) { + return QModelIndex(); + } + return createIndex(row, column, static_cast(mMigrators.at(row).data())); +} + +QModelIndex MigratorModel::parent(const QModelIndex &/*child*/) const +{ + return QModelIndex(); +} + +QVariant MigratorModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const +{ + if (role == Qt::DisplayRole) { + switch (section) { + case Name: + return i18nc("Name of the migrator in this row", "Name"); + case Progress: + return i18nc("Progress of the mgirator in %", "Progress"); + case State: + return i18nc("Current status of the migrator (done, in progress, ...)", "Status"); + default: + Q_ASSERT(false); + } + } + return QVariant(); +} + +QVariant MigratorModel::data(const QModelIndex &index, int role) const +{ + const Row *row = static_cast(index.internalPointer()); + const QSharedPointer migrator(row->mMigrator); + if (!migrator) { + qWarning() << "migrator not found"; + return QVariant(); + } + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case Name: + return migrator->displayName(); + case Progress: + return QStringLiteral("%1 %").arg(migrator->progress()); + case State: + return migrator->status(); + default: + Q_ASSERT(false); + } + case IdentifierRole: + return migrator->identifier(); + case LogfileRole: + return migrator->logfile(); + case Qt::ToolTipRole: + return migrator->description(); + default: + break; + } + return QVariant(); +} + +QSharedPointer MigratorModel::migrator(const QString &identifier) const +{ + foreach (const QSharedPointer &row, mMigrators) { + if (row->mMigrator->identifier() == identifier) { + return row->mMigrator; + } + } + return QSharedPointer(); +} + +QList< QSharedPointer > MigratorModel::migrators() const +{ + QList< QSharedPointer > migrators; + foreach (const QSharedPointer &row, mMigrators) { + return migrators << row->mMigrator; + } + return migrators; +} + +MigrationScheduler::MigrationScheduler(KJobTrackerInterface *jobTracker, QObject *parent) + : QObject(parent), + mModel(new MigratorModel), + mJobTracker(jobTracker) +{ +} + +MigrationScheduler::~MigrationScheduler() +{ + delete mAutostartExecutor; +} + +void MigrationScheduler::addMigrator(const QSharedPointer &migrator) +{ + if (mModel->addMigrator(migrator)) { + QSharedPointer logModel(new LogModel); + connect(migrator.data(), &MigratorBase::message, logModel.data(), &LogModel::message); + mLogModel.insert(migrator->identifier(), logModel); + if (migrator->shouldAutostart()) { + checkForAutostart(migrator); + } + } +} + +QAbstractItemModel &MigrationScheduler::model() +{ + return *mModel; +} + +QStandardItemModel &MigrationScheduler::logModel(const QString &identifier) +{ + Q_ASSERT(mLogModel.contains(identifier)); + return *mLogModel.value(identifier); +} + +void MigrationScheduler::checkForAutostart(const QSharedPointer &migrator) +{ + if (migrator->migrationState() != MigratorBase::Complete) { + + if (!mAutostartExecutor) { + mAutostartExecutor = new MigrationExecutor; + if (mJobTracker) { + mJobTracker->registerJob(mAutostartExecutor); + } + + mAutostartExecutor->start(); + } + + mAutostartExecutor->add(migrator); + } +} + +void MigrationScheduler::start(const QString &identifier) +{ + //TODO create separate executor? + const QSharedPointer m = mModel->migrator(identifier); + if (m) { + m->start(); + } +} + +void MigrationScheduler::pause(const QString &identifier) +{ + const QSharedPointer m = mModel->migrator(identifier); + if (m) { + m->pause(); + } +} + +void MigrationScheduler::abort(const QString &identifier) +{ + const QSharedPointer m = mModel->migrator(identifier); + if (m) { + m->abort(); + } +} + diff --git a/agents/migration/migrationscheduler.h b/agents/migration/migrationscheduler.h new file mode 100644 index 00000000..8823b8af --- /dev/null +++ b/agents/migration/migrationscheduler.h @@ -0,0 +1,132 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#ifndef MIGRATIONSCHEDULER_H +#define MIGRATIONSCHEDULER_H + +#include "migratorbase.h" +#include +#include +#include +#include +#include + +class MigrationExecutor; +class KJobTrackerInterface; +class MigratorModel; + +class LogModel : public QStandardItemModel +{ + Q_OBJECT +public Q_SLOTS: + void message(MigratorBase::MessageType type, const QString &msg); +}; + +class Row: public QObject +{ + Q_OBJECT +public: + QSharedPointer mMigrator; + MigratorModel &mModel; + + explicit Row(const QSharedPointer &migrator, MigratorModel &model); + + bool operator==(const Row &other) const; + +private Q_SLOTS: + void stateChanged(MigratorBase::MigrationState); + void progress(int); +}; + +/** + * The model serves as container for the migrators and exposes the status of each migrator. + * + * It can be plugged into a Listview to inform about the migration progress. + */ +class MigratorModel: public QAbstractItemModel +{ +public: + enum Roles { + IdentifierRole = Qt::UserRole + 1, + LogfileRole + }; + bool addMigrator(const QSharedPointer &migrator); + + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + QSharedPointer migrator(const QString &identifier) const; + QList< QSharedPointer > migrators() const; + +private: + enum Columns { + Name = 0, + Progress = 1, + State = 2, + ColumnCount + }; + friend class Row; + int positionOf(const Row &); + void columnChanged(const Row &, int column); + QList< QSharedPointer > mMigrators; +}; + +/** + * Scheduler for migration jobs. + * + * Status information is exposed via getModel, which returns a list model containing all migrators with basic information. + * Additionally a logmodel is available via getLogModel for each migrator. The logmodel is continuously filled with information, and can be requested and displayed at any time. + * + * Migrators which return true on shouldAutostart() automatically enter a queue to be processed one after the other. + * When manually triggered it is possible though to run multiple jobs in parallel. + */ +class MigrationScheduler : public QObject +{ + Q_OBJECT +public: + explicit MigrationScheduler(KJobTrackerInterface *jobTracker = Q_NULLPTR, QObject *parent = Q_NULLPTR); + virtual ~MigrationScheduler(); + + void addMigrator(const QSharedPointer &migrator); + + //A model for the view + QAbstractItemModel &model(); + QStandardItemModel &logModel(const QString &identifier); + + //Control + void start(const QString &identifier); + void pause(const QString &identifier); + void abort(const QString &identifier); + +private: + void checkForAutostart(const QSharedPointer &migrator); + + QScopedPointer mModel; + QHash > mLogModel; + QPointer mAutostartExecutor; + KJobTrackerInterface *mJobTracker; +}; + +#endif // MIGRATIONSCHEDULER_H diff --git a/agents/migration/migrationstatuswidget.cpp b/agents/migration/migrationstatuswidget.cpp new file mode 100644 index 00000000..6264e009 --- /dev/null +++ b/agents/migration/migrationstatuswidget.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#include "migrationstatuswidget.h" +#include "migrationscheduler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MigrationStatusWidget::MigrationStatusWidget(MigrationScheduler &scheduler, QWidget *parent) + : QWidget(parent), + mScheduler(scheduler) +{ + QVBoxLayout *vboxLayout = new QVBoxLayout; + { + QToolBar *toolbar = new QToolBar(QStringLiteral("MigrationControlToolbar"), this); + + QAction *start = toolbar->addAction(QStringLiteral("Start")); + start->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); + connect(start, &QAction::triggered, this, &MigrationStatusWidget::startSelected); + + QAction *pause = toolbar->addAction(QStringLiteral("Pause")); + pause->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause"))); + connect(pause, &QAction::triggered, this, &MigrationStatusWidget::pauseSelected); + + QAction *abort = toolbar->addAction(QStringLiteral("Abort")); + abort->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); + connect(abort, &QAction::triggered, this, &MigrationStatusWidget::abortSelected); + + vboxLayout->addWidget(toolbar); + } + { + QTreeView *treeView = new QTreeView(this); + treeView->setModel(&mScheduler.model()); + mSelectionModel = treeView->selectionModel(); + Q_ASSERT(mSelectionModel); + //Not sure why this is required, but otherwise the view doesn't load anything from the model + treeView->update(QModelIndex()); + connect(treeView, &QTreeView::doubleClicked, this, &MigrationStatusWidget::onItemActivated); + + vboxLayout->addWidget(treeView); + } + setLayout(vboxLayout); +} + +void MigrationStatusWidget::startSelected() +{ + foreach (const QModelIndex &index, mSelectionModel->selectedRows()) { + mScheduler.start(index.data(MigratorModel::IdentifierRole).toString()); + } +} + +void MigrationStatusWidget::pauseSelected() +{ + foreach (const QModelIndex &index, mSelectionModel->selectedRows()) { + mScheduler.pause(index.data(MigratorModel::IdentifierRole).toString()); + } +} + +void MigrationStatusWidget::abortSelected() +{ + foreach (const QModelIndex &index, mSelectionModel->selectedRows()) { + mScheduler.abort(index.data(MigratorModel::IdentifierRole).toString()); + } +} + +void MigrationStatusWidget::onItemActivated(const QModelIndex &index) +{ + QDialog *dlg = new QDialog(this); + QVBoxLayout *topLayout = new QVBoxLayout; + dlg->setLayout(topLayout); + QWidget *widget = new QWidget(dlg); + topLayout->addWidget(widget); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject); + topLayout->addWidget(buttonBox); + + QVBoxLayout *vboxLayout = new QVBoxLayout; + { + QListView *listView = new QListView(widget); + listView->setModel(&mScheduler.logModel(index.data(MigratorModel::IdentifierRole).toString())); + listView->setAutoScroll(true); + listView->scrollToBottom(); + vboxLayout->addWidget(listView); + } + { + QHBoxLayout *hboxLayout = new QHBoxLayout; + QLabel *label = new QLabel(QStringLiteral("%2").arg(index.data(MigratorModel::LogfileRole).toString()).arg(ki18n("Logfile").toString()), widget); + label->setOpenExternalLinks(true); + hboxLayout->addWidget(label); + hboxLayout->addStretch(); + vboxLayout->addLayout(hboxLayout); + } + widget->setLayout(vboxLayout); + + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setWindowTitle(i18nc("Title of the window displaying the log of a single migration job.", "Migration Info")); + dlg->resize(600, 300); + dlg->show(); +} + diff --git a/agents/migration/migrationstatuswidget.h b/agents/migration/migrationstatuswidget.h new file mode 100644 index 00000000..dec5b9e7 --- /dev/null +++ b/agents/migration/migrationstatuswidget.h @@ -0,0 +1,46 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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, see . + * + */ + +#ifndef MIGRATIONSTATUSWIDGET_H +#define MIGRATIONSTATUSWIDGET_H + +#include "migrationscheduler.h" +#include +#include + +class MigrationStatusWidget: public QWidget +{ + Q_OBJECT +public: + explicit MigrationStatusWidget(MigrationScheduler &scheduler, QWidget *parent = Q_NULLPTR); +private Q_SLOTS: + void startSelected(); + void pauseSelected(); + void abortSelected(); +private: + MigrationScheduler &mScheduler; + QItemSelectionModel *mSelectionModel; +public Q_SLOTS: + void onItemActivated(const QModelIndex &); +}; + +#endif // MIGRATIONCONFIGDIALOG_H + diff --git a/agents/newmailnotifier/CMakeLists.txt b/agents/newmailnotifier/CMakeLists.txt new file mode 100644 index 00000000..12fad7bc --- /dev/null +++ b/agents/newmailnotifier/CMakeLists.txt @@ -0,0 +1,63 @@ + +include_directories(${kdepim-runtime_BINARY_DIR}) +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_newmailnotifier_agent\") + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +set(newmailnotifieragent_SRCS + newmailnotifiersettingsdialog.cpp + newmailnotifieragent.cpp + specialnotifierjob.cpp + newmailnotifierselectcollectionwidget.cpp + newmailnotifiershowmessagejob.cpp +) + +ecm_qt_declare_logging_category(newmailnotifieragent_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME log_newmailnotifier) + +kconfig_add_kcfg_files(newmailnotifieragent_SRCS + newmailnotifieragentsettings.kcfgc + ) + + +qt5_add_dbus_adaptor(newmailnotifieragent_SRCS org.freedesktop.Akonadi.NewMailNotifier.xml newmailnotifieragent.h NewMailNotifierAgent) + + +add_executable( akonadi_newmailnotifier_agent ${newmailnotifieragent_SRCS}) + + +target_link_libraries( akonadi_newmailnotifier_agent + KF5::AkonadiCore + KF5::Mime + KF5::AkonadiMime + KF5::AkonadiContact + KF5::Codecs + KF5::IdentityManagement + KF5::NotifyConfig + KF5::AkonadiAgentBase + KF5::DBusAddons + KF5::XmlGui + KF5::Notifications + KF5::WindowSystem + KF5::Completion + KF5::Service + KF5::IconThemes +) + +if (Qt5TextToSpeech_FOUND) + target_link_libraries(akonadi_newmailnotifier_agent + Qt5::TextToSpeech) +endif() + +if( APPLE ) + set_target_properties( akonadi_newmailnotifier_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/Info.plist.template) + set_target_properties( akonadi_newmailnotifier_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.newmailnotifier") + set_target_properties( akonadi_newmailnotifier_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE New Mail Notifier") +endif () + +install(TARGETS akonadi_newmailnotifier_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) + + +install(FILES newmailnotifieragent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents") +install(FILES akonadi_newmailnotifier_agent.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) + diff --git a/agents/newmailnotifier/Messages.sh b/agents/newmailnotifier/Messages.sh new file mode 100755 index 00000000..9d815ddb --- /dev/null +++ b/agents/newmailnotifier/Messages.sh @@ -0,0 +1,4 @@ +#! /bin/sh +$EXTRACTRC `find . -name '*.kcfg'` >> rc.cpp || exit 11 +$XGETTEXT *.cpp -o $podir/akonadi_newmailnotifier_agent.pot +rm -f rc.cpp diff --git a/agents/newmailnotifier/TODO b/agents/newmailnotifier/TODO new file mode 100644 index 00000000..9f04060b --- /dev/null +++ b/agents/newmailnotifier/TODO @@ -0,0 +1,4 @@ +for 4.12: +--------- +- Look at https://github.com/shellscape/Gmail-Notifier-Plus/tree/master/Promotional for idea +- Show if we can display full collection path (for the moment we can't) diff --git a/agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc b/agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc new file mode 100644 index 00000000..78a96aaf --- /dev/null +++ b/agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc @@ -0,0 +1,137 @@ +[Global] +IconName=kmail +Comment=New email notify +Comment[bg]=Уведомяване за нова поща +Comment[bs]=Obavijest o novoj pošti +Comment[ca]=Notificador de correu electrònic nou +Comment[ca@valencia]=Notificador de correu electrònic nou +Comment[cs]=Upozornění na nový e-mail +Comment[da]=Bekendtgørelse af nye e-mails +Comment[de]=E-Mail-Benachrichtigung +Comment[el]=Ειδοποίηση νέας αλληλογραφίας +Comment[en_GB]=New email notify +Comment[es]=Nueva notificación de correo +Comment[et]=Uue kirja teavitaja +Comment[fi]=Ilmoitus uudesta postista +Comment[fr]=Notification de nouveaux courriers électroniques +Comment[ga]=Fógairt ríomhphoist nua +Comment[gl]=Notificación de nova mensaxe. +Comment[hu]=Új e-mail értesítés +Comment[ia]=Notifica de nove messages de e-posta +Comment[it]=Notifica dei nuovi messaggi di posta +Comment[kk]=Жаңа эл.пошта туралы хабарлау +Comment[km]=ជូនដំណឹង​អ៊ីមែល​ថ្មី +Comment[ko]=새 메일 알림 +Comment[lt]=Naujo pašto pranešimas +Comment[lv]=Jauna pasta paziņojums +Comment[nb]=Varsling om ny e-post +Comment[nds]=Bescheed över nieg Nettpost +Comment[nl]=Melding van nieuwe e-mail +Comment[pl]=Powiadomienie o nowej poczcie +Comment[pt]=Notificação de correio novo +Comment[pt_BR]=Notificação de novo e-mail +Comment[ru]=Уведомление о новой почте +Comment[sk]=Oznámenie novej pošty +Comment[sl]=Obvestilo o novi e-pošti +Comment[sr]=Обавештење о пристиглој е‑пошти +Comment[sr@ijekavian]=Обавештење о пристиглој е‑пошти +Comment[sr@ijekavianlatin]=Obaveštenje o pristigloj e‑pošti +Comment[sr@latin]=Obaveštenje o pristigloj e‑pošti +Comment[sv]=Ny brevunderrättelse +Comment[tr]=Yeni e-posta bildirimi +Comment[uk]=Сповіщувач про нові повідомлення +Comment[x-test]=xxNew email notifyxx +Comment[zh_CN]=新邮件提醒 +Comment[zh_TW]=新郵件通知 +Name=New email notify +Name[bg]=Уведомяване за нова поща +Name[bs]=Obavijest o novoj pošti +Name[ca]=Notificador de correu electrònic nou +Name[ca@valencia]=Notificador de correu electrònic nou +Name[cs]=Upozornění na nový e-mail +Name[da]=Bekendtgørelse af nye e-mails +Name[de]=E-Mail-Benachrichtigung +Name[el]=Ειδοποίηση νέας αλληλογραφίας +Name[en_GB]=New email notify +Name[es]=Nueva notificación de correo +Name[et]=Uue kirja teavitaja +Name[fi]=Ilmoitus uudesta postista +Name[fr]=Notification de nouveaux courriers électroniques +Name[ga]=Fógairt ríomhphoist nua +Name[gl]=Notificación de nova mensaxe +Name[hu]=Új e-mail értesítés +Name[ia]=Notifica de nove messages de e-posta +Name[it]=Notifica dei nuovi messaggi di posta +Name[kk]=Жаңа эл.пошта туралы хабарлау +Name[km]=ជូនដំណឹង​អ៊ីមែល​ថ្មី +Name[ko]=새 메일 알림 +Name[lt]=Naujo pašto pranešimas +Name[lv]=Jauna pasta paziņojums +Name[nb]=Varsling om ny e-post +Name[nds]=Nieg Nettpost +Name[nl]=Melding van nieuwe e-mail +Name[pl]=Powiadomienie o nowej poczcie +Name[pt]=Notificação de correio novo +Name[pt_BR]=Notificação de novo e-mail +Name[ro]=Notificare mesaje noi +Name[ru]=Уведомление о новой почте +Name[sk]=Oznámenie novej pošty +Name[sl]=Obvestilo o novi e-pošti +Name[sr]=Обавештење о пристиглој е‑пошти +Name[sr@ijekavian]=Обавештење о пристиглој е‑пошти +Name[sr@ijekavianlatin]=Obaveštenje o pristigloj e‑pošti +Name[sr@latin]=Obaveštenje o pristigloj e‑pošti +Name[sv]=Ny brevunderrättelse +Name[tr]=Yeni e-posta bildirimi +Name[uk]=Сповіщувач про нові повідомлення +Name[x-test]=xxNew email notifyxx +Name[zh_CN]=新邮件提醒 +Name[zh_TW]=新郵件通知 + +[Event/new-email] +Name=New email arrived +Name[bg]=Пристигна нова поща +Name[bs]=Nova pošta stigla +Name[ca]=Ha arribat correu electrònic nou +Name[ca@valencia]=Ha arribat correu electrònic nou +Name[cs]=Přišel nový e-mail +Name[da]=Ny e-mail ankommet +Name[de]=Neue Nachrichten sind eingetroffen +Name[el]=Ελήφθη νέα αλληλογραφία +Name[en_GB]=New email arrived +Name[es]=Llegó correo nuevo +Name[et]=Saabus uus kiri +Name[fi]=Uutta postia saapunut +Name[fr]=Un nouveau courrier électronique est arrivé +Name[ga]=Tháinig ríomhphost nua +Name[gl]=Chegou unha mensaxe nova +Name[hu]=Új e-mail érkezett +Name[ia]=Nove message de e-posta arrivava +Name[it]=Nuova posta ricevuta +Name[kk]=Жаңа пошта келді +Name[km]=មាន​អ៊ីមែល​ថ្មី​មក​ដល់ +Name[ko]=새 메일이 도착했습니다 +Name[lt]=Naujas laiškas gautas +Name[lv]=Saņemts jauns e-pasts +Name[nb]=Ny e-post ankommet +Name[nds]=Niege Nettpost ankamen +Name[nl]=Nieuwe e-mail aangekomen +Name[pl]=Nadeszła nowa poczta +Name[pt]=Notificação de correio novo +Name[pt_BR]=Notificação de novo e-mail +Name[ro]=Mesaje noi sosite +Name[ru]=Получена новая почта +Name[sk]=Prišla nová pošta +Name[sl]=Prispela je nova e-pošta +Name[sr]=Стигла је нова е‑пошта +Name[sr@ijekavian]=Стигла је нова е‑пошта +Name[sr@ijekavianlatin]=Stigla je nova e‑pošta +Name[sr@latin]=Stigla je nova e‑pošta +Name[sv]=Ny post har anlänt +Name[tr]=Yeni e-posta alındı +Name[uk]=Надійшла нова пошта +Name[x-test]=xxNew email arrivedxx +Name[zh_CN]=新邮件到达 +Name[zh_TW]=新郵件已抵達 +Action=Popup + diff --git a/agents/newmailnotifier/newmailnotifieragent.cpp b/agents/newmailnotifier/newmailnotifieragent.cpp new file mode 100644 index 00000000..b164e811 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragent.cpp @@ -0,0 +1,575 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + Copyright (c) 2010 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 "newmailnotifieragent.h" + +#include +#include "specialnotifierjob.h" +#include "newmailnotifieradaptor.h" +#include "newmailnotifieragentsettings.h" +#include "newmailnotifiersettingsdialog.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "newmailnotifier_debug.h" +#include +#include +#include + +using namespace Akonadi; + +NewMailNotifierAgent::NewMailNotifierAgent(const QString &id) + : AgentBase(id) +{ + Kdelibs4ConfigMigrator migrate(QStringLiteral("newmailnotifieragent")); + migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_newmailnotifier_agentrc") << QStringLiteral("akonadi_newmailnotifier_agent.notifyrc")); + migrate.migrate(); + + KLocalizedString::setApplicationDomain("akonadi_newmailnotifier_agent"); + Akonadi::AttributeFactory::registerAttribute(); + new NewMailNotifierAdaptor(this); + + mIdentityManager = new KIdentityManagement::IdentityManager(false, this); + connect(mIdentityManager, SIGNAL(changed()), SLOT(slotIdentitiesChanged())); + slotIdentitiesChanged(); + mDefaultPixmap = QIcon::fromTheme(QStringLiteral("kmail")).pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium); + + KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/NewMailNotifierAgent"), + this, QDBusConnection::ExportAdaptors); + KDBusConnectionPool::threadConnection().registerService(QStringLiteral("org.freedesktop.Akonadi.NewMailNotifierAgent")); + + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceStatusChanged, this, &NewMailNotifierAgent::slotInstanceStatusChanged); + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &NewMailNotifierAgent::slotInstanceRemoved); + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceAdded, this, &NewMailNotifierAgent::slotInstanceAdded); + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceNameChanged, this, &NewMailNotifierAgent::slotInstanceNameChanged); + + changeRecorder()->setMimeTypeMonitored(KMime::Message::mimeType()); + changeRecorder()->itemFetchScope().setCacheOnly(true); + changeRecorder()->itemFetchScope().setFetchModificationTime(false); + changeRecorder()->fetchCollection(true); + changeRecorder()->setChangeRecordingEnabled(false); + changeRecorder()->ignoreSession(Akonadi::Session::defaultSession()); + changeRecorder()->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); + changeRecorder()->setCollectionMonitored(Collection::root(), true); + mTimer.setInterval(5 * 1000); + connect(&mTimer, &QTimer::timeout, this, &NewMailNotifierAgent::slotShowNotifications); + + if (isActive()) { + mTimer.setSingleShot(true); + } +} + +void NewMailNotifierAgent::slotIdentitiesChanged() +{ + mListEmails = mIdentityManager->allEmails(); +} + +void NewMailNotifierAgent::doSetOnline(bool online) +{ + if (!online) { + clearAll(); + } +} + +void NewMailNotifierAgent::setExcludeMyselfFromNotification(bool b) +{ + NewMailNotifierAgentSettings::setExcludeEmailsFromMe(b); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::excludeMyselfFromNotification() const +{ + return NewMailNotifierAgentSettings::excludeEmailsFromMe(); +} + +void NewMailNotifierAgent::setShowPhoto(bool show) +{ + NewMailNotifierAgentSettings::setShowPhoto(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showPhoto() const +{ + return NewMailNotifierAgentSettings::showPhoto(); +} + +void NewMailNotifierAgent::setShowFrom(bool show) +{ + NewMailNotifierAgentSettings::setShowFrom(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showFrom() const +{ + return NewMailNotifierAgentSettings::showFrom(); +} + +void NewMailNotifierAgent::setShowSubject(bool show) +{ + NewMailNotifierAgentSettings::setShowSubject(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showSubject() const +{ + return NewMailNotifierAgentSettings::showSubject(); +} + +void NewMailNotifierAgent::setShowFolderName(bool show) +{ + NewMailNotifierAgentSettings::setShowFolder(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showFolderName() const +{ + return NewMailNotifierAgentSettings::showFolder(); +} + +void NewMailNotifierAgent::setEnableAgent(bool enabled) +{ + NewMailNotifierAgentSettings::setEnabled(enabled); + NewMailNotifierAgentSettings::self()->save(); + if (!enabled) { + clearAll(); + } +} + +void NewMailNotifierAgent::setVerboseMailNotification(bool verbose) +{ + NewMailNotifierAgentSettings::setVerboseNotification(verbose); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::verboseMailNotification() const +{ + return NewMailNotifierAgentSettings::verboseNotification(); +} + +void NewMailNotifierAgent::setTextToSpeakEnabled(bool enabled) +{ + NewMailNotifierAgentSettings::setTextToSpeakEnabled(enabled); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::textToSpeakEnabled() const +{ + return NewMailNotifierAgentSettings::textToSpeakEnabled(); +} + +void NewMailNotifierAgent::setTextToSpeak(const QString &msg) +{ + NewMailNotifierAgentSettings::setTextToSpeak(msg); + NewMailNotifierAgentSettings::self()->save(); +} + +QString NewMailNotifierAgent::textToSpeak() const +{ + return NewMailNotifierAgentSettings::textToSpeak(); +} + +void NewMailNotifierAgent::clearAll() +{ + mNewMails.clear(); + mInstanceNameInProgress.clear(); +} + +bool NewMailNotifierAgent::enabledAgent() const +{ + return NewMailNotifierAgentSettings::enabled(); +} + +bool NewMailNotifierAgent::showButtonToDisplayMail() const +{ + return NewMailNotifierAgentSettings::showButtonToDisplayMail(); +} + +void NewMailNotifierAgent::setShowButtonToDisplayMail(bool b) +{ + NewMailNotifierAgentSettings::setShowButtonToDisplayMail(b); + NewMailNotifierAgentSettings::self()->save(); +} + +void NewMailNotifierAgent::showConfigureDialog(qlonglong windowId) +{ + configure(windowId); +} + +void NewMailNotifierAgent::configure(WId windowId) +{ + QPointer dialog = new NewMailNotifierSettingsDialog; + if (windowId) { +#ifndef Q_OS_WIN + KWindowSystem::setMainWindow(dialog, windowId); +#else + KWindowSystem::setMainWindow(dialog, (HWND)windowId); +#endif + } + dialog->exec(); + delete dialog; +} + +bool NewMailNotifierAgent::excludeSpecialCollection(const Akonadi::Collection &collection) const +{ + if (collection.hasAttribute()) { + return true; + } + + if (collection.hasAttribute()) { + if (collection.attribute()->ignoreNewMail()) { + return true; + } + } + + if (!collection.contentMimeTypes().contains(KMime::Message::mimeType())) { + return true; + } + + SpecialMailCollections::Type type = SpecialMailCollections::self()->specialCollectionType(collection); + switch (type) { + case SpecialMailCollections::Invalid: //Not a special collection + case SpecialMailCollections::Inbox: + return false; + default: + return true; + } + +} + +void NewMailNotifierAgent::itemsRemoved(const Item::List &items) +{ + if (!isActive()) { + return; + } + + QHash< Akonadi::Collection, QList >::iterator end(mNewMails.end()); + for (QHash< Akonadi::Collection, QList >::iterator it = mNewMails.begin(); it != end; ++it) { + QList idList = it.value(); + bool itemFound = false; + Q_FOREACH (const Item &item, items) { + if (idList.contains(item.id())) { + idList.removeAll(item.id()); + itemFound = true; + } + } + if (itemFound) { + if (mNewMails[it.key()].isEmpty()) { + mNewMails.remove(it.key()); + } else { + mNewMails[it.key()] = idList; + } + } + } +} + +void NewMailNotifierAgent::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) +{ + if (!isActive()) { + return; + } + Q_FOREACH (const Akonadi::Item &item, items) { + QHash< Akonadi::Collection, QList >::iterator end(mNewMails.end()); + for (QHash< Akonadi::Collection, QList >::iterator it = mNewMails.begin(); it != end; ++it) { + QList idList = it.value(); + if (idList.contains(item.id()) && addedFlags.contains("\\SEEN")) { + idList.removeAll(item.id()); + if (idList.isEmpty()) { + mNewMails.remove(it.key()); + break; + } else { + (*it) = idList; + } + } + } + } +} + +void NewMailNotifierAgent::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) +{ + if (!isActive()) { + return; + } + + Q_FOREACH (const Akonadi::Item &item, items) { + if (ignoreStatusMail(item)) { + continue; + } + + if (excludeSpecialCollection(collectionSource)) { + continue; // outbox, sent-mail, trash, drafts or templates. + } + + if (mNewMails.contains(collectionSource)) { + QList idListFrom = mNewMails[ collectionSource ]; + if (idListFrom.contains(item.id())) { + idListFrom.removeAll(item.id()); + + if (idListFrom.isEmpty()) { + mNewMails.remove(collectionSource); + } else { + mNewMails[ collectionSource ] = idListFrom; + } + if (!excludeSpecialCollection(collectionDestination)) { + QList idListTo = mNewMails[ collectionDestination ]; + idListTo.append(item.id()); + mNewMails[ collectionDestination ] = idListTo; + } + } + } + } +} + +bool NewMailNotifierAgent::ignoreStatusMail(const Akonadi::Item &item) +{ + Akonadi::MessageStatus status; + status.setStatusFromFlags(item.flags()); + if (status.isRead() || status.isSpam() || status.isIgnored()) { + return true; + } + return false; +} + +void NewMailNotifierAgent::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) +{ + if (!isActive()) { + return; + } + + if (excludeSpecialCollection(collection)) { + return; // outbox, sent-mail, trash, drafts or templates. + } + + if (ignoreStatusMail(item)) { + return; + } + + if (!mTimer.isActive()) { + mTimer.start(); + } + mNewMails[ collection ].append(item.id()); +} + +void NewMailNotifierAgent::slotShowNotifications() +{ + if (mNewMails.isEmpty()) { + return; + } + + if (!isActive()) { + return; + } + + if (!mInstanceNameInProgress.isEmpty()) { + //Restart timer until all is done. + mTimer.start(); + return; + } + + QString message; + if (NewMailNotifierAgentSettings::verboseNotification()) { + bool hasUniqMessage = true; + Akonadi::Item::Id item = -1; + QString currentPath; + QStringList texts; + QHash< Akonadi::Collection, QList >::const_iterator end(mNewMails.constEnd()); + const int numberOfCollection(mNewMails.count()); + if (numberOfCollection > 1) { + hasUniqMessage = false; + } + + for (QHash< Akonadi::Collection, QList >::const_iterator it = mNewMails.constBegin(); it != end; ++it) { + Akonadi::EntityDisplayAttribute *attr = it.key().attribute(); + QString displayName; + if (attr && !attr->displayName().isEmpty()) { + displayName = attr->displayName(); + } else { + displayName = it.key().name(); + } + + if (hasUniqMessage) { + if (it.value().count() == 0) { + //You can have an unique folder with 0 message + return; + } else if (it.value().count() == 1) { + item = it.value().at(0); + currentPath = displayName; + break; + } else { + hasUniqMessage = false; + } + } + QString resourceName; + if (!mCacheResourceName.contains(it.key().resource())) { + Q_FOREACH (const Akonadi::AgentInstance &instance, Akonadi::AgentManager::self()->instances()) { + if (instance.identifier() == it.key().resource()) { + mCacheResourceName.insert(instance.identifier(), instance.name()); + resourceName = instance.name(); + break; + } + } + } else { + resourceName = mCacheResourceName.value(it.key().resource()); + } + const int numberOfEmails(it.value().count()); + if (numberOfEmails > 0) { + texts.append(i18ncp("%2 = name of mail folder; %3 = name of Akonadi POP3/IMAP/etc resource (as user named it)", + "One new email in %2 from \"%3\"", + "%1 new emails in %2 from \"%3\"", numberOfEmails, displayName, + resourceName)); + } + } + if (hasUniqMessage) { + SpecialNotifierJob *job = new SpecialNotifierJob(mListEmails, currentPath, item, this); + job->setDefaultPixmap(mDefaultPixmap); + connect(job, &SpecialNotifierJob::displayNotification, this, &NewMailNotifierAgent::slotDisplayNotification); + + mNewMails.clear(); + return; + } else { + message = texts.join(QStringLiteral("
")); + } + } else { + message = i18n("New mail arrived"); + } + + qCDebug(NEWMAILNOTIFIER_LOG) << message; + + slotDisplayNotification(mDefaultPixmap, message); + + mNewMails.clear(); +} + +void NewMailNotifierAgent::slotDisplayNotification(const QPixmap &pixmap, const QString &message) +{ + KNotification::event(QStringLiteral("new-email"), + message, + pixmap, + Q_NULLPTR, + KNotification::CloseOnTimeout, + QStringLiteral("akonadi_newmailnotifier_agent")); + +} + +void NewMailNotifierAgent::slotInstanceNameChanged(const Akonadi::AgentInstance &instance) +{ + if (!isActive()) { + return; + } + + const QString identifier(instance.identifier()); + if (mCacheResourceName.contains(identifier)) { + mCacheResourceName.remove(identifier); + mCacheResourceName.insert(identifier, instance.name()); + } +} + +void NewMailNotifierAgent::slotInstanceStatusChanged(const Akonadi::AgentInstance &instance) +{ + if (!isActive()) { + return; + } + + const QString identifier(instance.identifier()); + switch (instance.status()) { + case Akonadi::AgentInstance::Broken: + case Akonadi::AgentInstance::Idle: { + if (mInstanceNameInProgress.contains(identifier)) { + mInstanceNameInProgress.removeAll(identifier); + } + break; + } + case Akonadi::AgentInstance::Running: { + if (!excludeAgentType(instance)) { + if (!mInstanceNameInProgress.contains(identifier)) { + mInstanceNameInProgress.append(identifier); + } + } + break; + } + case Akonadi::AgentInstance::NotConfigured: + //Nothing + break; + } +} + +bool NewMailNotifierAgent::excludeAgentType(const Akonadi::AgentInstance &instance) +{ + if (instance.type().mimeTypes().contains(KMime::Message::mimeType())) { + const QStringList capabilities(instance.type().capabilities()); + if (capabilities.contains(QStringLiteral("Resource")) && + !capabilities.contains(QStringLiteral("Virtual")) && + !capabilities.contains(QStringLiteral("MailTransport"))) { + return false; + } else { + return true; + } + } + return true; +} + +void NewMailNotifierAgent::slotInstanceRemoved(const Akonadi::AgentInstance &instance) +{ + if (!isActive()) { + return; + } + + const QString identifier(instance.identifier()); + if (mInstanceNameInProgress.contains(identifier)) { + mInstanceNameInProgress.removeAll(identifier); + } +} + +void NewMailNotifierAgent::slotInstanceAdded(const Akonadi::AgentInstance &instance) +{ + mCacheResourceName.insert(instance.identifier(), instance.name()); +} + +void NewMailNotifierAgent::printDebug() +{ + qCDebug(NEWMAILNOTIFIER_LOG) << "instance in progress: " << mInstanceNameInProgress + << "\n notifier enabled : " << NewMailNotifierAgentSettings::enabled() + << "\n check in progress : " << !mInstanceNameInProgress.isEmpty(); +} + +bool NewMailNotifierAgent::isActive() const +{ + return isOnline() && NewMailNotifierAgentSettings::enabled(); +} + +AKONADI_AGENT_MAIN(NewMailNotifierAgent) + diff --git a/agents/newmailnotifier/newmailnotifieragent.desktop b/agents/newmailnotifier/newmailnotifieragent.desktop new file mode 100644 index 00000000..9bffe6b5 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragent.desktop @@ -0,0 +1,97 @@ +[Desktop Entry] +Name=New Email Notifier +Name[bg]=Уведомяване за нова поща +Name[bs]=Novi obavještavač o pošti +Name[ca]=Notificador de correu electrònic nou +Name[ca@valencia]=Notificador de correu electrònic nou +Name[cs]=Upozornění na nový e-mail +Name[da]=Bekendtgørelse af nye e-mails +Name[de]=E-Mail-Benachrichtigung +Name[el]=Ειδοποιητής νέας αλληλογραφίας +Name[en_GB]=New Email Notifier +Name[es]=Nuevo notificador de correo +Name[et]=Uue kirja teavitaja +Name[fi]=Uudesta postista ilmoitin +Name[fr]=Notification de nouveaux courriers électroniques +Name[ga]=Fógra Ríomhphoist Nua +Name[gl]=Novo notificador de correo electrónico +Name[hu]=Új e-mail értesítő +Name[ia]=Notificator de nove messages de e-posta +Name[it]=Notifiche dei nuovi messaggi di posta +Name[kk]=Жаңа эл.пошта туралы хабарлау +Name[km]=កម្មវិធី​ជូន​ដំណឹង​អ៊ីមែល​ថ្មី +Name[ko]=새 메일 알리미 +Name[lt]=Naujo pašto pranešėjas +Name[lv]=Jauna pasta paziņotājs +Name[nb]=Varsling om ny e-post +Name[nds]=Nieg-Nettpost-Bescheedgever +Name[nl]=Nieuwe e-mailmelder +Name[pa]=ਨਵੀਂ ਈਮੇਲ ਸੂਚਨਾ +Name[pl]=Powiadomienie o nowej poczcie +Name[pt]=Notificação de Correio Novo +Name[pt_BR]=Notificação de novo e-mail +Name[ro]=Notificator mesaje noi +Name[ru]=Уведомления о новой почте +Name[sk]=Oznamovač novej pošty +Name[sl]=Obvestilnik o novi e-pošti +Name[sr]=Извештавач о пристиглој е‑пошти +Name[sr@ijekavian]=Извештавач о пристиглој е‑пошти +Name[sr@ijekavianlatin]=Izveštavač o pristigloj e‑pošti +Name[sr@latin]=Izveštavač o pristigloj e‑pošti +Name[sv]=Ny brevunderrättelse +Name[tr]=Yeni E-posta Bildirimi +Name[uk]=Сповіщувач про нові повідомлення +Name[x-test]=xxNew Email Notifierxx +Name[zh_CN]=新邮件提醒 +Name[zh_TW]=新郵件通知器 +Comment=Notifications about newly received emails +Comment[ast]=Avisos tocante a correos nuevos recibíos +Comment[bs]=Obavještenja o novoprimljenoj pošti +Comment[ca]=Notificacions quant a correus electrònics nous rebuts +Comment[ca@valencia]=Notificacions quant a correus electrònics nous rebuts +Comment[cs]=Oznamování nově příchozích e-mailů +Comment[da]=Bekendtgørelser om nyligt modtagne e-mails +Comment[de]=Benachrichtigungen über neu empfangene E-Mails +Comment[el]=Ειδοποιήσεις για νέα αλληλογραφία +Comment[en_GB]=Notifications about newly received emails +Comment[es]=Notificaciones sobre correos recibidos recientemente +Comment[et]=Märguanded äsja saabunud e-kirjade kohta +Comment[fi]=Ilmoitukset saapuneesta uudesta sähköpostista +Comment[fr]=Notifications à propos des courriers électroniques récemment reçus +Comment[ga]=Fógraí faoi ríomhphost nua +Comment[gl]=Notificacións de mensaxes acabadas de chegar +Comment[hu]=Értesítések újonnan érkezett e-mailekről +Comment[ia]=Notificationes re nove e-postas recipite +Comment[it]=Notifiche dei messaggi di posta elettronica ricevuti recentemente +Comment[kk]=Жаңа эл.пошта келгені туралы хабарлау +Comment[km]=ការ​ជូន​ដំណឹង​អំពី​អ៊ីមែល​​​ដែល​បាន​ទទួល​ថ្មីៗ +Comment[ko]=새로 받은 메일 알림 +Comment[lt]=Pranešimai apie naujai atsiųstus el. laiškus +Comment[lv]=Paziņojumi par jauniem e-pastiem +Comment[nb]=Varslinger om nylig mottatte e-poster +Comment[nds]=Bescheden över nieg rinkamen Nettbreven +Comment[nl]=Meldingen over nieuw ontvangen e-mails +Comment[pl]=Powiadomienia o nowo otrzymanych wiadomościach +Comment[pt]=Notificações acerca do correio novo recebido +Comment[pt_BR]=Notificações sobre os novos e-mails recebidos +Comment[ro]=Notificări despre mesajele noi +Comment[ru]=Уведомления о получении новых электронных писем +Comment[sk]=Notifikácie o novo prijatých e-mailoch +Comment[sl]=Obvestila o na novo prispeli e-pošti +Comment[sr]=Обавештења о недавно пристиглој е‑пошти +Comment[sr@ijekavian]=Обавештења о недавно пристиглој е‑пошти +Comment[sr@ijekavianlatin]=Obaveštenja o nedavno pristigloj e‑pošti +Comment[sr@latin]=Obaveštenja o nedavno pristigloj e‑pošti +Comment[sv]=Underrättelser om nyss mottagen e-post +Comment[tr]=Yeni gelen e-postalar için bildirimler +Comment[uk]=Сповіщення щодо щойно отриманих повідомлень +Comment[x-test]=xxNotifications about newly received emailsxx +Comment[zh_CN]=新邮件通知 +Comment[zh_TW]=收到新郵件的通知 +Icon=mail-unread-new +Type=AkonadiAgent +Exec=akonadi_newmailnotifier_agent + +X-Akonadi-MimeTypes=message/rfc822 +X-Akonadi-Capabilities=Unique,Autostart +X-Akonadi-Identifier=akonadi_newmailnotifier_agent diff --git a/agents/newmailnotifier/newmailnotifieragent.h b/agents/newmailnotifier/newmailnotifieragent.h new file mode 100644 index 00000000..fe0f152a --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragent.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + Copyright (c) 2010 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 NEWMAILNOTIFIERAGENT_H +#define NEWMAILNOTIFIERAGENT_H + +#include // make sure this is included before QHash, otherwise it wont find the correct qHash implementation for some reason +#include + +#include +#include +#include +namespace Akonadi +{ +class AgentInstance; +} + +namespace KIdentityManagement +{ +class IdentityManager; +} + +class NewMailNotifierAgent : public Akonadi::AgentBase, public Akonadi::AgentBase::ObserverV3 +{ + Q_OBJECT + +public: + explicit NewMailNotifierAgent(const QString &id); + + void showConfigureDialog(qlonglong windowId = 0); + + void setEnableAgent(bool b); + bool enabledAgent() const; + + void setVerboseMailNotification(bool b); + bool verboseMailNotification() const; + + void setBeepOnNewMails(bool b); + bool beepOnNewMails() const; + + void setShowPhoto(bool b); + bool showPhoto() const; + + void setShowFrom(bool b); + bool showFrom() const; + + void setShowSubject(bool b); + bool showSubject() const; + + void setShowFolderName(bool b); + bool showFolderName() const; + + void setExcludeMyselfFromNotification(bool b); + bool excludeMyselfFromNotification() const; + + void setTextToSpeakEnabled(bool enabled); + bool textToSpeakEnabled() const; + + QString textToSpeak() const; + void setTextToSpeak(const QString &msg); + + void printDebug(); + + bool showButtonToDisplayMail() const; + void setShowButtonToDisplayMail(bool b); + +protected: + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) Q_DECL_OVERRIDE; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &sourceCollection, const Akonadi::Collection &destinationCollection) Q_DECL_OVERRIDE; + void itemsRemoved(const Akonadi::Item::List &items) Q_DECL_OVERRIDE; + void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) Q_DECL_OVERRIDE; + void doSetOnline(bool online) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void slotShowNotifications(); + void configure(WId windowId) Q_DECL_OVERRIDE; + void slotInstanceStatusChanged(const Akonadi::AgentInstance &instance); + void slotInstanceRemoved(const Akonadi::AgentInstance &instance); + void slotInstanceAdded(const Akonadi::AgentInstance &instance); + void slotDisplayNotification(const QPixmap &pixmap, const QString &message); + void slotIdentitiesChanged(); + void slotInstanceNameChanged(const Akonadi::AgentInstance &instance); + +private: + bool excludeAgentType(const Akonadi::AgentInstance &instance); + bool ignoreStatusMail(const Akonadi::Item &item); + bool isActive() const; + void clearAll(); + bool excludeSpecialCollection(const Akonadi::Collection &collection) const; + QPixmap mDefaultPixmap; + QStringList mListEmails; + QHash > mNewMails; + QHash mCacheResourceName; + QTimer mTimer; + QStringList mInstanceNameInProgress; + KIdentityManagement::IdentityManager *mIdentityManager; +}; + +#endif diff --git a/agents/newmailnotifier/newmailnotifieragentsettings.kcfg b/agents/newmailnotifier/newmailnotifieragentsettings.kcfg new file mode 100644 index 00000000..7546b571 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragentsettings.kcfg @@ -0,0 +1,40 @@ + + + KLocalizedString + + + true + + + true + + + true + + + true + + + true + + + true + + + false + + + false + + + i18nc("%s is a variable for agent. Do not change it", "A message was received from %s") + + + + false + + + diff --git a/agents/newmailnotifier/newmailnotifieragentsettings.kcfgc b/agents/newmailnotifier/newmailnotifieragentsettings.kcfgc new file mode 100644 index 00000000..e7f02441 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragentsettings.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=newmailnotifieragentsettings.kcfg +ClassName=NewMailNotifierAgentSettings +Singleton=true +Mutators=true +SetUserTexts=true diff --git a/agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp new file mode 100644 index 00000000..13d53785 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp @@ -0,0 +1,218 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + 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 "newmailnotifierselectcollectionwidget.h" +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "newmailnotifier_debug.h" + +#include +#include +#include +#include +#include + +NewMailNotifierSelectCollectionWidget::NewMailNotifierSelectCollectionWidget(QWidget *parent) + : QWidget(parent), + mNeedUpdate(false) +{ + QVBoxLayout *vbox = new QVBoxLayout; + + QLabel *label = new QLabel(i18n("Select which folders to monitor for new message notifications:")); + vbox->addWidget(label); + + // Create a new change recorder. + mChangeRecorder = new Akonadi::ChangeRecorder(this); + mChangeRecorder->setMimeTypeMonitored(KMime::Message::mimeType()); + mChangeRecorder->fetchCollection(true); + mChangeRecorder->setAllMonitored(true); + + mModel = new Akonadi::EntityTreeModel(mChangeRecorder, this); + // Set the model to show only collections, not items. + mModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation); + connect(mModel, &Akonadi::EntityTreeModel::collectionTreeFetched, this, &NewMailNotifierSelectCollectionWidget::slotCollectionTreeFetched); + + Akonadi::CollectionFilterProxyModel *mimeTypeProxy = new Akonadi::CollectionFilterProxyModel(this); + mimeTypeProxy->setExcludeVirtualCollections(true); + mimeTypeProxy->addMimeTypeFilters(QStringList() << KMime::Message::mimeType()); + mimeTypeProxy->setSourceModel(mModel); + + // Create the Check proxy model. + mSelectionModel = new QItemSelectionModel(mimeTypeProxy); + mCheckProxy = new KCheckableProxyModel(this); + mCheckProxy->setSelectionModel(mSelectionModel); + mCheckProxy->setSourceModel(mimeTypeProxy); + + mCollectionFilter = new KRecursiveFilterProxyModel(this); + mCollectionFilter->setSourceModel(mCheckProxy); + mCollectionFilter->setDynamicSortFilter(true); + mCollectionFilter->setFilterCaseSensitivity(Qt::CaseInsensitive); + + KLineEdit *searchLine = new KLineEdit(this); + searchLine->setPlaceholderText(i18n("Search...")); + searchLine->setClearButtonShown(true); + connect(searchLine, &QLineEdit::textChanged, + this, &NewMailNotifierSelectCollectionWidget::slotSetCollectionFilter); + + vbox->addWidget(searchLine); + + mFolderView = new QTreeView; + mFolderView->setEditTriggers(QAbstractItemView::NoEditTriggers); + mFolderView->setAlternatingRowColors(true); + vbox->addWidget(mFolderView); + + mFolderView->setModel(mCollectionFilter); + + QHBoxLayout *hbox = new QHBoxLayout; + vbox->addLayout(hbox); + + QPushButton *button = new QPushButton(i18n("&Select All"), this); + connect(button, &QPushButton::clicked, this, &NewMailNotifierSelectCollectionWidget::slotSelectAllCollections); + hbox->addWidget(button); + + button = new QPushButton(i18n("&Unselect All"), this); + connect(button, &QPushButton::clicked, this, &NewMailNotifierSelectCollectionWidget::slotUnselectAllCollections); + hbox->addWidget(button); + hbox->addStretch(1); + setLayout(vbox); +} + +NewMailNotifierSelectCollectionWidget::~NewMailNotifierSelectCollectionWidget() +{ + +} + +void NewMailNotifierSelectCollectionWidget::slotCollectionTreeFetched() +{ + if (!mNeedUpdate) { + mNeedUpdate = true; + QTimer::singleShot(1000, this, &NewMailNotifierSelectCollectionWidget::slotUpdateCollectionStatus); + } + mFolderView->expandAll(); +} + +void NewMailNotifierSelectCollectionWidget::slotSetCollectionFilter(const QString &filter) +{ + mCollectionFilter->setFilterWildcard(filter); + mFolderView->expandAll(); +} + +void NewMailNotifierSelectCollectionWidget::slotUpdateCollectionStatus() +{ + updateStatus(QModelIndex()); +} + +void NewMailNotifierSelectCollectionWidget::slotSelectAllCollections() +{ + forceStatus(QModelIndex(), true); +} + +void NewMailNotifierSelectCollectionWidget::slotUnselectAllCollections() +{ + forceStatus(QModelIndex(), false); +} + +void NewMailNotifierSelectCollectionWidget::updateStatus(const QModelIndex &parent) +{ + const int nbCol = mCheckProxy->rowCount(parent); + for (int i = 0; i < nbCol; ++i) { + const QModelIndex child = mCheckProxy->index(i, 0, parent); + + const Akonadi::Collection collection = + mCheckProxy->data(child, Akonadi::EntityTreeModel::CollectionRole).value(); + + Akonadi::NewMailNotifierAttribute *attr = collection.attribute(); + if (!attr || !attr->ignoreNewMail()) { + mCheckProxy->setData(child, Qt::Checked, Qt::CheckStateRole); + } + updateStatus(child); + } + mNeedUpdate = false; +} + +void NewMailNotifierSelectCollectionWidget::forceStatus(const QModelIndex &parent, bool status) +{ + const int nbCol = mCheckProxy->rowCount(parent); + for (int i = 0; i < nbCol; ++i) { + const QModelIndex child = mCheckProxy->index(i, 0, parent); + mCheckProxy->setData(child, status ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + forceStatus(child, status); + } +} + +void NewMailNotifierSelectCollectionWidget::updateCollectionsRecursive(const QModelIndex &parent) +{ + const int nbCol = mCheckProxy->rowCount(parent); + for (int i = 0; i < nbCol; ++i) { + const QModelIndex child = mCheckProxy->index(i, 0, parent); + + Akonadi::Collection collection = + mCheckProxy->data(child, Akonadi::EntityTreeModel::CollectionRole).value(); + + Akonadi::NewMailNotifierAttribute *attr = collection.attribute(); + Akonadi::CollectionModifyJob *modifyJob = Q_NULLPTR; + const bool selected = (mCheckProxy->data(child, Qt::CheckStateRole).value() != 0); + if (selected && attr && attr->ignoreNewMail()) { + collection.removeAttribute(); + modifyJob = new Akonadi::CollectionModifyJob(collection); + modifyJob->setProperty("AttributeAdded", true); + } else if (!selected && (!attr || !attr->ignoreNewMail())) { + attr = collection.attribute(Akonadi::Collection::AddIfMissing); + attr->setIgnoreNewMail(true); + modifyJob = new Akonadi::CollectionModifyJob(collection); + modifyJob->setProperty("AttributeAdded", false); + } + + if (modifyJob) { + connect(modifyJob, &Akonadi::CollectionModifyJob::finished, this, &NewMailNotifierSelectCollectionWidget::slotModifyJobDone); + } + updateCollectionsRecursive(child); + } +} + +void NewMailNotifierSelectCollectionWidget::slotModifyJobDone(KJob *job) +{ + Akonadi::CollectionModifyJob *modifyJob = qobject_cast(job); + if (modifyJob && job->error()) { + if (job->property("AttributeAdded").toBool()) { + qCWarning(NEWMAILNOTIFIER_LOG) << "Failed to append NewMailNotifierAttribute to collection" + << modifyJob->collection().id() << ":" + << job->errorString(); + } else { + qCWarning(NEWMAILNOTIFIER_LOG) << "Failed to remove NewMailNotifierAttribute from collection" + << modifyJob->collection().id() << ":" + << job->errorString(); + } + } +} + diff --git a/agents/newmailnotifier/newmailnotifierselectcollectionwidget.h b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.h new file mode 100644 index 00000000..a45531b8 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + 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 NEWMAILNOTIFIERSELECTCOLLECTIONWIDGET_H +#define NEWMAILNOTIFIERSELECTCOLLECTIONWIDGET_H + +#include +#include +#include + +class QItemSelectionModel; +class KRecursiveFilterProxyModel; +namespace Akonadi +{ +class EntityTreeModel; +class ChangeRecorder; +} +class QTreeView; +class KCheckableProxyModel; +class KJob; + +class NewMailNotifierSelectCollectionWidget : public QWidget +{ + Q_OBJECT +public: + explicit NewMailNotifierSelectCollectionWidget(QWidget *parent = Q_NULLPTR); + ~NewMailNotifierSelectCollectionWidget(); + + void updateCollectionsRecursive(const QModelIndex &parent); + +private Q_SLOTS: + void slotSelectAllCollections(); + void slotUnselectAllCollections(); + void slotModifyJobDone(KJob *job); + void slotUpdateCollectionStatus(); + void slotSetCollectionFilter(const QString &); + + void slotCollectionTreeFetched(); + +private: + void updateStatus(const QModelIndex &parent); + void forceStatus(const QModelIndex &parent, bool status); + QTreeView *mFolderView; + QItemSelectionModel *mSelectionModel; + Akonadi::EntityTreeModel *mModel; + Akonadi::ChangeRecorder *mChangeRecorder; + KCheckableProxyModel *mCheckProxy; + KRecursiveFilterProxyModel *mCollectionFilter; + bool mNeedUpdate; +}; + +#endif // NEWMAILNOTIFIERSELECTCOLLECTIONWIDGET_H diff --git a/agents/newmailnotifier/newmailnotifiersettingsdialog.cpp b/agents/newmailnotifier/newmailnotifiersettingsdialog.cpp new file mode 100644 index 00000000..be057cf0 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiersettingsdialog.cpp @@ -0,0 +1,231 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + 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 "newmailnotifiersettingsdialog.h" +#include "newmailnotifierattribute.h" +#include "newmailnotifierselectcollectionwidget.h" +#include "newmailnotifieragentsettings.h" + +#include "kdepim-runtime-version.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char *textToSpeakMessage = + I18N_NOOP("" + "

Here you can define message. " + "You can use:

" + "
    " + "
  • %s set subject
  • " + "
  • %f set from
  • " + "
" + "
"); + +NewMailNotifierSettingsDialog::NewMailNotifierSettingsDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(i18n("New Mail Notifier settings")); + setWindowIcon(QIcon::fromTheme(QStringLiteral("kmail"))); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help); + QVBoxLayout *mainLayout = new QVBoxLayout; + setLayout(mainLayout); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + okButton->setDefault(true); + okButton->setShortcut(Qt::CTRL | Qt::Key_Return); + connect(buttonBox, &QDialogButtonBox::accepted, this, &NewMailNotifierSettingsDialog::slotOkClicked); + connect(buttonBox, &QDialogButtonBox::rejected, this, &NewMailNotifierSettingsDialog::reject); + + QWidget *w = new QWidget; + mainLayout->addWidget(w); + mainLayout->addWidget(buttonBox); + QVBoxLayout *lay = new QVBoxLayout; + w->setLayout(lay); + QTabWidget *tab = new QTabWidget; + lay->addWidget(tab); + + QWidget *settings = new QWidget; + QVBoxLayout *vbox = new QVBoxLayout; + settings->setLayout(vbox); + + QGroupBox *grp = new QGroupBox(i18n("Choose which fields to show:")); + vbox->addWidget(grp); + QVBoxLayout *groupboxLayout = new QVBoxLayout; + grp->setLayout(groupboxLayout); + + mShowPhoto = new QCheckBox(i18n("Show Photo")); + mShowPhoto->setChecked(NewMailNotifierAgentSettings::showPhoto()); + groupboxLayout->addWidget(mShowPhoto); + + mShowFrom = new QCheckBox(i18n("Show From")); + mShowFrom->setChecked(NewMailNotifierAgentSettings::showFrom()); + groupboxLayout->addWidget(mShowFrom); + + mShowSubject = new QCheckBox(i18n("Show Subject")); + mShowSubject->setChecked(NewMailNotifierAgentSettings::showSubject()); + groupboxLayout->addWidget(mShowSubject); + + mShowFolders = new QCheckBox(i18n("Show Folders")); + mShowFolders->setChecked(NewMailNotifierAgentSettings::showFolder()); + groupboxLayout->addWidget(mShowFolders); + + mExcludeMySelf = new QCheckBox(i18n("Do not notify when email was sent by me")); + mExcludeMySelf->setChecked(NewMailNotifierAgentSettings::excludeEmailsFromMe()); + vbox->addWidget(mExcludeMySelf); + + mAllowToShowMail = new QCheckBox(i18n("Show button to display mail")); + mAllowToShowMail->setChecked(NewMailNotifierAgentSettings::showButtonToDisplayMail()); + vbox->addWidget(mAllowToShowMail); + + vbox->addStretch(); + tab->addTab(settings, i18n("Display")); + +#ifdef HAVE_SPEECH + QWidget *textSpeakWidget = new QWidget; + vbox = new QVBoxLayout; + textSpeakWidget->setLayout(vbox); + mTextToSpeak = new QCheckBox(i18n("Enabled")); + mTextToSpeak->setChecked(NewMailNotifierAgentSettings::textToSpeakEnabled()); + vbox->addWidget(mTextToSpeak); + + QLabel *howIsItWork = new QLabel(i18n("How does this work?")); + howIsItWork->setTextInteractionFlags(Qt::LinksAccessibleByMouse); + howIsItWork->setContextMenuPolicy(Qt::NoContextMenu); + vbox->addWidget(howIsItWork); + connect(howIsItWork, &QLabel::linkActivated, this, &NewMailNotifierSettingsDialog::slotHelpLinkClicked); + + QHBoxLayout *textToSpeakLayout = new QHBoxLayout; + textToSpeakLayout->setMargin(0); + QLabel *lab = new QLabel(i18n("Message:")); + textToSpeakLayout->addWidget(lab); + mTextToSpeakSetting = new QLineEdit; + mTextToSpeakSetting->setClearButtonEnabled(true); + mTextToSpeakSetting->setText(NewMailNotifierAgentSettings::textToSpeak()); + mTextToSpeakSetting->setEnabled(mTextToSpeak->isChecked()); + mTextToSpeakSetting->setWhatsThis(i18n(textToSpeakMessage)); + textToSpeakLayout->addWidget(mTextToSpeakSetting); + vbox->addLayout(textToSpeakLayout); + vbox->addStretch(); + tab->addTab(textSpeakWidget, i18n("Text to Speak")); + connect(mTextToSpeak, &QCheckBox::toggled, mTextToSpeakSetting, &QLineEdit::setEnabled); +#else + mTextToSpeak = Q_NULLPTR; + mTextToSpeakSetting = Q_NULLPTR; +#endif + + mNotify = new KNotifyConfigWidget(this); + mNotify->setApplication(QStringLiteral("akonadi_newmailnotifier_agent")); + tab->addTab(mNotify, i18n("Notify")); + + mSelectCollection = new NewMailNotifierSelectCollectionWidget; + tab->addTab(mSelectCollection, i18n("Folders")); + + KAboutData aboutData = KAboutData( + QStringLiteral("newmailnotifieragent"), + i18n("New Mail Notifier Agent"), + QStringLiteral(KDEPIM_RUNTIME_VERSION), + i18n("Notifies about new mail."), + KAboutLicense::GPL_V2, + i18n("Copyright (C) 2013-2016 Laurent Montel")); + + aboutData.addAuthor(i18n("Laurent Montel"), + i18n("Maintainer"), QStringLiteral("montel@kde.org")); + aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), + i18nc("EMAIL OF TRANSLATORS", "Your emails")); + + KHelpMenu *helpMenu = new KHelpMenu(this, aboutData, true); + //Initialize menu + QMenu *menu = helpMenu->menu(); + helpMenu->action(KHelpMenu::menuAboutApp)->setIcon(QIcon::fromTheme(QStringLiteral("kmail"))); + buttonBox->button(QDialogButtonBox::Help)->setMenu(menu); + readConfig(); +} + +NewMailNotifierSettingsDialog::~NewMailNotifierSettingsDialog() +{ + writeConfig(); +} + +static const char *myConfigGroupName = "NewMailNotifierDialog"; + +void NewMailNotifierSettingsDialog::readConfig() +{ + KConfigGroup group(KSharedConfig::openConfig(), myConfigGroupName); + + const QSize size = group.readEntry("Size", QSize(500, 300)); + if (size.isValid()) { + resize(size); + } +} + +void NewMailNotifierSettingsDialog::writeConfig() +{ + KConfigGroup group(KSharedConfig::openConfig(), myConfigGroupName); + group.writeEntry("Size", size()); + group.sync(); +} + +void NewMailNotifierSettingsDialog::slotHelpLinkClicked(const QString &) +{ + const QString help = + i18n(textToSpeakMessage); + + QWhatsThis::showText(QCursor::pos(), help); +} + +void NewMailNotifierSettingsDialog::slotOkClicked() +{ + mSelectCollection->updateCollectionsRecursive(QModelIndex()); + + NewMailNotifierAgentSettings::setShowPhoto(mShowPhoto->isChecked()); + NewMailNotifierAgentSettings::setShowFrom(mShowFrom->isChecked()); + NewMailNotifierAgentSettings::setShowSubject(mShowSubject->isChecked()); + NewMailNotifierAgentSettings::setShowFolder(mShowFolders->isChecked()); + NewMailNotifierAgentSettings::setExcludeEmailsFromMe(mExcludeMySelf->isChecked()); +#ifdef HAVE_SPEECH + NewMailNotifierAgentSettings::setTextToSpeakEnabled(mTextToSpeak->isChecked()); + NewMailNotifierAgentSettings::setTextToSpeak(mTextToSpeakSetting->text()); +#endif + NewMailNotifierAgentSettings::setShowButtonToDisplayMail(mAllowToShowMail->isChecked()); + NewMailNotifierAgentSettings::self()->save(); + mNotify->save(); + accept(); +} + diff --git a/agents/newmailnotifier/newmailnotifiersettingsdialog.h b/agents/newmailnotifier/newmailnotifiersettingsdialog.h new file mode 100644 index 00000000..dba5cdbc --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiersettingsdialog.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + 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 NEWMAILNOTIFIERSETTINGSDIALOG_H +#define NEWMAILNOTIFIERSETTINGSDIALOG_H + +#include +#include + +class KNotifyConfigWidget; +class QCheckBox; +class QLineEdit; +class NewMailNotifierSelectCollectionWidget; +class NewMailNotifierSettingsDialog : public QDialog +{ + Q_OBJECT +public: + explicit NewMailNotifierSettingsDialog(QWidget *parent = Q_NULLPTR); + ~NewMailNotifierSettingsDialog(); + +private Q_SLOTS: + void slotOkClicked(); + void slotHelpLinkClicked(const QString &); + +private: + void writeConfig(); + void readConfig(); + QCheckBox *mShowPhoto; + QCheckBox *mShowFrom; + QCheckBox *mShowSubject; + QCheckBox *mShowFolders; + QCheckBox *mExcludeMySelf; + QCheckBox *mAllowToShowMail; + KNotifyConfigWidget *mNotify; + QCheckBox *mTextToSpeak; + QLineEdit *mTextToSpeakSetting; + NewMailNotifierSelectCollectionWidget *mSelectCollection; +}; + +#endif // NEWMAILNOTIFIERSETTINGSDIALOG_H diff --git a/agents/newmailnotifier/newmailnotifiershowmessagejob.cpp b/agents/newmailnotifier/newmailnotifiershowmessagejob.cpp new file mode 100644 index 00000000..44c25e44 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiershowmessagejob.cpp @@ -0,0 +1,59 @@ +/* + Copyright (c) 2014 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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 +*/ + +#include "newmailnotifiershowmessagejob.h" +#include "newmailnotifier_debug.h" +#include +#include +#include +#include +#include + +NewMailNotifierShowMessageJob::NewMailNotifierShowMessageJob(Akonadi::Item::Id id, QObject *parent) + : KJob(parent), + mId(id) +{ +} + +NewMailNotifierShowMessageJob::~NewMailNotifierShowMessageJob() +{ +} + +void NewMailNotifierShowMessageJob::start() +{ + if (mId < 0) { + Q_EMIT emitResult(); + return; + } + const QString kmailInterface = QStringLiteral("org.kde.kmail"); + QDBusReply reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(kmailInterface); + if (!reply.isValid() || !reply.value()) { + // Program is not already running, so start it + QString errmsg; + if (KToolInvocation::startServiceByDesktopName(QStringLiteral("kmail2"), QString(), &errmsg)) { + qCDebug(NEWMAILNOTIFIER_LOG) << " Can not start kmail" << errmsg; + setError(UserDefinedError); + Q_EMIT emitResult(); + return; + } + } + QDBusInterface kmail(kmailInterface, QStringLiteral("/KMail"), QStringLiteral("org.kde.kmail.kmail")); + if (kmail.isValid()) { + kmail.call(QStringLiteral("showMail"), mId); + } + Q_EMIT emitResult(); +} diff --git a/agents/newmailnotifier/newmailnotifiershowmessagejob.h b/agents/newmailnotifier/newmailnotifiershowmessagejob.h new file mode 100644 index 00000000..2ae0b827 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiershowmessagejob.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2014 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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 +*/ + +#ifndef NEWMAILNOTIFIERSHOWMESSAGEJOB_H +#define NEWMAILNOTIFIERSHOWMESSAGEJOB_H + +#include +#include + +class NewMailNotifierShowMessageJob : public KJob +{ + Q_OBJECT +public: + explicit NewMailNotifierShowMessageJob(Akonadi::Item::Id id, QObject *parent = Q_NULLPTR); + ~NewMailNotifierShowMessageJob(); + + void start() Q_DECL_OVERRIDE; + +private: + Akonadi::Item::Id mId; + +}; + +#endif // NEWMAILNOTIFIERSHOWMESSAGEJOB_H diff --git a/agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml b/agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml new file mode 100644 index 00000000..c7361324 --- /dev/null +++ b/agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/agents/newmailnotifier/specialnotifierjob.cpp b/agents/newmailnotifier/specialnotifierjob.cpp new file mode 100644 index 00000000..349f250a --- /dev/null +++ b/agents/newmailnotifier/specialnotifierjob.cpp @@ -0,0 +1,181 @@ +/* + Copyright (c) 2013-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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 +*/ + +#include "newmailnotifiershowmessagejob.h" +#include "specialnotifierjob.h" +#include "newmailnotifieragentsettings.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include "newmailnotifier_debug.h" + +#include +#ifdef HAVE_SPEECH +#include +#endif + +SpecialNotifierJob::SpecialNotifierJob(const QStringList &listEmails, const QString &path, Akonadi::Item::Id id, QObject *parent) + : QObject(parent), + mListEmails(listEmails), + mPath(path), + mItemId(id) +{ + Akonadi::Item item(mItemId); + Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item, this); + job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); + + connect(job, &Akonadi::ItemFetchJob::result, this, &SpecialNotifierJob::slotItemFetchJobDone); +} + +SpecialNotifierJob::~SpecialNotifierJob() +{ + +} + +void SpecialNotifierJob::setDefaultPixmap(const QPixmap &pixmap) +{ + mDefaultPixmap = pixmap; +} + +void SpecialNotifierJob::slotItemFetchJobDone(KJob *job) +{ + if (job->error()) { + qCWarning(NEWMAILNOTIFIER_LOG) << job->errorString(); + deleteLater(); + return; + } + + const Akonadi::Item::List lst = qobject_cast(job)->items(); + if (lst.count() == 1) { + const Akonadi::Item item = lst.first(); + if (!item.hasPayload()) { + qCDebug(NEWMAILNOTIFIER_LOG) << " message has not payload."; + deleteLater(); + return; + } + + const KMime::Message::Ptr mb = item.payload(); + mFrom = mb->from()->asUnicodeString(); + mSubject = mb->subject()->asUnicodeString(); + if (NewMailNotifierAgentSettings::showPhoto()) { + Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(this); + job->setLimit(1); + job->setQuery(Akonadi::ContactSearchJob::Email, KEmailAddress::firstEmailAddress(mFrom).toLower(), Akonadi::ContactSearchJob::ExactMatch); + connect(job, &Akonadi::ItemFetchJob::result, this, &SpecialNotifierJob::slotSearchJobFinished); + } else { + emitNotification(mDefaultPixmap); + deleteLater(); + } + } else { + qCWarning(NEWMAILNOTIFIER_LOG) << " Found item different from 1: " << lst.count(); + deleteLater(); + return; + } +} + +void SpecialNotifierJob::slotSearchJobFinished(KJob *job) +{ + const Akonadi::ContactSearchJob *searchJob = qobject_cast(job); + if (searchJob->error()) { + qCWarning(NEWMAILNOTIFIER_LOG) << "Unable to fetch contact:" << searchJob->errorText(); + emitNotification(mDefaultPixmap); + return; + } + if (!searchJob->contacts().isEmpty()) { + const KContacts::Addressee addressee = searchJob->contacts().at(0); + const KContacts::Picture photo = addressee.photo(); + const QImage image = photo.data(); + if (image.isNull()) { + emitNotification(mDefaultPixmap); + } else { + emitNotification(QPixmap::fromImage(image)); + } + } else { + emitNotification(mDefaultPixmap); + } +} + +void SpecialNotifierJob::emitNotification(const QPixmap &pixmap) +{ + if (NewMailNotifierAgentSettings::excludeEmailsFromMe()) { + Q_FOREACH (const QString &email, mListEmails) { + if (mFrom.contains(email)) { + //Exclude this notification + deleteLater(); + return; + } + } + } + + QStringList result; + if (NewMailNotifierAgentSettings::showFrom()) { + result << i18n("From: %1", mFrom.toHtmlEscaped()); + } + if (NewMailNotifierAgentSettings::showSubject()) { + QString subject = mSubject.simplified(); + if (subject.length() > 80) { + subject.truncate(80); + subject += QStringLiteral("..."); + } + result << i18n("Subject: %1", subject.toHtmlEscaped()); + } + if (NewMailNotifierAgentSettings::showFolder()) { + result << i18n("In: %1", mPath); + } + + if (NewMailNotifierAgentSettings::textToSpeakEnabled()) { + if (!NewMailNotifierAgentSettings::textToSpeak().isEmpty()) { +#ifdef HAVE_SPEECH + QTextToSpeech *speech = new QTextToSpeech(this); + QString message = NewMailNotifierAgentSettings::textToSpeak(); + message.replace(QStringLiteral("%s"), mSubject.toHtmlEscaped()); + message.replace(QStringLiteral("%f"), mFrom.toHtmlEscaped()); + speech->say(message); +#endif + } + } + + if (NewMailNotifierAgentSettings::showButtonToDisplayMail()) { + KNotification *notification = new KNotification(QStringLiteral("new-email"), Q_NULLPTR, KNotification::CloseOnTimeout); + notification->setText(result.join(QStringLiteral("\n"))); + notification->setPixmap(pixmap); + notification->setActions(QStringList() << i18n("Show mail...")); + + connect(notification, static_cast(&KNotification::activated), this, &SpecialNotifierJob::slotOpenMail); + connect(notification, &KNotification::closed, this, &SpecialNotifierJob::deleteLater); + + notification->sendEvent(); + } else { + Q_EMIT displayNotification(pixmap, result.join(QStringLiteral("\n"))); + deleteLater(); + } +} + +void SpecialNotifierJob::slotOpenMail() +{ + NewMailNotifierShowMessageJob *job = new NewMailNotifierShowMessageJob(mItemId); + job->start(); +} diff --git a/agents/newmailnotifier/specialnotifierjob.h b/agents/newmailnotifier/specialnotifierjob.h new file mode 100644 index 00000000..ddd465d3 --- /dev/null +++ b/agents/newmailnotifier/specialnotifierjob.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 2013-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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 +*/ + +#ifndef SPECIALNOTIFIERJOB_H +#define SPECIALNOTIFIERJOB_H + +#include +#include +#include +#include +class KJob; + +class SpecialNotifierJob : public QObject +{ + Q_OBJECT +public: + explicit SpecialNotifierJob(const QStringList &listEmails, const QString &path, Akonadi::Item::Id id, QObject *parent = Q_NULLPTR); + ~SpecialNotifierJob(); + + void setDefaultPixmap(const QPixmap &pixmap); + +Q_SIGNALS: + void displayNotification(const QPixmap &pixmap, const QString &message); + +private Q_SLOTS: + void slotSearchJobFinished(KJob *job); + void slotItemFetchJobDone(KJob *); + void slotOpenMail(); +private: + void emitNotification(const QPixmap &pixmap); + QPixmap mDefaultPixmap; + QStringList mListEmails; + QString mSubject; + QString mFrom; + QString mPath; + Akonadi::Item::Id mItemId; +}; + +#endif // SPECIALNOTIFIERJOB_H diff --git a/akonadi-prefix.h.cmake b/akonadi-prefix.h.cmake new file mode 100644 index 00000000..b2753f10 --- /dev/null +++ b/akonadi-prefix.h.cmake @@ -0,0 +1,6 @@ +/* This file contains all the paths that change when changing the installation prefix */ + +#define AKONADIPREFIX "${CMAKE_INSTALL_PREFIX}" +#define AKONADIDATA "${SHARE_INSTALL_PREFIX}" +#define AKONADICONFIG "${CONFIG_INSTALL_DIR}" + diff --git a/akonadi-version.h.cmake b/akonadi-version.h.cmake new file mode 100644 index 00000000..58c028e4 --- /dev/null +++ b/akonadi-version.h.cmake @@ -0,0 +1,25 @@ +/* + This file is part of kdepim. + Copyright (C) 2009 Christophe Giboudeaux + + 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 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 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_VERSION_H +#define AKONADI_VERSION_H + +#define AKONADI_VERSION "@AKONADI_VERSION@" + +#endif // AKONADI_VERSION_H \ No newline at end of file diff --git a/cmake/modules/FindXsltproc.cmake b/cmake/modules/FindXsltproc.cmake new file mode 100644 index 00000000..45b46cfc --- /dev/null +++ b/cmake/modules/FindXsltproc.cmake @@ -0,0 +1,32 @@ +# Find xsltproc executable and provide a macro to generate D-Bus interfaces. +# +# The following variables are defined : +# XSLTPROC_EXECUTABLE - path to the xsltproc executable +# Xsltproc_FOUND - true if the program was found +# +find_program(XSLTPROC_EXECUTABLE xsltproc DOC "Path to the xsltproc executable") +mark_as_advanced(XSLTPROC_EXECUTABLE) + +if(XSLTPROC_EXECUTABLE) + set(Xsltproc_FOUND TRUE) + + # We depend on kdepimlibs, make sure it's found + if(NOT DEFINED KF5Akonadi_DATA_DIR) + find_package(KF5Akonadi REQUIRED) + endif() + + + # Macro to generate a D-Bus interface description from a KConfigXT file + macro(kcfg_generate_dbus_interface _kcfg _name) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + COMMAND ${XSLTPROC_EXECUTABLE} --stringparam interfaceName ${_name} + ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + > ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + DEPENDS ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + ) + endmacro() +endif() + diff --git a/defaultsetup/CMakeLists.txt b/defaultsetup/CMakeLists.txt new file mode 100644 index 00000000..c0394b8e --- /dev/null +++ b/defaultsetup/CMakeLists.txt @@ -0,0 +1,10 @@ +configure_file(defaultaddressbook.desktop ${CMAKE_CURRENT_BINARY_DIR}/defaultaddressbook) +configure_file(defaultcalendar.desktop ${CMAKE_CURRENT_BINARY_DIR}/defaultcalendar) +configure_file(defaultnotebook.desktop ${CMAKE_CURRENT_BINARY_DIR}/defaultnotebook) +configure_file(birthdaycalendar.desktop ${CMAKE_CURRENT_BINARY_DIR}/birthdaycalendar) + +install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/defaultcalendar + ${CMAKE_CURRENT_BINARY_DIR}/defaultaddressbook + ${CMAKE_CURRENT_BINARY_DIR}/defaultnotebook + ${CMAKE_CURRENT_BINARY_DIR}/birthdaycalendar + DESTINATION ${KDE_INSTALL_DATADIR}/akonadi/firstrun ) diff --git a/defaultsetup/birthdaycalendar.desktop b/defaultsetup/birthdaycalendar.desktop new file mode 100644 index 00000000..53aa1e80 --- /dev/null +++ b/defaultsetup/birthdaycalendar.desktop @@ -0,0 +1,3 @@ +[Agent] +Id=birthdaycalendar +Type=akonadi_birthdays_resource diff --git a/defaultsetup/defaultaddressbook.desktop b/defaultsetup/defaultaddressbook.desktop new file mode 100644 index 00000000..cb010138 --- /dev/null +++ b/defaultsetup/defaultaddressbook.desktop @@ -0,0 +1,56 @@ +[Agent] +Id=defaultaddressbook +Type=akonadi_contacts_resource +Name=Personal Contacts +Name[ast]=Contautos personales +Name[bg]=Лични контакти +Name[bs]=Lični kontakti +Name[ca]=Contactes personals +Name[ca@valencia]=Contactes personals +Name[cs]=Osobní kontakty +Name[da]=Personlige kontakter +Name[de]=Persönliche Kontakte +Name[el]=Προσωπικές επαφές +Name[en_GB]=Personal Contacts +Name[es]=Contactos personales +Name[et]=Isiklikud kontaktid +Name[fi]=Omat yhteystiedot +Name[fr]=Contacts personnels +Name[ga]=Teagmhálacha Pearsanta +Name[gl]=Contactos Persoais +Name[hu]=Személyes névjegyek +Name[ia]=Contactos personal +Name[it]=Contatti personali +Name[ja]=個人の連絡先 +Name[kk]=Дербес контакттар +Name[km]=ទំនាក់ទំនង​ផ្ទាល់ខ្លួន +Name[ko]=개인 연락처 +Name[lt]=Asmeniniai kontaktai +Name[lv]=Personīgie kontakti +Name[nb]=Personlige kontakter +Name[nds]=Persöönlich Kontakten +Name[nl]=Persoonlijke contacten +Name[nn]=Personlege kontaktar +Name[pa]=ਨਿੱਜੀ ਸੰਪਰਕ +Name[pl]=Kontakty osobiste +Name[pt]=Contactos Pessoais +Name[pt_BR]=Contatos pessoais +Name[ro]=Contacte personale +Name[ru]=Личные контакты +Name[sk]=Osobné kontakty +Name[sl]=Osebni stiki +Name[sr]=Лични контакти +Name[sr@ijekavian]=Лични контакти +Name[sr@ijekavianlatin]=Lični kontakti +Name[sr@latin]=Lični kontakti +Name[sv]=Personliga kontakter +Name[tr]=Kişisel Bağlantılar +Name[ug]=شەخسىي ئالاقەداشلار +Name[uk]=Особисті контакти +Name[x-test]=xxPersonal Contactsxx +Name[zh_CN]=个人联系人 +Name[zh_TW]=個人聯絡人 + +[Settings] +IsConfigured=true +Path[$e]=$HOME/.local/share/contacts/ diff --git a/defaultsetup/defaultcalendar.desktop b/defaultsetup/defaultcalendar.desktop new file mode 100644 index 00000000..2b8c8c99 --- /dev/null +++ b/defaultsetup/defaultcalendar.desktop @@ -0,0 +1,52 @@ +[Agent] +Id=defaultcalendar +Type=akonadi_ical_resource +Name=Personal Calendar +Name[ast]=Calendariu personal +Name[bg]=Личен календар +Name[bs]=Lični kalendar +Name[ca]=Calendari personal +Name[ca@valencia]=Calendari personal +Name[cs]=Osobní kalendář +Name[da]=Personlig kalender +Name[de]=Persönlicher Kalender +Name[el]=Προσωπικό ημερολόγιο +Name[en_GB]=Personal Calendar +Name[es]=Calendario personal +Name[et]=Isiklik kalender +Name[fi]=Oma kalenteri +Name[fr]=Agenda personnel +Name[ga]=Féilire Pearsanta +Name[gl]=Calendario persoal +Name[hu]=Személyes naptár +Name[ia]=Calendario Personal +Name[it]=Calendario personale +Name[kk]=Дербес күнтізбе +Name[km]=ប្រតិទិន​ផ្ទាល់ខ្លួន +Name[ko]=개인 달력 +Name[lt]=Asmeninis kalendorius +Name[lv]=Personīgais kalendārs +Name[nb]=Personlig kalender +Name[nds]=Persöönlich Kalenner +Name[nl]=Persoonlijke agenda +Name[pl]=Kalendarz osobisty +Name[pt]=Calendário Pessoal +Name[pt_BR]=Calendário pessoal +Name[ro]=Calendar personal +Name[ru]=Личный календарь +Name[sk]=Osobný kalendár +Name[sl]=Osebni koledar +Name[sr]=Лични календар +Name[sr@ijekavian]=Лични календар +Name[sr@ijekavianlatin]=Lični kalendar +Name[sr@latin]=Lični kalendar +Name[sv]=Personlig kalender +Name[tr]=Kişisel Takvim +Name[uk]=Особистий календар +Name[x-test]=xxPersonal Calendarxx +Name[zh_CN]=个人日历 +Name[zh_TW]=個人行事曆 + +[Settings] +Path[$e]=$HOME/.local/share/apps/korganizer/std.ics + diff --git a/defaultsetup/defaultnotebook.desktop b/defaultsetup/defaultnotebook.desktop new file mode 100644 index 00000000..753431af --- /dev/null +++ b/defaultsetup/defaultnotebook.desktop @@ -0,0 +1,79 @@ +[Agent] +Id=defaultnotebook +Type=akonadi_akonotes_resource +Name=Notes +Name[af]=Notas +Name[ar]=ملاحظات +Name[ast]=Notes +Name[be]=Заметкі +Name[bg]=Бележки +Name[br]=Notennoù +Name[bs]=Bilješke +Name[ca]=Notes +Name[ca@valencia]=Notes +Name[cs]=Poznámky +Name[cy]=Nodiadau +Name[da]=Noter +Name[de]=Notizen +Name[el]=Σημειώσεις +Name[en_GB]=Notes +Name[eo]=Notoj +Name[es]=Notas +Name[et]=Sedelid +Name[eu]=Oharrak +Name[fa]=یادداشتها +Name[fi]=Muistiinpanot +Name[fr]=Notes +Name[fy]=Notysjes +Name[ga]=Nótaí +Name[gl]=Notas +Name[he]=פתקים +Name[hu]=Feljegyzések +Name[ia]=Notas +Name[is]=Minnismiðar +Name[it]=Note +Name[ja]=メモ +Name[ka]=ჩანიშვნები +Name[kk]=Жазбалар +Name[km]=ចំណាំ +Name[ko]=노트 +Name[lt]=Užrašai +Name[lv]=Piezīmes +Name[mai]=टिप्पणी +Name[mk]=Белешки +Name[ms]=Nota +Name[nb]=Notater +Name[nds]=Notizen +Name[ne]=टिपोट +Name[nl]=Notities +Name[nn]=Notat +Name[pa]=ਨੋਟਿਸ +Name[pl]=Notatki +Name[pt]=Notas +Name[pt_BR]=Notas +Name[ro]=Notițe +Name[ru]=Заметки +Name[se]=Nohtat +Name[sk]=Poznámky +Name[sl]=Sporočilca +Name[sq]=Shënimet +Name[sr]=Белешке +Name[sr@ijekavian]=Биљешке +Name[sr@ijekavianlatin]=Bilješke +Name[sr@latin]=Beleške +Name[sv]=Anteckningar +Name[ta]=குறிப்புகள் +Name[tg]=Ахборот +Name[th]=บันทึกย่อ +Name[tr]=Notlar +Name[ug]=ئىزاھ +Name[uk]=Примітки +Name[uz]=Yozma xotira +Name[uz@cyrillic]=Ёзма хотира +Name[wa]=Notes +Name[x-test]=xxNotesxx +Name[zh_CN]=便笺 +Name[zh_TW]=備忘錄 + +[Settings] +Path[$e]=$HOME/.local/share/notes/ diff --git a/doc/git-migration.txt b/doc/git-migration.txt new file mode 100644 index 00000000..2908dd65 --- /dev/null +++ b/doc/git-migration.txt @@ -0,0 +1,7 @@ +Migrated to git using the following: + +svn2git: http://gitorious.org/svn2git version: 409d8bc4cbaade82672f251c45178c3cfed4619d +kde-ruleset: http://projects.kde.org/projects/playground/sdk/kde-ruleset/ version: d71b11d9434d951bdaaff0f7cd3de9dcae000018 +kde svn repo synced at revision: 1208541 + +command: time ionice -c3 nice svn-all-fast-export --identity-map ../kde-ruleset/account-map --rules ../kde-ruleset/kdepim-runtime-rules --add-metadata --debug-rules --stats --svn-branches ../svn/ diff --git a/doc/libakonadi.xmi b/doc/libakonadi.xmi new file mode 100644 index 00000000..72f35f35 --- /dev/null +++ b/doc/libakonadi.xmi @@ -0,0 +1,9639 @@ + + + + + umbrello uml modeller http://uml.sf.net + 1.5.3 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+
diff --git a/doc/pics/akonadi_agent_handling.eps b/doc/pics/akonadi_agent_handling.eps new file mode 100644 index 00000000..974edc15 --- /dev/null +++ b/doc/pics/akonadi_agent_handling.eps @@ -0,0 +1,1142 @@ +%!PS-Adobe-1.0 EPSF-3.0 +%%BoundingBox: 35 656 610 819 +%%Creator: Qt 3.3.6 +%%CreationDate: Do Sep 28 16:06:56 2006 +%%Orientation: Portrait +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman + +%%EndComments +%%BeginProlog +% Prolog copyright 1994-2005 Trolltech. You may copy this prolog in any way +% that is directly related to this document. For other use of this prolog, +% see your licensing agreement for Qt. +/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D +/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D +/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D +/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read +pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end +d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi +false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88 +0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{ +LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{ +gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get +SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7 +bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL +0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB +exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL +ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1 +eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if +64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3 +i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll +putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3 +1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D +/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3 +colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1 +QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray +QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2 +add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq +{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC +imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel +where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d +/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7 +DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d +/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height +h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4 +DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{ +pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC +WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{ +1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie} +if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4 +2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg +RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0 +exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if +BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h +add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0 +6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT +}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h +D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div +add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1 +ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT +0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy +MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R +{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h +ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry +D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y +w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul +200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90 +x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0 +-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h +ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale +NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS} +D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT +x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP +ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255 +div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0 +B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25 +/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true +exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3 +-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch +/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup +maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding +fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length +dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end +definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d} +D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty +MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch +stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT +1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0 +exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore +showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop +pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D +/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt +ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP} +D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D + +/LArr[ [] [] [ 10.417 3.125 ] [ 3.125 10.417 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d +/pageinit { +35.52 24 translate +% 185*280mm (portrait) +0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d +%%EndProlog +%%BeginSetup +% Fonts and encodings used +/BitstreamVeraSerif-RomanList [ +[ /BitstreamVeraSerif-Roman 1.0 0.0 ] + [ /BitstreamVeraSerif 1.0 0.0 ] + [ /Helvetica 0.988 0.000 ] +] d +%%BeginFont: Bitstream Vera Serif +%!PS-Adobe-3.0 Resource-Font +%%Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. +%%Creator: Converted from TrueType by Qt +25 dict begin +/_d{bind def}bind def +/_m{moveto}_d +/_l{lineto}_d +/_cl{closepath eofill}_d +/_c{curveto}_d +/_sc{7 -1 roll{setcachedevice}{pop pop pop pop pop pop}ifelse}_d +/_e{exec}_d +/FontName /BitstreamVeraSerif-Roman def +/PaintType 0 def +/FontMatrix[.001 0 0 .001 0 0]def +/FontBBox[-182 -235 1287 928]def +/FontType 3 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (Bitstream Vera Serif) def +/FullName (Bitstream Vera Serif) def +/Notice (Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.) def +/Weight (Roman) def +/Version (Release 1.10) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -213 def +/UnderlineThickness 133 def +end readonly def +/CharStrings 32 dict dup begin +/.notdef{600 0 50 -176 550 705 _sc +50 -176 _m +50 705 _l +550 705 _l +550 -176 _l +50 -176 _l +106 -120 _m +494 -120 _l +494 649 _l +106 649 _l +106 -120 _l +_cl}_d +/space{318 0 0 0 0 0 _sc +}_d +/parenleft{390 0 79 -155 319 760 _sc +319 -155 _m +239 -119 179 -63 139 13 _c +99 89 79 186 79 302 _c +79 418 99 514 139 591 _c +179 668 239 724 319 760 _c +319 712 _l +269 677 233 628 211 566 _c +189 503 178 415 178 302 _c +178 188 189 100 211 38 _c +233 -24 269 -72 319 -107 _c +319 -155 _l +_cl}_d +/parenright{390 0 71 -155 311 760 _sc +71 -155 _m +71 -107 _l +121 -72 157 -24 179 38 _c +201 100 212 188 212 302 _c +212 415 201 503 179 566 _c +157 628 121 677 71 712 _c +71 760 _l +150 724 210 668 250 591 _c +290 514 311 418 311 302 _c +311 186 290 89 250 13 _c +210 -63 150 -119 71 -155 _c +_cl}_d +/colon{337 0 104 -13 234 434 _sc +104 51 _m +104 69 110 84 123 97 _c +135 109 151 116 169 116 _c +187 116 202 109 215 97 _c +227 84 234 69 234 51 _c +234 32 227 17 215 5 _c +203 -7 187 -13 169 -13 _c +150 -13 134 -7 122 5 _c +110 17 104 32 104 51 _c +104 369 _m +104 387 110 402 123 415 _c +135 427 151 434 169 434 _c +187 434 203 427 215 415 _c +227 403 234 387 234 369 _c +234 350 227 334 215 322 _c +203 310 187 304 169 304 _c +151 304 135 310 123 323 _c +110 335 104 351 104 369 _c +_cl}_d +/A{722 0 -5 0 732 729 _sc +200 264 _m +468 264 _l +334 611 _l +200 264 _l +-5 0 _m +-5 52 _l +58 52 _l +318 729 _l +400 729 _l +660 52 _l +732 52 _l +732 0 _l +467 0 _l +467 52 _l +548 52 _l +487 212 _l +180 212 _l +119 52 _l +199 52 _l +199 0 _l +-5 0 _l +_cl}_d +/B{{735 0 55 0 674 729 _sc +247 52 _m +393 52 _l +451 52 494 64 521 90 _c +548 115 562 155 562 211 _c +562 265 548 305 521 331 _c +494 356 451 369 393 369 _c +247 369 _l +247 52 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +415 729 _l +488 729 543 714 581 684 _c +618 654 637 609 637 549 _c +637 505 624 471 598 445 _c +572 419 535 404 485 398 _c +547 390 594 370 626 338 _c +658 306 674 264 674 211 _c +674 139 651 85 605 51 _c +559 17 488 0 392 0 _c +55 0 _l +247 421 _m +371 421 _l +424 421 463 431 488 451 _c +512 471 525 504 525 549 _c +}_e{525 593 512 626 488 646 _c +463 666 424 677 371 677 _c +247 677 _l +247 421 _l +_cl}_e}_d +/C{{765 0 56 -13 705 742 _sc +705 193 _m +683 125 647 73 597 39 _c +546 4 482 -13 405 -13 _c +357 -13 312 -5 272 11 _c +231 27 195 50 164 82 _c +127 118 100 160 82 206 _c +64 252 56 305 56 364 _c +56 477 88 568 154 638 _c +219 707 305 742 413 742 _c +453 742 495 736 540 726 _c +584 716 633 700 685 679 _c +685 511 _l +630 511 _l +618 572 593 617 557 646 _c +521 675 470 690 405 690 _c +327 690 268 662 228 607 _c +188 551 168 470 168 364 _c +168 257 188 176 228 121 _c +268 65 327 38 405 38 _c +459 38 503 51 539 77 _c +574 103 599 141 615 193 _c +705 193 _l +_cl}_e}_d +/D{802 0 55 0 744 729 _sc +247 52 _m +338 52 _l +432 52 505 79 556 133 _c +606 187 632 264 632 365 _c +632 466 606 543 556 597 _c +505 650 432 677 338 677 _c +247 677 _l +247 52 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +345 729 _l +471 729 569 697 639 633 _c +709 569 744 479 744 365 _c +744 250 708 160 638 96 _c +568 32 470 0 345 0 _c +55 0 _l +_cl}_d +/I{395 0 55 0 340 729 _sc +247 52 _m +340 52 _l +340 0 _l +55 0 _l +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +340 729 _l +340 677 _l +247 677 _l +247 52 _l +_cl}_d +/M{1024 0 50 0 973 729 _sc +55 0 _m +55 52 _l +148 52 _l +148 677 _l +50 677 _l +50 729 _l +262 729 _l +518 210 _l +774 729 _l +973 729 _l +973 677 _l +876 677 _l +876 52 _l +969 52 _l +969 0 _l +684 0 _l +684 52 _l +777 52 _l +777 615 _l +527 107 _l +458 107 _l +208 615 _l +208 52 _l +301 52 _l +301 0 _l +55 0 _l +_cl}_d +/O{{820 0 56 -13 764 742 _sc +410 38 _m +490 38 550 65 591 120 _c +631 175 652 256 652 364 _c +652 471 631 552 591 607 _c +550 662 490 690 410 690 _c +330 690 269 662 229 607 _c +188 552 168 471 168 364 _c +168 256 188 175 229 120 _c +269 65 330 38 410 38 _c +410 -13 _m +360 -13 315 -5 273 11 _c +231 27 195 50 164 82 _c +127 118 100 160 82 206 _c +64 252 56 304 56 364 _c +56 422 64 475 82 521 _c +100 567 127 609 164 646 _c +196 678 232 702 273 718 _c +314 734 360 742 410 742 _c +516 742 601 707 666 638 _c +731 568 764 477 764 364 _c +764 305 755 252 737 206 _c +719 159 692 118 656 82 _c +624 50 587 26 546 10 _c +505 -5 460 -13 410 -13 _c +_cl}_e}_d +/Q{{820 0 56 -159 764 742 _sc +422 -13 _m +310 -13 221 21 155 89 _c +89 157 56 248 56 364 _c +56 422 64 475 82 521 _c +100 567 127 609 164 646 _c +196 678 232 702 273 718 _c +314 734 360 742 410 742 _c +516 742 601 707 666 638 _c +731 568 764 477 764 364 _c +764 267 739 186 691 120 _c +642 54 575 13 489 -5 _c +506 -27 527 -43 553 -53 _c +578 -63 608 -69 644 -69 _c +659 -69 _l +659 -159 _l +604 -156 557 -142 518 -118 _c +478 -94 446 -59 422 -13 _c +410 38 _m +490 38 550 65 591 120 _c +631 175 652 256 652 364 _c +652 471 631 552 591 607 _c +550 662 490 690 410 690 _c +330 690 269 662 229 607 _c +188 552 168 471 168 364 _c +168 256 188 175 229 120 _c +269 65 330 38 410 38 _c +_cl}_e}_d +/R{{753 0 55 0 777 729 _sc +479 362 _m +501 356 521 345 538 330 _c +554 315 569 294 582 268 _c +688 52 _l +777 52 _l +777 0 _l +605 0 _l +491 232 _l +469 276 449 305 431 319 _c +413 332 388 339 356 339 _c +247 339 _l +247 52 _l +350 52 _l +350 0 _l +55 0 _l +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +425 729 _l +495 729 550 712 589 678 _c +627 644 647 596 647 534 _c +647 484 633 444 605 416 _c +577 387 535 369 479 362 _c +247 391 _m +391 391 _l +440 391 476 402 500 426 _c +523 449 535 485 535 534 _c +}_e{535 582 523 618 500 642 _c +476 665 440 677 391 677 _c +247 677 _l +247 391 _l +_cl}_e}_d +/a{{596 0 50 -13 568 533 _sc +398 163 _m +398 273 _l +282 273 _l +237 273 204 263 182 244 _c +160 224 150 195 150 156 _c +150 120 161 91 183 70 _c +205 48 235 38 273 38 _c +310 38 340 49 363 72 _c +386 95 398 125 398 163 _c +488 324 _m +488 52 _l +568 52 _l +568 0 _l +398 0 _l +398 56 _l +378 32 355 14 329 3 _c +303 -7 272 -13 238 -13 _c +180 -13 134 2 100 32 _c +66 62 50 104 50 156 _c +50 209 69 250 108 280 _c +146 310 201 325 272 325 _c +398 325 _l +398 361 _l +398 400 386 430 362 452 _c +338 474 304 485 261 485 _c +225 485 197 476 176 460 _c +154 444 141 420 136 388 _c +90 388 _l +}_e{90 493 _l +121 506 151 516 181 523 _c +210 529 239 533 267 533 _c +339 533 393 515 431 479 _c +469 443 488 392 488 324 _c +_cl}_e}_d +/b{{640 0 29 -13 590 760 _sc +115 52 _m +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 438 _l +222 470 244 494 272 510 _c +299 525 333 533 373 533 _c +437 533 489 507 529 457 _c +569 407 590 341 590 260 _c +590 178 569 112 529 62 _c +489 12 437 -13 373 -13 _c +333 -13 299 -5 272 9 _c +244 24 222 48 205 81 _c +205 0 _l +29 0 _l +29 52 _l +115 52 _l +205 234 _m +205 171 217 123 241 91 _c +265 58 299 42 345 42 _c +391 42 425 60 449 97 _c +473 133 485 188 485 260 _c +485 332 473 386 449 422 _c +425 458 391 477 345 477 _c +299 477 265 460 241 427 _c +217 394 205 347 205 285 _c +205 234 _l +}_e{_cl}_e}_d +/c{{560 0 50 -13 514 533 _sc +514 156 _m +501 100 477 58 441 30 _c +405 1 358 -13 301 -13 _c +225 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 73 408 119 458 _c +165 508 225 533 301 533 _c +333 533 366 529 399 521 _c +431 513 464 502 497 487 _c +497 354 _l +445 354 _l +438 399 423 432 400 453 _c +377 474 344 485 302 485 _c +253 485 216 466 192 428 _c +167 390 155 334 155 260 _c +155 184 167 128 192 90 _c +216 52 253 34 302 34 _c +340 34 371 44 394 64 _c +417 84 433 115 442 156 _c +514 156 _l +_cl}_e}_d +/d{{640 0 50 -13 611 760 _sc +525 52 _m +611 52 _l +611 0 _l +435 0 _l +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 708 _l +350 708 _l +350 760 _l +525 760 _l +525 52 _l +435 234 _m +435 285 _l +435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +}_e{_cl}_e}_d +/e{{592 0 50 -13 542 533 _sc +542 250 _m +155 250 _l +155 246 _l +155 176 168 123 194 87 _c +220 51 259 34 311 34 _c +350 34 382 44 408 65 _c +433 85 451 116 461 157 _c +533 157 _l +519 100 492 57 454 29 _c +415 1 364 -13 302 -13 _c +226 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 72 408 118 458 _c +163 508 222 533 296 533 _c +374 533 435 508 477 460 _c +519 412 540 342 542 250 _c +436 302 _m +434 362 421 408 397 439 _c +373 469 340 485 296 485 _c +254 485 222 469 198 438 _c +174 407 160 362 155 302 _c +436 302 _l +_cl}_e}_d +/g{{640 0 50 -221 611 533 _sc +525 467 _m +525 11 _l +525 -63 504 -120 463 -160 _c +422 -200 364 -221 288 -221 _c +254 -221 221 -218 190 -212 _c +158 -206 128 -196 100 -184 _c +100 -75 _l +147 -75 _l +153 -109 166 -133 188 -149 _c +210 -165 241 -173 282 -173 _c +334 -173 373 -158 398 -128 _c +422 -98 435 -51 435 11 _c +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 519 _l +611 519 _l +611 467 _l +525 467 _l +435 285 _m +}_e{435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +435 285 _l +_cl}_e}_d +/h{{644 0 36 0 616 760 _sc +41 0 _m +41 52 _l +122 52 _l +122 708 _l +36 708 _l +36 760 _l +212 760 _l +212 427 _l +228 461 250 488 276 506 _c +302 524 333 533 369 533 _c +426 533 468 516 495 484 _c +522 451 536 400 536 330 _c +536 52 _l +616 52 _l +616 0 _l +368 0 _l +368 52 _l +446 52 _l +446 302 _l +446 365 438 408 422 432 _c +406 455 379 467 340 467 _c +298 467 266 451 244 421 _c +222 391 212 347 212 289 _c +212 52 _l +290 52 _l +290 0 _l +41 0 _l +_cl}_e}_d +/i{320 0 36 0 297 736 _sc +97 680 _m +97 695 102 708 113 719 _c +124 730 137 736 153 736 _c +167 736 180 730 191 719 _c +202 708 208 695 208 680 _c +208 664 202 651 192 641 _c +181 630 168 625 153 625 _c +137 625 124 630 113 641 _c +102 651 97 664 97 680 _c +212 52 _m +297 52 _l +297 0 _l +36 0 _l +36 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +212 519 _l +212 52 _l +_cl}_d +/k{{606 0 29 0 613 760 _sc +286 0 _m +34 0 _l +34 52 _l +115 52 _l +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 265 _l +424 467 _l +349 467 _l +349 519 _l +584 519 _l +584 467 _l +495 467 _l +341 324 _l +538 52 _l +613 52 _l +613 0 _l +357 0 _l +357 52 _l +431 52 _l +276 265 _l +205 199 _l +205 52 _l +286 52 _l +286 0 _l +_cl}_e}_d +/l{320 0 29 0 290 760 _sc +205 52 _m +290 52 _l +290 0 _l +29 0 _l +29 52 _l +115 52 _l +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 52 _l +_cl}_d +/n{{644 0 36 0 616 533 _sc +41 0 _m +41 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +212 519 _l +212 427 _l +228 461 250 488 276 506 _c +302 524 333 533 369 533 _c +426 533 468 516 495 484 _c +522 451 536 400 536 330 _c +536 52 _l +616 52 _l +616 0 _l +368 0 _l +368 52 _l +446 52 _l +446 302 _l +446 365 438 408 422 432 _c +406 456 379 468 340 468 _c +298 468 266 452 244 422 _c +222 391 212 347 212 289 _c +212 52 _l +290 52 _l +290 0 _l +41 0 _l +_cl}_e}_d +/o{602 0 50 -13 552 533 _sc +301 34 _m +349 34 385 53 410 91 _c +434 129 447 185 447 260 _c +447 334 434 390 410 428 _c +385 466 349 485 301 485 _c +253 485 216 466 192 428 _c +167 390 155 334 155 260 _c +155 185 167 129 192 91 _c +216 53 253 34 301 34 _c +301 -13 _m +225 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 72 408 118 458 _c +164 508 225 533 301 533 _c +377 533 437 508 483 458 _c +529 408 552 342 552 260 _c +552 177 529 111 483 61 _c +437 11 377 -13 301 -13 _c +_cl}_d +/r{478 0 36 0 478 533 _sc +478 520 _m +478 390 _l +426 390 _l +424 416 417 435 405 448 _c +392 460 373 467 349 467 _c +305 467 271 451 247 421 _c +223 390 212 346 212 289 _c +212 52 _l +316 52 _l +316 0 _l +41 0 _l +41 52 _l +122 52 _l +122 468 _l +36 468 _l +36 519 _l +212 519 _l +212 427 _l +229 463 251 489 279 507 _c +307 524 341 533 381 533 _c +395 533 411 531 427 529 _c +443 527 460 524 478 520 _c +_cl}_d +/s{{513 0 56 -13 462 533 _sc +56 29 _m +56 150 _l +108 150 _l +109 111 121 82 144 63 _c +167 43 201 34 246 34 _c +286 34 317 41 338 57 _c +359 72 370 94 370 123 _c +370 145 362 164 347 178 _c +331 192 299 207 249 223 _c +184 245 _l +139 259 107 277 87 299 _c +67 320 57 347 57 381 _c +57 428 74 465 109 492 _c +144 519 192 533 254 533 _c +281 533 310 529 340 522 _c +370 515 402 505 434 491 _c +434 378 _l +382 378 _l +380 411 369 437 347 456 _c +325 475 295 485 257 485 _c +219 485 190 478 171 465 _c +151 451 142 431 142 405 _c +142 383 149 365 164 352 _c +178 339 208 326 252 312 _c +323 290 _l +372 274 407 255 429 232 _c +451 209 462 180 462 144 _c +}_e{462 94 443 56 405 28 _c +367 0 316 -13 250 -13 _c +216 -13 184 -9 152 -3 _c +120 3 88 14 56 29 _c +_cl}_e}_d +/t{402 0 29 -13 394 680 _sc +108 467 _m +29 467 _l +29 519 _l +108 519 _l +108 680 _l +198 680 _l +198 519 _l +367 519 _l +367 467 _l +198 467 _l +198 137 _l +198 93 202 64 211 52 _c +219 40 235 34 258 34 _c +281 34 298 41 309 55 _c +319 69 325 91 326 122 _c +394 122 _l +391 74 378 40 355 19 _c +332 -2 297 -13 250 -13 _c +198 -13 161 -1 140 21 _c +118 43 108 82 108 137 _c +108 467 _l +_cl}_d +/u{{644 0 27 -13 607 519 _sc +354 519 _m +522 519 _l +522 52 _l +607 52 _l +607 0 _l +432 0 _l +432 92 _l +415 57 393 31 367 13 _c +341 -4 310 -13 276 -13 _c +218 -13 175 3 148 35 _c +121 67 108 119 108 189 _c +108 467 _l +27 467 _l +27 519 _l +198 519 _l +198 217 _l +198 153 205 110 221 87 _c +237 63 264 52 304 52 _c +346 52 377 67 399 98 _c +421 128 432 173 432 231 _c +432 467 _l +354 467 _l +354 519 _l +_cl}_e}_d +/v{565 0 -2 0 562 519 _sc +247 0 _m +56 467 _l +-2 467 _l +-2 519 _l +236 519 _l +236 467 _l +153 467 _l +299 110 _l +445 467 _l +367 467 _l +367 519 _l +562 519 _l +562 467 _l +504 467 _l +313 0 _l +247 0 _l +_cl}_d +/w{856 0 16 0 843 519 _sc +480 519 _m +613 114 _l +730 467 _l +655 467 _l +655 519 _l +843 519 _l +843 467 _l +785 467 _l +631 0 _l +556 0 _l +428 388 _l +300 0 _l +228 0 _l +74 467 _l +16 467 _l +16 519 _l +251 519 _l +251 467 _l +167 467 _l +283 114 _l +417 519 _l +480 519 _l +_cl}_d +end readonly def + +/BuildGlyph + {exch begin + CharStrings exch + 2 copy known not{pop /.notdef}if + true 3 1 roll get exec + end}_d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +}_d + +FontName currentdict end definefont pop +%% Font Page 00 +/BitstreamVeraSerif-Roman-ENC-00 [ +/.notdef/space/colon/C/l/i/e/n/t/A/g/M/a/r/parenleft/b/k/o/d/parenright/R/s/u +/c/h/I/D/B/v/O/w/Q/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef] def +/BitstreamVeraSerif-Roman-Uni-00 BitstreamVeraSerif-Roman-ENC-00 /BitstreamVeraSerif-Roman MFEmb +%%BeginFont: BitstreamVeraSerif-Roman +%!PS-AdobeFont-1.0 Composite Font +%%FontName: BitstreamVeraSerif-Roman-Uni +%%Creator: Composite font created by Qt +25 dict begin +/FontName /BitstreamVeraSerif-Roman-Uni def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontType 0 def +/FMapType 2 def +/Encoding [ +0]def +/FDepVector [ +/BitstreamVeraSerif-Roman-Uni-00 findfont +]def +FontName currentdict end definefont pop +%%EndFont +%%EndFont +/F1 8.33333/BitstreamVeraSerif-Roman-Uni DF +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +QI +%%EndPageSetup +[1 0 0 1 -42 -48]ST +0 0 B 0 0 PE +WB +W BC +42 48 597 168 R +2 0 255 0 0 0 0 PE +NB +67 216 69 VL +252 216 69 VL +432 202 69 VL +610 189 69 VL +0 0 B 0 0 PE +WB +243 100 17 32 R +255 0 0 P1 +NB +243 100 17 32 R +243 104 67 HL +1 255 0 0 BR +NP +242 104 MT +238 101 LT +238 107 LT +CP BF QS +71 126 67 129 DL +71 132 67 129 DL +3 0 255 0 0 0 0 PE +243 129 67 HL +0 0 B 0 0 PE +WB +423 101 17 32 R +255 0 0 P1 +NB +423 101 17 32 R +423 105 260 HL +1 255 0 0 BR +NP +422 105 MT +418 102 LT +418 108 LT +CP BF QS +264 127 260 130 DL +264 133 260 130 DL +3 0 255 0 0 0 0 PE +423 130 260 HL +255 0 0 P1 +NB +604 110 608 113 DL +604 116 608 113 DL +608 113 440 HL +255 0 0 P1 +437 162 433 165 DL +437 168 433 165 DL +608 165 433 HL +255 0 0 P1 +257 175 253 178 DL +257 181 253 178 DL +430 178 253 HL +255 0 0 P1 +72 189 68 192 DL +72 195 68 192 DL +250 192 68 HL +255 0 0 P1 +1 255 255 192 BR +42 48 50 21 R +CLSTART +5 5 40 11 ACR +CLEND +B P1 +F1 F +61 Y<000100020001000300040005000600070008>[3 0 3 0 3 0 6 0 3 0 3 0 5 0 5 0 0 0]34 50 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +255 0 0 P1 +1 255 255 192 BR +186 48 133 21 R +CLO +[1 0 0 1 -42 -48]ST +CLSTART +149 5 123 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<0001000200010009000A000600070008000B000C0007000C000A0006000D0001000E00040005000F000C001000110007000C001200050013>[3 0 3 0 3 0 6 0 5 0 5 0 5 0 3 0 8 0 5 0 5 0 5 0 5 0 5 0 4 0 3 0 3 0 3 0 3 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 3 0 0 0]123 191 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +255 0 0 P1 +1 255 255 192 BR +407 48 50 21 R +CLO +[1 0 0 1 -42 -48]ST +CLSTART +370 5 40 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<0001000200010003001100070008000D00110004>[3 0 3 0 3 0 6 0 5 0 5 0 3 0 4 0 5 0 0 0]40 412 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +255 0 0 P1 +1 255 255 192 BR +582 48 57 21 R +CLO +[1 0 0 1 -42 -48]ST +CLSTART +545 5 47 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<00010002000100140006001500110016000D00170006>[3 0 3 0 3 0 6 0 5 0 4 0 5 0 5 0 4 0 4 0 0 0]47 587 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +35 37 147 15 ACR +CLEND +50 d2 P1 +NB +F1 F +95 Y<000B0006000800180011001200010017000C00040004000200010017000D0006000C000800060009000A0006000700080019000700150008000C000700170006>[8 0 5 0 3 0 5 0 5 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 4 0 5 0 5 0 3 0 5 0 6 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 0 0]139 81 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +215 38 137 15 ACR +CLEND +50 d2 P1 +NB +F1 F +96 Y<001A001B0016001500010017000C00040004000200010017000D0006000C000800060009000A0006000700080019000700150008000C000700170006>[6 0 6 0 5 0 4 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 4 0 5 0 5 0 3 0 5 0 6 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 0 0]129 261 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +399 46 118 15 ACR +CLEND +50 d2 P1 +NB +F1 F +104 Y<0009001700080005001100070002000100150008000C000D0008001500010008001800060001000D0006001500110016000D00170006>[6 0 4 0 3 0 3 0 5 0 5 0 3 0 3 0 4 0 3 0 5 0 4 0 3 0 4 0 3 0 3 0 5 0 5 0 3 0 4 0 5 0 4 0 5 0 5 0 4 0 4 0 0 0]110 445 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +399 98 155 15 ACR +CLEND +50 d2 P1 +NB +F1 F +156 Y<001A001B00160015000100150005000A0007000C00040002000100150006000D001C000500170006001D001E00070006000D00030018000C0007000A00060012>[6 0 6 0 5 0 4 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 4 0 5 0 4 0 4 0 3 0 4 0 5 0 6 0 7 0 5 0 5 0 4 0 6 0 5 0 5 0 5 0 5 0 5 0 0 0]147 445 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +216 111 155 15 ACR +CLEND +50 d2 P1 +NB +F1 F +169 Y<001A001B00160015000100150005000A0007000C000400020001000C000A0006000700080019000700150008000C00070017000600090012001200060012>[6 0 6 0 5 0 4 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 5 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 5 0 6 0 5 0 5 0 5 0 0 0]138 266 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +39 125 134 15 ACR +CLEND +50 d2 P1 +NB +F1 F +183 Y<001F0008000100150005000A0007000C000400020001000C000A0006000700080019000700150008000C00070017000600090012001200060012>[6 0 3 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 5 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 5 0 6 0 5 0 5 0 5 0 0 0]126 85 XYT + +QP +%%Trailer +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman +%%EOF diff --git a/doc/pics/akonadi_agent_handling.png b/doc/pics/akonadi_agent_handling.png new file mode 100644 index 0000000000000000000000000000000000000000..ef14ec5114e1a591f618063a2908dc550c8e5e91 GIT binary patch literal 9008 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[3 0 3 0 3 0 6 0 3 0 3 0 5 0 5 0 0 0]34 55 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +328 48 51 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +294 5 41 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<00010002000100090008000A000B000C000D0006>[3 0 3 0 3 0 5 0 3 0 5 0 4 0 5 0 5 0 0 0]41 333 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +472 48 57 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +438 5 47 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<000100020001000E0006000F000A0010000B00110006>[3 0 3 0 3 0 6 0 5 0 4 0 5 0 5 0 4 0 4 0 0 0]47 477 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +186 48 85 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +152 5 75 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<00010002000100090006000C000B0011001200010013000B000A0014000500150006000B>[3 0 3 0 3 0 5 0 5 0 5 0 4 0 4 0 5 0 3 0 5 0 4 0 5 0 4 0 3 0 5 0 5 0 0 0]75 191 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +562 48 107 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +528 5 97 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<0001000200010016001700080006000B0007000C000400010018000C0008000C00010009000A0010000B00110006>[3 0 3 0 3 0 6 0 4 0 3 0 5 0 4 0 5 0 5 0 3 0 3 0 6 0 5 0 3 0 5 0 3 0 5 0 5 0 5 0 4 0 4 0 0 0]97 567 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +39 23 102 15 ACR +CLEND +50 d2 P1 +NB +F1 F +81 Y<001800190010000F00010011000C0004000400020001000F0006000C000B00110012001A00080006001B000F>[6 0 6 0 5 0 4 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 5 0 5 0 4 0 4 0 5 0 3 0 3 0 5 0 7 0 0 0]94 82 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +319 48 136 15 ACR +CLEND +50 d2 P1 +NB +F1 F +106 Y<001800190010000F00010011000C0004000400020001000B0006001C00100006000F0008001A00080006001B001800060004000500140006000B001D>[6 0 6 0 5 0 4 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 5 0 5 0 5 0 5 0 4 0 3 0 3 0 3 0 5 0 7 0 6 0 5 0 3 0 3 0 4 0 5 0 4 0 0 0]128 362 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +39 104 138 15 ACR +CLEND +50 d2 P1 +NB +F1 F +162 Y<001800190010000F0001000F0005000D0007000C000400020001000F0006000C000B00110012001A00080006001B000F000E0006000F001000040008>[6 0 6 0 5 0 4 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 4 0 5 0 5 0 4 0 4 0 5 0 3 0 3 0 5 0 7 0 4 0 6 0 5 0 4 0 5 0 3 0 0 0]130 82 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +198 49 74 15 ACR +CLEND +50 d2 P1 +NB +F1 F +107 Y<001A001E001F001300010011000C000400040002000100200006000800110012>[3 0 8 0 6 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 3 0 5 0 3 0 4 0 0 0]66 241 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +467 48 92 15 ACR +CLEND +50 d2 P1 +NB +F1 F +106 Y<001F001100080005000A000700020001000B0006001C00100006000F000800010015000C0008000C>[6 0 4 0 3 0 3 0 5 0 5 0 3 0 3 0 4 0 5 0 5 0 5 0 5 0 4 0 3 0 3 0 5 0 5 0 3 0 0 0]84 510 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +467 68 94 15 ACR +CLEND +50 d2 P1 +NB +F1 F +126 Y<001F001100080005000A000700020001000B00060008000B000500060014000600010015000C0008000C>[6 0 4 0 3 0 3 0 5 0 5 0 3 0 3 0 4 0 5 0 3 0 4 0 3 0 5 0 4 0 5 0 3 0 5 0 5 0 3 0 0 0]86 510 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +320 68 75 15 ACR +CLEND +50 d2 P1 +NB +F1 F +<001A001E001F001300010011000C0004000400020001000F0008000A000B0006>[3 0 8 0 6 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 3 0 5 0 4 0 0 0]67 363 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +38 148 74 15 ACR +CLEND +50 d2 P1 +NB +F1 F +206 Y<001A001E001F001300010011000C000400040002000100200006000800110012>[3 0 8 0 6 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 3 0 5 0 3 0 4 0 0 0]66 81 XYT + +QP +%%Trailer +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman +%%EOF diff --git a/doc/pics/akonadi_client_search.png b/doc/pics/akonadi_client_search.png new file mode 100644 index 0000000000000000000000000000000000000000..1c5915afda520f28627dd0e0cc5047a6c832bef0 GIT binary patch literal 10244 zcmb7qcQl+^+xO%+=?O_hNus0>HA1v7T0|EFF?tZ9h9ug}s39VHC&~~6QKCkjqW4a8 zX7o{KFc{r;%Q?^UobP$xf8I4~?Y*zPfBS0tDr?qEz;jjk%NK55fIuLZ6%}MOAdo+a z5Xh;ba}*~#A76=rgwpe8FJwVdSy|-Aj~_fd_cAgv($mvNM$!=ogr1&Ee0+RNOiV~f z$nD$oetv#lUS5TTANl#&UcY{wnd$NEo12srufD#%uC6W{85yQ`>(!r^d7M@L>>7B@G;ckkZW+1b5$^M;1zN?l!KO-;Cg zfvlOC*~N?J&!0bge0;2-A#r?M^ZdD3e7wWf*4E_Y_nSAb3kV3z&CQWWoWGi?yL*gNGnh0m zjfyLaRD{ltpN?Gg`E^KGr{kRvI5IBM$y;XM3vGO57)CtO^~({>ycV4w#>%fzxndxX zZP%WL?rv?Jc4srn@ccmh5)t?sPvOqy#Jv}aNhdBq|4$QgyQ2@-Wb}~7_5NdOUg`M< z_4gThNHBrf+hvCB4`T(~Wv06_GcUD0(?q$-0j zk&S|atN4Oj_0y+3aeP~3L~|zBgdpnRnQKMTaoPPQlZOS`u?72j7)_z39zaj@P$%nH z(A__d^L?^SMpw_K>3$&&ZF{-1k8PiD#i=eA)fhxfrtIh1o}f+dvF5-nzL)L^;fvE& z+VvE{aopM?lnS^+%+c=`%s|(_Ao13iZXn^PHOK#G3%5M zXmA{;kx9-S+ttOb!g7;{Wk|We~C^AZhi;kRS8V)=$%?23tY zG?yB?W%{$4YaQX+nrTrU>b))%4Rbpck@?}lyrQm0K9MQzPN_Hjt zomQG?3g|dyAPWOZlf+C-QdA5<+mk}6+N zFC;zO>$)>Y!)CMao<}*OGMa3N@&G}rS70)Bg^E=R0ndzW7NB#f1Em)sRESF-n6vm; zxSQ@q)$-EYNlL1^oXw>!?-IOpu-kSfFE6}jKTdq(koLKnNz7(@iG>ajG)$M~PV%OeNWMtr-?x?Zqc#J1MJ&ju(t+0|rbdg^14E!mI z55iDOX(#2Sp15va0}J$4v;L5XrSll7LwNBt++|X^n*D}mlFDGozvZTGSr+%3Cr_9ZT06`Ai zF983|BlNZG$VV2-w_rH^$~dwHU7OC&Jb8PBWKkaWp;5zX(G{VlXaytUN?ul9eBG;j zS*s-@(-42Kv_CR)VGF^8FL@Ezjx4~b^bdVr_8(tHW`E2nCk75AC~HtXzSe_#lug`B zE_aV}^I#kMvFYVCHTHNd!LR2 zTLi{%TkaBWS6aVW++g^kQpa#Pf^Aqjc~d=w0HyX^YntL!%F3p3}yN^OW75J^qhd}-yIrlf!&XZpF49F8i zI6ga))q$U%oKDi-deo^aOTcDt!ZE~oJ4wV`_Ft-duk<=@lULtx-c?v~= zH^p7Tr_967%l`EaeL8bGuqYJ;>Cr?`;Q*q1<}oCfVR;cc7o`fzg#5Ir<~tYuNOA4Q z(JC1~f+xL59@r-*#eP1ON8!yo@rp-qRn&sT>BwM}t(E0YXCM>4_(g)UD{kVbi6}3E zuO-Uwz7xV_g&aQ~w)fzJ%EFGr#7X;KcRZeNPRJJ*k(;Z(f|x1t6R=AePI&ll*X&k^ zrxF*CM)Od2nSmDIySY{!Q2p(pc~(nKm+M2L|7vN5X!qu20Oi?yT|FiiBlPj$yS;D$Bs`KbxOnX<%ELx1ty+Q2QIN}zREu#%k z#PM~nBtG0o>{SP0g(o z(Tw=lPhV&M#E!Uq;h<~|3h*I1HgK!NTYxBcGO~JFp(w;0sKR}Tk1FrQ^lY}fTSVNg z2OL$Eb_gZzfN}0Cy!r|X_CAMsx@J;)0Jc}2B1?rwzHFBf~57{ z{xElZ{2;k(-kt+jy^74VTlu5`m=6B>W>saRP*1Xs5cP`=vBrsS?!Q0adXY@PWpYN= zYUoie#r&lW5KlyHv;n?`>f%cyr$m-1vxJloTG1m@j-rm4p)mnvZ})kA#l)8h5ooXp z36C_2)&jb6WpBcf1Gf8HZ*q&p48qK9G6IYi%3j91W;$rp0dc!sEc{vAj5)))VCO$; z?rB4_-m5sM_(t2%a|(%i`{6lVid}-G)sF-Po049kJeUVf258c1n?k_IypBMRX6d8u zzg%I8A(_u)M8*QavLCrnzElJlq$NLaF(=Dc#n-#5xP? zrlIC;-GA=101<)fC3vJvb%)(7w>YO&DrvRce7<0%{N2AF-f%~zYd_`UmXNt;Hkv$EdX2xpt$c;ULC=lkN_%0jv1_~W=!p%H zuI(NvBB>zExnF^3P!7vtkhC;a>10u#ZZ|Kj-}N+rKR`>qCc-Z{1-D)w$ zFXq(b#hoUMrYtejsZSCiGqK-XJ^eT|Hd{lO^0Y6@gF&|vRlRSFcmu5$%qqn-m3CJO zh$RDqdV+?GUj%a*yC5$4^5soS7ZQ+xMZx-tpnS}n%9@3MzolpcACu#r4rr)8>xL*gtz&qP> z06&}X{ZC-Dm-7D4MT(J!-Lp#^Q^;Hs&(%skJ}_nTts>VM!~1}(2?p#%428z@E5{{{2FEk4|F^Rg_H3$`z62c*|=e;gz+Jo)c`x8)hVx=6a#p6y@DZ-9ZCoU zGzdch8hBg;2o+=t=+gT)T@m=@$8Bw??{Q-_o-2$1Ct)t$xuxhE74lA~uBPVsck0b! zoa*S(Q!P8SM@>UPmFupwSUWY3BvFs0q2>aEz%=%a(3CPhI|Gq)&^^zCZ)tb2M*zP{ zn?r8YgNqJ}{G_|Y7U)Z>{3P{h&es2BF;CJq5b-XPc&WG4P1hCHeEkzDoxVH@hVASr zSr9LU-HoIS*E8PbW>)`DCXp;VI5_;rLD3Q;F7Zgr7$6*2&ZJIdcWsvZ3ikGOwPhoI zr=IzwuA@_g9I%W9xy!D#E4qwy{ohx~@KD!GX|ALw8?In*HqNLyHkvo}@MB zlzY70bBMC*PGUY};_bjb0s3jA-b2ZzG|5K&_ymJ+ z;Nj@)6gXBZto5{OoSAN9-eE?M>A)C9_&~(Vb0s2;RYlD!A;H02EwZjwU}ctZE6Zgl z_af>wm7KuPjEa!-QR-oVX_y2Dv7x2`wM==txSY2*0bgUEzAFDPYqwwS*%`BaV9@5r zME8de!JUu_Bkcww$i!^p6*(#x0-ri$Qw~TN96kP7G-$`Yp<4ZCo97C$OkFLQPyf{O zC)D101G!%F3gnoxBkQ~Co5RvKn`_*(I~`r@KD$r-lJD5Z2Y-5*`(#AFPT@1Adr{MWt7&ZLCeJ2&H9Df9Fl-hx7P`Svm5 zipSd_hmBCt0HC-5CKC&se%7*e?uzCm2_2#%zoYF9!sHf0cc0C2PD-@u9P#?(nQX?} zUa_Bnmc6^Gu%|BQL(%oR`^iI|mD50b(@`e1&Bg?A~~OFiE#9AT2D4FWS@=xab@GGpDSLbiB+_h z$>6rz@aH8aCq+>-e^bp-A@+c{-U48npKjbWvkx<*8F%qNo7>etYxqm<+RF0PKZUl% zt*Q6>8(%OC{;kz_J9At_f%K)j*hL2g8|KsVdA(~MK~?^8MyqgDta{eEYOLj!8=z4m zQ%OkF;V++?{|?7HHPIDV)7Z=$9cf+d$S~?&a*LW0gmF=vx$w!(aE{h>kkh~yi1I=Q zW)O>bW9It?4sZVLpzk3p?>+!UMD@hs6h=bBFs~0hBX)*l>R-uc?wxqW_cO4^@O+=0 z*COZ>=jORyva=Vc1RKdNhE6%fjlC#n_&q^kG=wSX-tT@syq>G+=YfXTo{^ZIUY>AWl} z{E)nitU;E@KBeKTyK?wAs0F%Yh7FMQyq65z8KSrynhU&WLss+^4wAKVku1b(voqRT zhG(J2gvcLdeGH=q&%+Xa!5-{4ukozgaHh6(GwdoA*w&e#i6LNkuc zp2g+ANgf7$G|GH~qCA4CmMb2znog>ZnaF-oAEP50@`0S6Y!y%3>#lz@>tP@gO$hE? zgGy`=X1a1OVrGf4(`C-E!q;Y1gIs-` zI5~h0P=)(P&3f+a=p!dol`h&eji%y?cI_Yb6<6BcFm=z`Q}OKqWqN<@&I|C6o zgN44>c%MB^mS7r(nhKy`wqu9$InFmdN|!}k>Ks=^^0B?aWNwvl z_VL#K$^({Zak#t9UD;nQA9XVfA`uCI^H%PehGVbZ-{W{BV z;JU2(ETgRYbys=y>y6zO$K^ZZ@k)ESfMX-7f{UbhE{o_EsQJUc7lH3*+KxY(9~*U? z-U!uB;Hr-N5)RBV76@>~89{}HT97wf<*`Nfm0j9JL-ZN{B7oI=J|+H*P?YJMeMX9g zfl0La-xMOH?WJk7`7EPrbK4xJt1z;Oy|V?nK@jDlvv!VAKk2kEo;+fHIwr9QGb6lI zo!kP&EaCTo>&sB{@M*$cT0^%*D~LHbnsAb_5}G0ZzHuOa!^`Li*z)oWFxHf7m&Uy#@$kaugWSNqZ91 zknv>iW{!3S9oM1-Rj#YBEId;jTZ1he(021yAnQwg||L|9p_2;~OXh4_O_PTIYR#n=Ne zInK62gwM4iDbCWecN4$5KlJYU$TZ~lhQtDQKseIzSQUG?!fA_LCs|}KY(=5kyKkYK z2vv)puOn)pH&S=Bu-V+2*835f8?LCKfqe@vO>6U1eXa-~`kX7-9J^|g(_8kq!VZ3b zeKIu)49qud@FK7LeX4G4F)2|R2nR%fRz3sMP0 z1iSg6cmEP8sHu>Tx*TmNrKoA!Y*!{pfStE+LdT3#9ifz3?lK2)r?P${!Zu7dLa*H+ zzQj+^x*Baawyfvxvp@28rn9u6YAS!{^YCupVw9d{qGw2STKwn7%?Txw z1G)7hZjmd3M6(c8$!fZ0tJ%2|T1#6k#;x?@l z`xbhBE22{L{77fk^^J2!6vTWKBcsdB6FKx=VtLoPws~oSY#&dTqy!+1uJ!?3yL`AX z=?LKM^aL%1AcF{&v9L#v>K)!I`SMb2743uKu8uMn;azQ%5aJU38$#4ycun;*R1sZ|pbCm~~&m!or1Jf=;{CnS6 zH#{c|hjD^Ow(A6qsQok-JFl$q3;SV_8Z9$1hlZ1e-~-rV4d)~9j}8iAdMGHTUFiDW zW-4RVXD9SsEg}MECTL-tm)CwxBXW##`?9n17pohfnx&I}+`Uhet3?vz(JGkzUg(b6 zjZScNIqr&aU__xikdwomb{m98VLZ>0T*+55Ru z)*9Wp{!|d&T$s{ey<;hIH=1W-?Fhkz-|I_EJ|)E;P-ZGT#w?%lZct@+`!6+aSpdou7YVGGWy)ZS^je|Lk2LQR+2@;7 z&6Q>uX*}#^8BgeqP?`p?G{YW4;|ESIx@V5GJ_`VOtjC+P@v}Oga&5LqKy*Y8pFSym zzwhqHqV^3Xlwm#k8Yh)iqMsRSpdh@4* zGB)M4lDnkRCQTO*TXb{h2)Ys4y_rP<1*U-l_ss$^W9@XR>tqX4kPOPLYmeO5fn7P4 zxZp}B>le3T2D<|Gl1B9@R%krU$=4V47cRQRQCu++pZhotB$KH@v&fZNvE!A6zby!o zQnxh+dq*fLy`tLmwXU?^DfGf~_>DY)hU4SW(x(21 zs0WJ3=dn8;lov;L&GX3p9ljxPrIR?^t0lxmKX*3Vq9Rx2w!cOj0Pj=E=i$h3&tm&A zyA%m-jCO+?tpJ?ozZcTyQgJCX(&u(n&)u1Y-uW0#{CGtg*S(a;!Kxh>t%2!}g4sJM za?C+jEx?Ugs5sq!FIHb}sk9ET?y7!&`&)*zM}Md~hy-zT8;Jj?@Rv?=VUx(ThF>Oo zkF6Ew9cC$q#YrB7wK@R8JMWdrJ~iTdk2yuF>e}uC1v&FDZ+5Bg^={}HQ=W2RofSnr z7al4aA@{i4g8*XDArr2*h9CT)Q?!}PHNMB1-)1Kv(6{%N><@ zTra{HFU!${KUAYcCaN~i#0l`DUd43Y{2JdF^YlT1dQ`ShXy%J4Y+b_dXE6LiJQq$| znV1g0Z}lXyFsVb>K(t+|A`oM#92~30x{@dO<(5%>IDJuP58qyXQe=U7xrk_fU>jAA zzHiRLI zl+ZdaAg7-B29Gndb*|BCXFi!P*>(4Vx!sH*I!1zz9?IaJ%o_LojJb=H>k!|y{IvG_ zJ5k@LR7F3+?dPjr2qR4e0ml1Cwh7y{ZZc8nD>iHjT zgcfnTG~&5S9X+8+#u$s+FHaj?ACBw=B!0ckwg0)*3IqrsYkb8uvX(3bY1h`f4)lL> z+9Uk5&-?M7elS^X4vH{yT7>S1Vfeqp3a~g25sw6xAz`gXDO4blh~48sNxCf~r&i7< z)6>7W^<_u+cm*jy8V9msOn=CG!fwf#a_*|K=^9ok;C3$!?q}U>Oy0aqi3Ol$W}oOb zF2vfY6`!G4Z@Z<_owCN&n#GS3aj_R#o6w4WnPBT9pV+J;zJ{n3R7}jG@%Ypc#gkQT zHD5_o{8PTL0|vK~5|nWhQt(GH@~ zKilr~sXk}R@_^A9JH+1E8t`WeX#^cQI>5O7Y+yM(TQ3s16z{(j4r{pDH(mMO%3Shj z&fB~nK5Ic9fYK|Jz8AgHq@v?Mr#gv{cIiCCtne?>y8ET&40< z3KBJ@6bv-aJvqc~?~hBM4+th4AFrg01KSttB|lP2e^w(vuOozC7k#@T?Y?nLbU5Jm z-q-gy&YYEAI2Inv*0u*L4Fb+eCynVpA#hgYrC(f?;l!VAPD?QcEx;&xDKYZm?}EZJ z3r)os!tK}3yJvaiX%@_S6#-|*S_C+4Q?4Kem4AcIx@B!e`l9>ct+iMyG>90=-HJ3N8WapFhob8pq8Qx#@%Kfx z^SiJg{tYrd(WMXobr?QT^0(X-MsGAlApqH?tl4MBwuLkNLjrji={3|8@%Ylr?dv`a zneXm|vm!q?B3xIyIt9-No1Df7_`IE2N~k@5XYY6{O6|CCOZ8(Wc~$Jbc>|;xJllxzfG|y5=wgQZ?Nn;8{9gARh4Wccs|u66RYMg(CA} zEIlr0M$EUp+JC6bnzi2dEOA}hIWp7vP8zUDS+TUsqwy>B$dPhPu}g18I)N#R#T+v@ z33a1ma#JEMFs|8blc(4qA$Umhe>$ za{Duop;g2{U~{9dA9gNeB!#owL}@9xZ>e$n)946xx>h9X=MZuu>?QG&p8SKkQ~LCdfaK$li2J=W0-FRwwXVtlLTG_Sx7@~{ZmcV6(`d`?NUZ?4XKR4x*yPEh!fkC* z)~GEaGB}CcpwU&Zj925!hL2MalG|{)%`TvTHmM8rdrdcr`L(1OExbIcyi4Y-(H2ji z2egWNbv{^Xbqn*7ks7zH$w zQbrXGF}j*UHUn5mj<71$E@nBTG7mG++QN zn+>MTx)+)T*Om*y7F7fHgRl{O&>&f&!Xl!(c!6=>4d2BGmxmGa$;0Q__H>BvJp7lT zXj5b2%GOWN5s>=Z*H6=#UfNFa>q1yrhF2s`EptRh(W7USh+@;JfXgvhHEhu&ng^hA zB4x(x$yDvN9%Eo_0bl8e+?rJKV6zKeM88SNedZs&`G>aP@rm6h15fR%9%vzm{r~*< zH`xPSKC$`tP)I-Cd=3=wTMGQ_q&qFXaEj7Pf3g V(`-i`@b7#OMOjsud}(8!{|8!$nr8q2 literal 0 HcmV?d00001 diff --git a/doc/pics/akonadi_client_search_small.png b/doc/pics/akonadi_client_search_small.png new file mode 100644 index 0000000000000000000000000000000000000000..c089a302c4cec594fefc5c0c9b524111ec470328 GIT binary patch literal 3726 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 + + + + umbrello uml modeller http://uml.sf.net + 1.5.3 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/pics/akonadi_concept_schema.sla b/doc/pics/akonadi_concept_schema.sla new file mode 100644 index 00000000..9ffc59b6 --- /dev/null +++ b/doc/pics/akonadi_concept_schema.sla @@ -0,0 +1,721 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/pics/akonadi_overview_uml.png b/doc/pics/akonadi_overview_uml.png new file mode 100644 index 0000000000000000000000000000000000000000..70817a61924fb0b864a315d6e0ed678486994940 GIT binary patch literal 14070 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 Wf^~SJsu=&eW@2o9ve*zG{eJ*Y7ZKY4 literal 0 HcmV?d00001 diff --git a/doc/pics/akonadi_overview_uml.ps b/doc/pics/akonadi_overview_uml.ps new file mode 100644 index 00000000..91f3cc0a --- /dev/null +++ b/doc/pics/akonadi_overview_uml.ps @@ -0,0 +1,2551 @@ +%!PS-Adobe-3.0 +%%BeginProcSet: reencode 1.0 0 +/RE +{ findfont begin + currentdict dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /FontName exch def dup length 0 ne + { /Encoding Encoding 256 array copy def + 0 exch + { dup type /nametype eq + { Encoding 2 index 2 index put + pop 1 add + } + { exch pop + } ifelse + } forall + } if pop + currentdict dup end end + /FontName get exch definefont pop + } bind def +%%EndProcSet: reencode 1.0 0 +%%BeginProcSet: ellipse 1.0 0 +/ellipsedict 8 dict def +ellipsedict /mtrx matrix put +/ellipse { ellipsedict begin +/endangle exch def +/startangle exch def +/yrad exch def +/xrad exch def +/y exch def +/x exch def +/savematrix mtrx currentmatrix def +x y translate +xrad yrad scale +0 0 1 0 360 arc +savematrix setmatrix end } def +%%EndProcSet: ellipse 1.0 0 +%%EndProlog +%%BeginSetup +/isolatin1encoding +[ 32 /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright + /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one + /two /three /four /five /six /seven /eight /nine /colon /semicolon + /less /equal /greater /question /at /A /B /C /D /E + /F /G /H /I /J /K /L /M /N /O + /P /Q /R /S /T /U /V /W /X /Y + /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c + /d /e /f /g /h /i /j /k /l /m + /n /o /p /q /r /s /t /u /v /w + /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright + /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior + /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf + /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla + /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde + /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex + /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring + /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis + /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave + /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def +%%EndSetup +1 setlinewidth +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +12 scalefont setfont +0.0 0.0 0.0 setrgbcolor +32 810 translate +0.625 0.625 scale +-20 -12 translate +newpath +20 -12 moveto +855 0 rlineto +0 -1195 rlineto +-855 0 rlineto +closepath +clip +1.0 1.0 1.0 setrgbcolor +newpath +24 -640 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -640 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +28 -653 moveto +(server) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -660 moveto +847 0 rlineto +0 -459 rlineto +-847 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -660 moveto +847 0 rlineto +0 -459 rlineto +-847 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +392 -920 moveto +390 0 rlineto +0 -20 rlineto +-390 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +392 -920 moveto +390 0 rlineto +0 -20 rlineto +-390 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +396 -933 moveto +(storage) show +1.0 1.0 1.0 setrgbcolor +newpath +392 -940 moveto +439 0 rlineto +0 -147 rlineto +-439 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +392 -940 moveto +439 0 rlineto +0 -147 rlineto +-439 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +488 -1032 moveto +95 0 rlineto +0 -45 rlineto +-95 0 rlineto +closepath +eofill +newpath +488 -1032 moveto +95 0 rlineto +0 -1 rlineto +-95 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +488 -1032 moveto +96 0 rlineto +0 -2 rlineto +-96 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +488 -1034 moveto +95 0 rlineto +0 -19 rlineto +-95 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +512 -1047 moveto +(DataStore) show +newpath +488 -1032 moveto +96 0 rlineto +0 -46 rlineto +-96 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +408 -944 moveto +108 0 rlineto +0 -21 rlineto +-108 0 rlineto +closepath +eofill +newpath +408 -944 moveto +108 0 rlineto +0 -1 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +408 -944 moveto +109 0 rlineto +0 -2 rlineto +-109 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +408 -946 moveto +108 0 rlineto +0 -19 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +412 -959 moveto +(NotificationCollector) show +newpath +408 -944 moveto +109 0 rlineto +0 -22 rlineto +-109 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -1048 moveto +67 0 rlineto +0 -21 rlineto +-67 0 rlineto +closepath +eofill +newpath +736 -1048 moveto +67 0 rlineto +0 -1 rlineto +-67 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +736 -1048 moveto +68 0 rlineto +0 -2 rlineto +-68 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -1050 moveto +67 0 rlineto +0 -19 rlineto +-67 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +740 -1063 moveto +(DbInitializer) show +newpath +736 -1048 moveto +68 0 rlineto +0 -22 rlineto +-68 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +520 -1032 moveto +520 -984 lineto +stroke +newpath +520 -984 moveto +517 -966 lineto +stroke +[] 0 setdash +newpath +525 -976 moveto +517 -966 lineto +stroke +newpath +512 -978 moveto +517 -966 lineto +stroke +545 -1005 moveto +(send changes) show +1.0 1.0 1.0 setrgbcolor +newpath +656 -1144 moveto +88 0 rlineto +0 -59 rlineto +-88 0 rlineto +closepath +eofill +newpath +656 -1144 moveto +88 0 rlineto +0 -14 rlineto +-88 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +656 -1144 moveto +89 0 rlineto +0 -15 rlineto +-89 0 rlineto +closepath +stroke +662 -1158 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +656 -1159 moveto +88 0 rlineto +0 -19 rlineto +-88 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +660 -1172 moveto +(MySQL Database) show +1.0 1.0 1.0 setrgbcolor +newpath +657 -1180 moveto +87 0 rlineto +0 -22 rlineto +-87 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +657 -1180 moveto +745 -1180 lineto +stroke +newpath +656 -1144 moveto +89 0 rlineto +0 -60 rlineto +-89 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +737 -1070 moveto +737 -1144 lineto +stroke +[] 0 setdash +newpath +730 -1132 moveto +737 -1144 lineto +stroke +newpath +744 -1132 moveto +737 -1144 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +584 -1078 moveto +656 -1144 lineto +stroke +[] 0 setdash +newpath +642 -1141 moveto +656 -1144 lineto +stroke +newpath +651 -1130 moveto +656 -1144 lineto +stroke +0.78431374 1.0 1.0 setrgbcolor +newpath +40 -864 moveto +94 0 rlineto +0 -21 rlineto +-94 0 rlineto +closepath +eofill +newpath +40 -864 moveto +94 0 rlineto +0 -1 rlineto +-94 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +40 -864 moveto +95 0 rlineto +0 -2 rlineto +-95 0 rlineto +closepath +stroke +0.78431374 1.0 1.0 setrgbcolor +newpath +40 -866 moveto +94 0 rlineto +0 -19 rlineto +-94 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +44 -879 moveto +(RecourceManager) show +newpath +40 -864 moveto +95 0 rlineto +0 -22 rlineto +-95 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +584 -1056 moveto +736 -1056 lineto +stroke +[] 0 setdash +newpath +724 -1063 moveto +736 -1056 lineto +stroke +newpath +724 -1049 moveto +736 -1056 lineto +stroke +615 -1037 moveto +(build db) show +1.0 1.0 1.0 setrgbcolor +newpath +696 -720 moveto +102 0 rlineto +0 -20 rlineto +-102 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +696 -720 moveto +102 0 rlineto +0 -20 rlineto +-102 0 rlineto +closepath +stroke +700 -733 moveto +(handler) show +1.0 1.0 1.0 setrgbcolor +newpath +696 -740 moveto +151 0 rlineto +0 -163 rlineto +-151 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +696 -740 moveto +151 0 rlineto +0 -163 rlineto +-151 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +704 -744 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +704 -744 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +704 -744 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +704 -746 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +715 -759 moveto +(Handler) show +newpath +704 -744 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -872 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +736 -872 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +736 -872 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -874 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +751 -887 moveto +(Status) show +newpath +736 -872 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +newpath +744 -872 moveto +744 -766 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +744 -766 moveto +751 -778 lineto +737 -778 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +744 -766 moveto +751 -778 lineto +737 -778 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +784 -800 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +784 -800 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +784 -800 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +784 -802 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +795 -815 moveto +(Append) show +newpath +784 -800 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +newpath +784 -800 moveto +764 -766 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +764 -766 moveto +776 -772 lineto +764 -779 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +764 -766 moveto +776 -772 lineto +764 -779 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +768 -840 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +768 -840 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +768 -840 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +768 -842 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +782 -855 moveto +(Delete) show +newpath +768 -840 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +newpath +768 -840 moveto +764 -766 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +764 -766 moveto +771 -777 lineto +757 -778 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +764 -766 moveto +771 -777 lineto +757 -778 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +536 -704 moveto +102 0 rlineto +0 -36 rlineto +-102 0 rlineto +closepath +eofill +newpath +536 -704 moveto +102 0 rlineto +0 -14 rlineto +-102 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +536 -704 moveto +103 0 rlineto +0 -15 rlineto +-103 0 rlineto +closepath +stroke +555 -718 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +536 -719 moveto +102 0 rlineto +0 -19 rlineto +-102 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +540 -732 moveto +(AkonadiConnection) show +newpath +536 -704 moveto +103 0 rlineto +0 -37 rlineto +-103 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +568 -840 moveto +75 0 rlineto +0 -36 rlineto +-75 0 rlineto +closepath +eofill +newpath +568 -840 moveto +75 0 rlineto +0 -14 rlineto +-75 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +568 -840 moveto +76 0 rlineto +0 -15 rlineto +-76 0 rlineto +closepath +stroke +574 -854 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +568 -855 moveto +75 0 rlineto +0 -19 rlineto +-75 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +572 -868 moveto +(CacheCleaner) show +newpath +568 -840 moveto +76 0 rlineto +0 -37 rlineto +-76 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +639 -728 moveto +696 -728 lineto +stroke +[] 0 setdash +newpath +684 -735 moveto +696 -728 lineto +stroke +newpath +684 -721 moveto +696 -728 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +368 -776 moveto +183 0 rlineto +0 -34 rlineto +-183 0 rlineto +closepath +eofill +newpath +368 -776 moveto +183 0 rlineto +0 -14 rlineto +-183 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +368 -776 moveto +184 0 rlineto +0 -15 rlineto +-184 0 rlineto +closepath +stroke +420 -790 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +368 -791 moveto +183 0 rlineto +0 -19 rlineto +-183 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +425 -804 moveto +(AkonadiServer) show +newpath +368 -776 moveto +184 0 rlineto +0 -35 rlineto +-184 0 rlineto +closepath +stroke +newpath +368 -811 moveto +135 -864 lineto +stroke +newpath +368 -811 moveto +359 -818 lineto +349 -815 lineto +357 -808 lineto +closepath +eofill +newpath +368 -811 moveto +359 -818 lineto +349 -815 lineto +357 -808 lineto +closepath +stroke +353 -833 moveto +(1) show +149 -879 moveto +(1) show +newpath +552 -811 moveto +568 -840 lineto +stroke +newpath +552 -811 moveto +561 -817 lineto +561 -828 lineto +552 -822 lineto +closepath +eofill +newpath +552 -811 moveto +561 -817 lineto +561 -828 lineto +552 -822 lineto +closepath +stroke +569 -822 moveto +(1) show +570 -824 moveto +(1) show +newpath +536 -811 moveto +536 -1032 lineto +stroke +newpath +536 -811 moveto +541 -821 lineto +536 -831 lineto +531 -821 lineto +closepath +eofill +newpath +536 -811 moveto +541 -821 lineto +536 -831 lineto +531 -821 lineto +closepath +stroke +548 -831 moveto +(1) show +548 -1022 moveto +(1) show +1.0 1.0 0.78431374 setrgbcolor +newpath +208 -760 moveto +107 0 rlineto +0 -21 rlineto +-107 0 rlineto +closepath +eofill +newpath +208 -760 moveto +107 0 rlineto +0 -1 rlineto +-107 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +208 -760 moveto +108 0 rlineto +0 -2 rlineto +-108 0 rlineto +closepath +stroke +1.0 1.0 0.78431374 setrgbcolor +newpath +208 -762 moveto +107 0 rlineto +0 -19 rlineto +-107 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +212 -775 moveto +(NotificationManager) show +newpath +208 -760 moveto +108 0 rlineto +0 -22 rlineto +-108 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +336 -712 moveto +108 0 rlineto +0 -21 rlineto +-108 0 rlineto +closepath +eofill +newpath +336 -712 moveto +108 0 rlineto +0 -1 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +336 -712 moveto +109 0 rlineto +0 -2 rlineto +-109 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +336 -714 moveto +108 0 rlineto +0 -19 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +340 -727 moveto +(CachePolicyManager) show +newpath +336 -712 moveto +109 0 rlineto +0 -22 rlineto +-109 0 rlineto +closepath +stroke +newpath +376 -776 moveto +376 -734 lineto +stroke +newpath +376 -776 moveto +371 -766 lineto +376 -756 lineto +381 -766 lineto +closepath +eofill +newpath +376 -776 moveto +371 -766 lineto +376 -756 lineto +381 -766 lineto +closepath +stroke +358 -766 moveto +(1) show +358 -754 moveto +(1) show +newpath +368 -782 moveto +316 -782 lineto +stroke +newpath +368 -782 moveto +358 -787 lineto +348 -782 lineto +358 -777 lineto +closepath +eofill +newpath +368 -782 moveto +358 -787 lineto +348 -782 lineto +358 -777 lineto +closepath +stroke +350 -802 moveto +(1) show +328 -802 moveto +(1) show +[5.0 5.0 ] 0 setdash +newpath +135 -886 moveto +488 -1032 lineto +stroke +[] 0 setdash +newpath +474 -1033 moveto +488 -1032 lineto +stroke +newpath +479 -1020 moveto +488 -1032 lineto +stroke +285 -942 moveto +(keep DS up to date) show +[5.0 5.0 ] 0 setdash +newpath +544 -776 moveto +544 -741 lineto +stroke +[] 0 setdash +newpath +551 -753 moveto +544 -741 lineto +stroke +newpath +537 -753 moveto +544 -741 lineto +stroke +575 -765 moveto +(use) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -496 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -496 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +stroke +28 -509 moveto +(server/control) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -516 moveto +847 0 rlineto +0 -91 rlineto +-847 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -516 moveto +847 0 rlineto +0 -91 rlineto +-847 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +0.78431374 1.0 1.0 setrgbcolor +newpath +104 -576 moveto +79 0 rlineto +0 -21 rlineto +-79 0 rlineto +closepath +eofill +newpath +104 -576 moveto +79 0 rlineto +0 -1 rlineto +-79 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +104 -576 moveto +80 0 rlineto +0 -2 rlineto +-80 0 rlineto +closepath +stroke +0.78431374 1.0 1.0 setrgbcolor +newpath +104 -578 moveto +79 0 rlineto +0 -19 rlineto +-79 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +108 -591 moveto +(AgentManager) show +newpath +104 -576 moveto +80 0 rlineto +0 -22 rlineto +-80 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +304 -576 moveto +82 0 rlineto +0 -21 rlineto +-82 0 rlineto +closepath +eofill +newpath +304 -576 moveto +82 0 rlineto +0 -1 rlineto +-82 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +304 -576 moveto +83 0 rlineto +0 -2 rlineto +-83 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +304 -578 moveto +82 0 rlineto +0 -19 rlineto +-82 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +308 -591 moveto +(ProfileManager) show +newpath +304 -576 moveto +83 0 rlineto +0 -22 rlineto +-83 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +496 -576 moveto +122 0 rlineto +0 -21 rlineto +-122 0 rlineto +closepath +eofill +newpath +496 -576 moveto +122 0 rlineto +0 -1 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +496 -576 moveto +123 0 rlineto +0 -2 rlineto +-123 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +496 -578 moveto +122 0 rlineto +0 -19 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +500 -591 moveto +(SearchProviderManager) show +newpath +496 -576 moveto +123 0 rlineto +0 -22 rlineto +-123 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +632 -640 moveto +81 0 rlineto +0 -40 rlineto +-81 0 rlineto +closepath +eofill +newpath +632 -640 moveto +81 0 rlineto +0 -14 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +632 -640 moveto +82 0 rlineto +0 -15 rlineto +-82 0 rlineto +closepath +stroke +635 -654 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +632 -655 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +660 -668 moveto +(IMAP) show +newpath +632 -640 moveto +82 0 rlineto +0 -41 rlineto +-82 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +632 -704 moveto +632 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +632 -681 moveto +639 -693 lineto +625 -693 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +632 -681 moveto +639 -693 lineto +625 -693 lineto +closepath +stroke +669 -706 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +184 -640 moveto +81 0 rlineto +0 -40 rlineto +-81 0 rlineto +closepath +eofill +newpath +184 -640 moveto +81 0 rlineto +0 -14 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +184 -640 moveto +82 0 rlineto +0 -15 rlineto +-82 0 rlineto +closepath +stroke +187 -654 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +184 -655 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +213 -668 moveto +(DBus) show +newpath +184 -640 moveto +82 0 rlineto +0 -41 rlineto +-82 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +258 -760 moveto +258 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +258 -681 moveto +265 -693 lineto +251 -693 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +258 -681 moveto +265 -693 lineto +251 -693 lineto +closepath +stroke +295 -734 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +135 -864 moveto +184 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +184 -681 moveto +187 -694 lineto +174 -690 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +184 -681 moveto +187 -694 lineto +174 -690 lineto +closepath +stroke +194 -781 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +336 -712 moveto +266 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +266 -681 moveto +279 -679 lineto +274 -692 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +266 -681 moveto +279 -679 lineto +274 -692 lineto +closepath +stroke +296 -688 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +184 -598 moveto +184 -640 lineto +stroke +[] 0 setdash +newpath +177 -628 moveto +184 -640 lineto +stroke +newpath +191 -628 moveto +184 -640 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +336 -496 moveto +81 0 rlineto +0 -40 rlineto +-81 0 rlineto +closepath +eofill +newpath +336 -496 moveto +81 0 rlineto +0 -14 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +336 -496 moveto +82 0 rlineto +0 -15 rlineto +-82 0 rlineto +closepath +stroke +339 -510 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +336 -511 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +365 -524 moveto +(DBus) show +newpath +336 -496 moveto +82 0 rlineto +0 -41 rlineto +-82 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +184 -576 moveto +336 -537 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +336 -537 moveto +326 -546 lineto +322 -533 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +336 -537 moveto +326 -546 lineto +322 -533 lineto +closepath +stroke +241 -546 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +377 -576 moveto +377 -537 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +377 -537 moveto +384 -549 lineto +370 -549 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +377 -537 moveto +384 -549 lineto +370 -549 lineto +closepath +stroke +414 -570 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +496 -576 moveto +418 -537 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +418 -537 moveto +431 -536 lineto +425 -548 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +418 -537 moveto +431 -536 lineto +425 -548 lineto +closepath +stroke +453 -548 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +32 -384 moveto +158 0 rlineto +0 -20 rlineto +-158 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +32 -384 moveto +158 0 rlineto +0 -20 rlineto +-158 0 rlineto +closepath +stroke +36 -397 moveto +(resources) show +1.0 1.0 1.0 setrgbcolor +newpath +32 -404 moveto +207 0 rlineto +0 -71 rlineto +-207 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +32 -404 moveto +207 0 rlineto +0 -71 rlineto +-207 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +296 -384 moveto +190 0 rlineto +0 -20 rlineto +-190 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +296 -384 moveto +190 0 rlineto +0 -20 rlineto +-190 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +300 -397 moveto +(searchprovider) show +1.0 1.0 1.0 setrgbcolor +newpath +296 -404 moveto +239 0 rlineto +0 -75 rlineto +-239 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +296 -404 moveto +239 0 rlineto +0 -75 rlineto +-239 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +24 -128 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -128 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +28 -141 moveto +(libakonadi) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -148 moveto +847 0 rlineto +0 -203 rlineto +-847 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -148 moveto +847 0 rlineto +0 -203 rlineto +-847 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +48 -440 moveto +81 0 rlineto +0 -21 rlineto +-81 0 rlineto +closepath +eofill +newpath +48 -440 moveto +81 0 rlineto +0 -1 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +48 -440 moveto +82 0 rlineto +0 -2 rlineto +-82 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +48 -442 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +52 -455 moveto +(VCardResource) show +newpath +48 -440 moveto +82 0 rlineto +0 -22 rlineto +-82 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +152 -440 moveto +70 0 rlineto +0 -21 rlineto +-70 0 rlineto +closepath +eofill +newpath +152 -440 moveto +70 0 rlineto +0 -1 rlineto +-70 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +152 -440 moveto +71 0 rlineto +0 -2 rlineto +-71 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +152 -442 moveto +70 0 rlineto +0 -19 rlineto +-70 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +156 -455 moveto +(ICalResource) show +newpath +152 -440 moveto +71 0 rlineto +0 -22 rlineto +-71 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +96 -288 moveto +99 0 rlineto +0 -36 rlineto +-99 0 rlineto +closepath +eofill +newpath +96 -288 moveto +99 0 rlineto +0 -14 rlineto +-99 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +96 -288 moveto +100 0 rlineto +0 -15 rlineto +-100 0 rlineto +closepath +stroke +99 -302 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +96 -303 moveto +99 0 rlineto +0 -19 rlineto +-99 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +112 -316 moveto +(ResourceBase) show +newpath +96 -288 moveto +100 0 rlineto +0 -37 rlineto +-100 0 rlineto +closepath +stroke +1.0 1.0 0.78431374 setrgbcolor +newpath +248 -288 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +248 -288 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +248 -288 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 0.78431374 setrgbcolor +newpath +248 -290 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +259 -303 moveto +(Monitor) show +newpath +248 -288 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +432 -280 moveto +123 0 rlineto +0 -36 rlineto +-123 0 rlineto +closepath +eofill +newpath +432 -280 moveto +123 0 rlineto +0 -14 rlineto +-123 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +432 -280 moveto +124 0 rlineto +0 -15 rlineto +-124 0 rlineto +closepath +stroke +435 -294 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +432 -295 moveto +123 0 rlineto +0 -19 rlineto +-123 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +446 -308 moveto +(SearchProviderBase) show +newpath +432 -280 moveto +124 0 rlineto +0 -37 rlineto +-124 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +256 -310 moveto +256 -640 lineto +stroke +[] 0 setdash +newpath +249 -628 moveto +256 -640 lineto +stroke +newpath +263 -628 moveto +256 -640 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +144 -576 moveto +144 -325 lineto +stroke +[] 0 setdash +newpath +151 -337 moveto +144 -325 lineto +stroke +newpath +137 -337 moveto +144 -325 lineto +stroke +183 -427 moveto +(create) show +newpath +114 -440 moveto +114 -325 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +114 -325 moveto +121 -337 lineto +107 -337 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +114 -325 moveto +121 -337 lineto +107 -337 lineto +closepath +stroke +newpath +168 -440 moveto +168 -325 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +168 -325 moveto +175 -337 lineto +161 -337 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +168 -325 moveto +175 -337 lineto +161 -337 lineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +548 -576 moveto +548 -317 lineto +stroke +[] 0 setdash +newpath +555 -329 moveto +548 -317 lineto +stroke +newpath +541 -329 moveto +548 -317 lineto +stroke +573 -453 moveto +(create) show +1.0 1.0 1.0 setrgbcolor +newpath +312 -448 moveto +122 0 rlineto +0 -21 rlineto +-122 0 rlineto +closepath +eofill +newpath +312 -448 moveto +122 0 rlineto +0 -1 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +312 -448 moveto +123 0 rlineto +0 -2 rlineto +-123 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +312 -450 moveto +122 0 rlineto +0 -19 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +316 -463 moveto +(MessageSearchProvider) show +newpath +312 -448 moveto +123 0 rlineto +0 -22 rlineto +-123 0 rlineto +closepath +stroke +newpath +432 -448 moveto +432 -317 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +432 -317 moveto +439 -329 lineto +425 -329 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +432 -317 moveto +439 -329 lineto +425 -329 lineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +576 -877 moveto +576 -1032 lineto +stroke +[] 0 setdash +newpath +569 -1020 moveto +576 -1032 lineto +stroke +newpath +583 -1020 moveto +576 -1032 lineto +stroke +601 -960 moveto +(clear old values) show +1.0 1.0 1.0 setrgbcolor +newpath +624 -176 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +624 -176 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +624 -176 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +624 -178 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +646 -191 moveto +(Job) show +newpath +624 -176 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +308 -288 moveto +624 -198 lineto +stroke +[] 0 setdash +newpath +614 -208 moveto +624 -198 lineto +stroke +newpath +610 -194 moveto +624 -198 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +656 -198 moveto +656 -640 lineto +stroke +[] 0 setdash +newpath +649 -628 moveto +656 -640 lineto +stroke +newpath +663 -628 moveto +656 -640 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -240 moveto +76 0 rlineto +0 -21 rlineto +-76 0 rlineto +closepath +eofill +newpath +752 -240 moveto +76 0 rlineto +0 -1 rlineto +-76 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +752 -240 moveto +77 0 rlineto +0 -2 rlineto +-77 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -242 moveto +76 0 rlineto +0 -19 rlineto +-76 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +756 -255 moveto +(ItemDeleteJob) show +newpath +752 -240 moveto +77 0 rlineto +0 -22 rlineto +-77 0 rlineto +closepath +stroke +newpath +752 -240 moveto +684 -198 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +684 -198 moveto +697 -198 lineto +690 -210 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +684 -198 moveto +697 -198 lineto +690 -210 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -272 moveto +71 0 rlineto +0 -21 rlineto +-71 0 rlineto +closepath +eofill +newpath +752 -272 moveto +71 0 rlineto +0 -1 rlineto +-71 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +752 -272 moveto +72 0 rlineto +0 -2 rlineto +-72 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -274 moveto +71 0 rlineto +0 -19 rlineto +-71 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +783 -287 moveto +(...) show +newpath +752 -272 moveto +72 0 rlineto +0 -22 rlineto +-72 0 rlineto +closepath +stroke +newpath +752 -272 moveto +684 -198 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +684 -198 moveto +697 -202 lineto +686 -211 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +684 -198 moveto +697 -202 lineto +686 -211 lineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +316 -782 moveto +408 -944 lineto +stroke +[] 0 setdash +newpath +395 -937 moveto +408 -944 lineto +stroke +newpath +408 -930 moveto +408 -944 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +24 -16 moveto +198 0 rlineto +0 -20 rlineto +-198 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -16 moveto +198 0 rlineto +0 -20 rlineto +-198 0 rlineto +closepath +stroke +28 -29 moveto +(kabc) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -36 moveto +247 0 rlineto +0 -79 rlineto +-247 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -36 moveto +247 0 rlineto +0 -79 rlineto +-247 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +304 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +304 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +308 -29 moveto +(kmime) show +1.0 1.0 1.0 setrgbcolor +newpath +304 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +304 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +480 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +480 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +484 -29 moveto +(kioslave) show +1.0 1.0 1.0 setrgbcolor +newpath +480 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +480 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +40 -48 moveto +239 -48 lineto +249 -58 lineto +249 -105 lineto +40 -105 lineto +40 -48 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +40 -48 moveto +239 -48 lineto +249 -58 lineto +249 -105 lineto +40 -105 lineto +40 -48 lineto +stroke +0.69803923 0.69803923 0.69803923 setrgbcolor +newpath +239 -48 moveto +249 -58 lineto +239 -58 lineto +239 -48 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +239 -48 moveto +249 -58 lineto +239 -58 lineto +239 -48 lineto +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +42 -63 moveto +(some widgets to manipulate addressees) show +42 -76 moveto +(using Jobs and Monitor) show +[5.0 5.0 ] 0 setdash +newpath +126 -48 moveto +126 -16 lineto +stroke +newpath +264 -116 moveto +264 -288 lineto +stroke +[] 0 setdash +newpath +257 -276 moveto +264 -288 lineto +stroke +newpath +271 -276 moveto +264 -288 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +272 -116 moveto +624 -176 lineto +stroke +[] 0 setdash +newpath +610 -180 moveto +624 -176 lineto +stroke +newpath +613 -167 moveto +624 -176 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +328 -64 moveto +80 0 rlineto +0 -21 rlineto +-80 0 rlineto +closepath +eofill +newpath +328 -64 moveto +80 0 rlineto +0 -1 rlineto +-80 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +328 -64 moveto +81 0 rlineto +0 -2 rlineto +-81 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +328 -66 moveto +80 0 rlineto +0 -19 rlineto +-80 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +332 -79 moveto +(MessageModel) show +newpath +328 -64 moveto +81 0 rlineto +0 -22 rlineto +-81 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +512 -64 moveto +72 0 rlineto +0 -21 rlineto +-72 0 rlineto +closepath +eofill +newpath +512 -64 moveto +72 0 rlineto +0 -1 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +512 -64 moveto +73 0 rlineto +0 -2 rlineto +-73 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +512 -66 moveto +72 0 rlineto +0 -19 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +516 -79 moveto +(AkonadiSlave) show +newpath +512 -64 moveto +73 0 rlineto +0 -22 rlineto +-73 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +448 -440 moveto +72 0 rlineto +0 -21 rlineto +-72 0 rlineto +closepath +eofill +newpath +448 -440 moveto +72 0 rlineto +0 -1 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +448 -440 moveto +73 0 rlineto +0 -2 rlineto +-73 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +448 -442 moveto +72 0 rlineto +0 -19 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +452 -455 moveto +(StrigiProvider) show +newpath +448 -440 moveto +73 0 rlineto +0 -22 rlineto +-73 0 rlineto +closepath +stroke +newpath +494 -440 moveto +494 -317 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +494 -317 moveto +501 -329 lineto +487 -329 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +494 -317 moveto +501 -329 lineto +487 -329 lineto +closepath +stroke +showpage +%%Trailer diff --git a/doc/pics/akonadi_overview_uml_small.png b/doc/pics/akonadi_overview_uml_small.png new file mode 100644 index 0000000000000000000000000000000000000000..06112b1d9893199161d690dcccbb6fee07afad0a GIT binary patch literal 57683 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

T%nDNVm+ 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%0ANfBnU$9b<(P|s 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 b_=(-MM>nDHIyWQmY#atpS3j3^P6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +