diff --git a/CMakeLists.txt b/CMakeLists.txt index 63c0638f1..4ddca3a6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,166 +1,166 @@ cmake_minimum_required(VERSION 3.0) set(KDEPIM_RUNTIME_VERSION_NUMBER "5.7.40") project(kdepim-runtime VERSION ${KDEPIM_RUNTIME_VERSION_NUMBER}) if (POLICY CMP0053) cmake_policy(SET CMP0053 NEW) endif() ############### 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_DEV_VERSION # is empty for final versions. For development versions "build" is # something like "pre", "", "alpha2", "beta1", "beta2", "rc1", "rc2". # # Examples in chronological order: # # 3.0 # 3.0.1 # 3.1 # 3.1 beta1 # 3.1 beta2 # 3.1 rc1 # 3.1 # 3.1.1 # 3.2 pre # 3.2 if(NOT DEFINED KDEPIM_DEV_VERSION) set(KDEPIM_DEV_VERSION "alpha") endif() set(KDEPIM_RUNTIME_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}${KDEPIM_DEV_VERSION}") configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) set(KF5_VERSION "5.39.0") find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${kdepim-runtime_SOURCE_DIR}/cmake/ ${ECM_MODULE_PATH}) # TODO: Remove the cmake/deprecated directory when we'll require ECM >= 5.41.0 if("${ECM_VERSION}" VERSION_LESS "5.41.0") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${kdepim-runtime_SOURCE_DIR}/cmake/deprecated) endif() include(CMakePackageConfigHelpers) include(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMQtDeclareLoggingCategory) include(ECMCoverageOption) set(QT_REQUIRED_VERSION "5.8.0") set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}") set(KDEPIMRUNTIME_LIB_SOVERSION "5") set(AKONADI_VERSION "5.6.90") set(KCONTACTS_LIB_VERSION "5.6.90") set(KCALENDARCORE_LIB_VERSION "5.6.90") set(IDENTITYMANAGEMENT_LIB_VERSION "5.6.90") set(KMAILTRANSPORT_LIB_VERSION "5.6.90") set(CALENDARUTILS_LIB_VERSION "5.6.90") -set(KDAV_LIB_VERSION "5.6.90") +set(KDAV_LIB_VERSION "5.7.41") set(KIMAP_LIB_VERSION "5.6.90") set(KMBOX_LIB_VERSION "5.6.90") set(AKONADICALENDAR_LIB_VERSION "5.6.90") set(KONTACTINTERFACE_LIB_VERSION "5.6.90") set(AKONADIKALARM_LIB_VERSION "5.6.90") set(KMIME_LIB_VERSION "5.6.90") set(XMLRPCCLIENT_LIB_VERSION "5.6.90") set(KCONTACTS_LIB_VERSION "5.6.90") set(AKONADIMIME_LIB_VERSION "5.6.90") set(AKONADICONTACT_LIB_VERSION "5.6.90") set(AKONADINOTE_LIB_VERSION "5.6.90") set(PIMCOMMON_LIB_VERSION "5.6.90") set(KGAPI_LIB_VERSION "5.6.90") set( SharedMimeInfo_MINIMUM_VERSION "1.0" ) find_package(SharedMimeInfo ${SharedMimeInfo_MINIMUM_VERSION} REQUIRED) find_package(Sasl2) set_package_properties(Sasl2 PROPERTIES TYPE REQUIRED) # QT5 package find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Network Widgets Test XmlPatterns DBus WebEngineWidgets) find_package(Qt5 OPTIONAL_COMPONENTS TextToSpeech) if (NOT Qt5TextToSpeech_FOUND) message(STATUS "Qt5TextToSpeech not found, speech feature will be disabled") else() add_definitions(-DHAVE_TEXTTOSPEECH) endif() # KF5 package 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(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) # for KPluralHandlingSpinBox find_package(KF5Notifications ${KF5_VERSION} CONFIG REQUIRED) # pop3 # KdepimLibs package find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KPimKDAV ${KDAV_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailTransportAkonadi ${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(KF5IMAP ${KIMAP_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiNotes ${AKONADINOTE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiCalendar ${AKONADICALENDAR_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimCommon ${PIMCOMMON_LIB_VERSION} CONFIG REQUIRED) find_package(KPimGAPI ${KGAPI_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_definitions( -DQT_NO_URL_CAST_FROM_STRING ) add_definitions( -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT ) #add_definitions( -DQT_DISABLE_DEPRECATED_BEFORE=0x060000 ) add_subdirectory(resources) add_subdirectory(agents) add_subdirectory(plugins) add_subdirectory(defaultsetup) add_subdirectory(kioslave) add_subdirectory(migration) add_subdirectory(doc) ## 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.renamecategories kdepim-runtime.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES ) diff --git a/resources/dav/resource/davfreebusyhandler.cpp b/resources/dav/resource/davfreebusyhandler.cpp index 4a4da6fe6..a4a0ed2fd 100644 --- a/resources/dav/resource/davfreebusyhandler.cpp +++ b/resources/dav/resource/davfreebusyhandler.cpp @@ -1,229 +1,229 @@ /* Copyright (c) 2011 Grégory Oestreicher 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. */ #include "davfreebusyhandler.h" #include "settings.h" #include #include #include #include #include #include #include #include #include "davresource_debug.h" DavFreeBusyHandler::DavFreeBusyHandler(QObject *parent) : QObject(parent) { } void DavFreeBusyHandler::canHandleFreeBusy(const QString &email) { const KDAV::DavUrl::List urls = Settings::self()->configuredDavUrls(); for (const KDAV::DavUrl &url : urls) { if (url.protocol() == KDAV::CalDav) { ++mRequestsTracker[email].handlingJobCount; KDAV::DavPrincipalSearchJob *job = new KDAV::DavPrincipalSearchJob(url, KDAV::DavPrincipalSearchJob::EmailAddress, email); job->setProperty("email", QVariant::fromValue(email)); job->setProperty("url", QVariant::fromValue(url.url().toString())); job->fetchProperty(QStringLiteral("schedule-inbox-URL"), QStringLiteral("urn:ietf:params:xml:ns:caldav")); connect(job, &KDAV::DavPrincipalSearchJob::result, this, &DavFreeBusyHandler::onPrincipalSearchJobFinished); job->start(); } } } void DavFreeBusyHandler::retrieveFreeBusy(const QString &email, const QDateTime &start, const QDateTime &end) { if (!mPrincipalScheduleOutbox.contains(email)) { Q_EMIT freeBusyRetrieved(email, QString(), false, i18n("No schedule-outbox found for %1", email)); return; } KCalCore::FreeBusy::Ptr fb(new KCalCore::FreeBusy(start, end)); KCalCore::Attendee::Ptr att(new KCalCore::Attendee(QString(), email)); fb->addAttendee(att); KCalCore::ICalFormat formatter; QByteArray fbData = formatter.createScheduleMessage(fb, KCalCore::iTIPRequest).toUtf8(); foreach (const QString &outbox, mPrincipalScheduleOutbox[email]) { ++mRequestsTracker[email].retrievalJobCount; uint requestId = mNextRequestId++; QUrl url(outbox); KIO::StoredTransferJob *job = KIO::storedHttpPost(fbData, url); job->addMetaData(QStringLiteral("content-type"), QStringLiteral("text/calendar")); job->setProperty("email", QVariant::fromValue(email)); job->setProperty("request-id", QVariant::fromValue(requestId)); connect(job, &KDAV::DavPrincipalSearchJob::result, this, &DavFreeBusyHandler::onRetrieveFreeBusyJobFinished); job->start(); } } void DavFreeBusyHandler::onPrincipalSearchJobFinished(KJob *job) { QString email = job->property("email").toString(); int handlingJobCount = --mRequestsTracker[email].handlingJobCount; if (job->error()) { if (handlingJobCount == 0 && !mRequestsTracker[email].handlingJobSuccessful) { Q_EMIT handlesFreeBusy(email, false); } return; } KDAV::DavPrincipalSearchJob *davJob = qobject_cast(job); - const QList results = davJob->results(); + const QVector results = davJob->results(); if (results.isEmpty()) { if (handlingJobCount == 0 && !mRequestsTracker[email].handlingJobSuccessful) { Q_EMIT handlesFreeBusy(email, false); } return; } mRequestsTracker[email].handlingJobSuccessful = true; for (const KDAV::DavPrincipalSearchJob::Result &result : qAsConst(results)) { qCDebug(DAVRESOURCE_LOG) << result.value; QUrl url(davJob->property("url").toString()); if (result.value.startsWith(QLatin1Char('/'))) { // href is only a path, use request url to complete url.setPath(result.value, QUrl::TolerantMode); } else { // href is a complete url url = QUrl::fromUserInput(result.value); } if (!mPrincipalScheduleOutbox[email].contains(url.url())) { mPrincipalScheduleOutbox[email] << url.url(); } } if (handlingJobCount == 0) { Q_EMIT handlesFreeBusy(email, true); } } void DavFreeBusyHandler::onRetrieveFreeBusyJobFinished(KJob *job) { QString email = job->property("email").toString(); uint requestId = job->property("request-id").toUInt(); int retrievalJobCount = --mRequestsTracker[email].retrievalJobCount; if (job->error()) { if (retrievalJobCount == 0 && !mRequestsTracker[email].retrievalJobSuccessful) { Q_EMIT (freeBusyRetrieved(email, QString(), false, job->errorString())); } return; } /* * Extract info from a document like the following: * * * * * mailto:wilfredo@example.com * * 2.0;Success * BEGIN:VCALENDAR * VERSION:2.0 * PRODID:-//Example Corp.//CalDAV Server//EN * METHOD:REPLY * BEGIN:VFREEBUSY * UID:4FD3AD926350 * DTSTAMP:20090602T200733Z * DTSTART:20090602T000000Z * DTEND:20090604T000000Z * ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com * ATTENDEE;CN="Wilfredo Sanchez Vega":mailto:wilfredo@example.com * FREEBUSY;FBTYPE=BUSY:20090602T110000Z/20090602T120000Z * FREEBUSY;FBTYPE=BUSY:20090603T170000Z/20090603T180000Z * END:VFREEBUSY * END:VCALENDAR * * * * * mailto:mike@example.org * * 3.7;Invalid calendar user * * */ KIO::StoredTransferJob *postJob = qobject_cast(job); QDomDocument response; response.setContent(postJob->data(), true); QDomElement scheduleResponse = response.documentElement(); // We are only expecting one response tag QDomElement responseElement = KDAV::Utils::firstChildElementNS(scheduleResponse, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("response")); if (responseElement.isNull()) { if (retrievalJobCount == 0 && !mRequestsTracker[email].retrievalJobSuccessful) { Q_EMIT (freeBusyRetrieved(email, QString(), false, i18n("Invalid response from the server"))); } return; } // We can load directly the calendar-data and use its content to create // an incidence base that will give us everything we need to test // the success QDomElement calendarDataElement = KDAV::Utils::firstChildElementNS(responseElement, QStringLiteral("urn:ietf:params:xml:ns:caldav"), QStringLiteral("calendar-data")); if (calendarDataElement.isNull()) { if (retrievalJobCount == 0 && !mRequestsTracker[email].retrievalJobSuccessful) { Q_EMIT (freeBusyRetrieved(email, QString(), false, i18n("Invalid response from the server"))); } return; } QString rawData = calendarDataElement.text(); KCalCore::ICalFormat format; KCalCore::FreeBusy::Ptr fb = format.parseFreeBusy(rawData); if (fb.isNull()) { if (retrievalJobCount == 0 && !mRequestsTracker[email].retrievalJobSuccessful) { Q_EMIT (freeBusyRetrieved(email, QString(), false, i18n("Unable to parse free-busy data received"))); } return; } // We're safe now mRequestsTracker[email].retrievalJobSuccessful = true; // fb->clearAttendees(); if (mRequestsTracker[email].resultingFreeBusy[requestId].isNull()) { mRequestsTracker[email].resultingFreeBusy[requestId] = fb; } else { mRequestsTracker[email].resultingFreeBusy[requestId]->merge(fb); } if (retrievalJobCount == 0) { QString fbStr = format.createScheduleMessage(mRequestsTracker[email].resultingFreeBusy[requestId], KCalCore::iTIPRequest); Q_EMIT freeBusyRetrieved(email, fbStr, true, QString()); } } diff --git a/resources/dav/resource/searchdialog.cpp b/resources/dav/resource/searchdialog.cpp index 82264c48c..bb77fa872 100644 --- a/resources/dav/resource/searchdialog.cpp +++ b/resources/dav/resource/searchdialog.cpp @@ -1,211 +1,211 @@ /* * Copyright (c) 2011 Grégory Oestreicher * * 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. */ #include "searchdialog.h" #include #include #include #include #include #include "davresource_debug.h" #include #include #include #include #include #include #include SearchDialog::SearchDialog(QWidget *parent) : QDialog(parent) , mModel(new QStandardItemModel(this)) { setWindowTitle(i18n("Search")); QWidget *mainWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(mainWidget); mUi.setupUi(mainWidget); mUi.credentialsGroup->setVisible(false); mUi.searchResults->setModel(mModel); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &SearchDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &SearchDialog::reject); mainLayout->addWidget(buttonBox); buttonBox->button(QDialogButtonBox::Ok)->setText(i18n("Add Selected Items")); connect(mUi.searchUrl, &KLineEdit::textChanged, this, &SearchDialog::checkUserInput); connect(mUi.searchParam, &KLineEdit::textChanged, this, &SearchDialog::checkUserInput); connect(mUi.searchButton, &QPushButton::clicked, this, &SearchDialog::search); checkUserInput(); } SearchDialog::~SearchDialog() { } bool SearchDialog::useDefaultCredentials() const { return mUi.useDefaultCreds->isChecked(); } void SearchDialog::setUsername(const QString &user) { mUi.username->setText(user); } QString SearchDialog::username() const { return mUi.username->text(); } void SearchDialog::setPassword(const QString &password) { mUi.password->setText(password); } QString SearchDialog::password() const { return mUi.password->text(); } QStringList SearchDialog::selection() const { const QModelIndexList indexes = mUi.searchResults->selectionModel()->selectedIndexes(); QStringList ret; ret.reserve(indexes.count()); for (const QModelIndex &index : indexes) { qCritical() << "SELECTED DATA: " << index.data(Qt::UserRole + 1).toString(); ret << index.data(Qt::UserRole + 1).toString(); } return ret; } void SearchDialog::checkUserInput() { if (mUi.searchUrl->text().isEmpty() || mUi.searchParam->text().isEmpty()) { mUi.searchButton->setEnabled(false); } else { mUi.searchButton->setEnabled(true); } } void SearchDialog::search() { mUi.searchResults->setEnabled(false); mModel->clear(); KDAV::DavPrincipalSearchJob::FilterType filter; if (mUi.searchType->currentIndex() == 0) { filter = KDAV::DavPrincipalSearchJob::DisplayName; } else { filter = KDAV::DavPrincipalSearchJob::EmailAddress; } QUrl url(mUi.searchUrl->text()); url.setUserInfo(QString()); KDAV::DavUrl davUrl; davUrl.setUrl(url); KDAV::DavPrincipalSearchJob *job = new KDAV::DavPrincipalSearchJob(davUrl, filter, mUi.searchParam->text()); const KDAV::DavProtocolBase *proto = KDAV::DavManager::self()->davProtocol(KDAV::CalDav); job->fetchProperty(proto->principalHomeSet(), proto->principalHomeSetNS()); proto = KDAV::DavManager::self()->davProtocol(KDAV::CardDav); job->fetchProperty(proto->principalHomeSet(), proto->principalHomeSetNS()); connect(job, &KDAV::DavPrincipalSearchJob::result, this, &SearchDialog::onSearchJobFinished); job->start(); } void SearchDialog::onSearchJobFinished(KJob *job) { if (job->error()) { KMessageBox::error(this, i18n("An error occurred when executing search:\n%1", job->errorText())); return; } KDAV::DavPrincipalSearchJob *davJob = qobject_cast(job); const KDAV::DavProtocolBase *caldav = KDAV::DavManager::self()->davProtocol(KDAV::CalDav); KDAV::DavUrl davUrl = davJob->davUrl(); QUrl url = davUrl.url(); - const QList results = davJob->results(); + const QVector results = davJob->results(); for (const KDAV::DavPrincipalSearchJob::Result &result : results) { if (result.value.startsWith(QLatin1Char('/'))) { url.setPath(result.value, QUrl::TolerantMode); } else { QUrl tmp(result.value); tmp.setUserInfo(QString()); url = tmp; } davUrl.setUrl(url); if (result.property == caldav->principalHomeSet()) { davUrl.setProtocol(KDAV::CalDav); } else { davUrl.setProtocol(KDAV::CardDav); } KDAV::DavCollectionsFetchJob *fetchJob = new KDAV::DavCollectionsFetchJob(davUrl); connect(fetchJob, &KDAV::DavCollectionsFetchJob::result, this, &SearchDialog::onCollectionsFetchJobFinished); fetchJob->start(); ++mSubJobCount; } } void SearchDialog::onCollectionsFetchJobFinished(KJob *job) { --mSubJobCount; if (mSubJobCount == 0) { mUi.searchResults->setEnabled(true); } if (job->error()) { if (mSubJobCount == 0) { KMessageBox::error(this, i18n("An error occurred when executing search:\n%1", job->errorText())); } return; } KDAV::DavCollectionsFetchJob *davJob = qobject_cast(job); const KDAV::DavCollection::List collections = davJob->collections(); for (const KDAV::DavCollection &collection : collections) { QStandardItem *item = new QStandardItem(collection.displayName()); QString data(KDAV::Utils::protocolName(collection.url().protocol()) + QLatin1Char('|') + collection.url().toDisplayString()); item->setData(data, Qt::UserRole + 1); item->setToolTip(collection.url().toDisplayString()); if (collection.url().protocol() == KDAV::CalDav) { item->setIcon(QIcon::fromTheme(QStringLiteral("view-calendar"))); } else { item->setIcon(QIcon::fromTheme(QStringLiteral("view-pim-contacts"))); } mModel->appendRow(item); } }