/* Copyright (c) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company Copyright (c) 2004 Cornelius Schumacher 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 "freebusymanager.h" #include "freebusymanager_p.h" #include "freebusydownloadjob_p.h" #include "mailscheduler_p.h" #include "publishdialog.h" #include "calendarsettings.h" #include "utils_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Akonadi; using namespace KCalCore; /// Free helper functions KUrl replaceVariablesUrl(const KUrl &url, const QString &email) { QString emailName; QString emailHost; const int atPos = email.indexOf('@'); if (atPos >= 0) { emailName = email.left(atPos); emailHost = email.mid(atPos + 1); } QString saveStr = url.path(); saveStr.replace(QRegExp("%[Ee][Mm][Aa][Ii][Ll]%"), email); saveStr.replace(QRegExp("%[Nn][Aa][Mm][Ee]%"), emailName); saveStr.replace(QRegExp("%[Ss][Ee][Rr][Vv][Ee][Rr]%"), emailHost); KUrl retUrl(url); retUrl.setPath(saveStr); return retUrl; } // We need this function because using KIO::NetAccess::exists() // is useless for the http and https protocols. And getting back // arbitrary data is also useless because a server can respond back // with a "no such document" page. So we need smart checking. FbCheckerJob::FbCheckerJob(const QList &urlsToCheck, QObject *parent) : KJob(parent), mUrlsToCheck(urlsToCheck) { } void FbCheckerJob::start() { checkNextUrl(); } void FbCheckerJob::checkNextUrl() { if (mUrlsToCheck.isEmpty()) { kDebug() << "No fb file found"; setError(KJob::UserDefinedError); emitResult(); return; } const KUrl url = mUrlsToCheck.takeFirst(); mData.clear(); KIO::TransferJob *job = KIO::get(url, KIO::NoReload, KIO::HideProgressInfo); connect(job, SIGNAL(data(KIO::Job*,QByteArray)), this, SLOT(dataReceived(KIO::Job*,QByteArray))); connect(job, SIGNAL(result(KJob*)), this, SLOT(onGetJobFinished(KJob*))); } void FbCheckerJob::dataReceived(KIO::Job*, const QByteArray &data) { mData.append(data); } void FbCheckerJob::onGetJobFinished(KJob *job) { KIO::TransferJob *transferJob = static_cast(job); if (mData.contains("BEGIN:VCALENDAR")) { kDebug() << "found freebusy"; mValidUrl = transferJob->url(); emitResult(); } else { checkNextUrl(); } } KUrl FbCheckerJob::validUrl() const { return mValidUrl; } /// FreeBusyManagerPrivate::FreeBusyProviderRequest FreeBusyManagerPrivate::FreeBusyProviderRequest::FreeBusyProviderRequest(const QString &provider) : mRequestStatus(NotStarted), mInterface(0) { mInterface = QSharedPointer( new QDBusInterface("org.freedesktop.Akonadi.Resource." + provider, "/FreeBusyProvider", "org.freedesktop.Akonadi.Resource.FreeBusyProvider")); } /// FreeBusyManagerPrivate::FreeBusyProvidersRequestsQueue FreeBusyManagerPrivate::FreeBusyProvidersRequestsQueue::FreeBusyProvidersRequestsQueue( const QString &start, const QString &end) : mHandlersCount(0), mResultingFreeBusy(0) { KDateTime startDate, endDate; if (!start.isEmpty()) { mStartTime = start; startDate = KDateTime::fromString(start); } else { // Set the start of the period to today 00:00:00 startDate = KDateTime(KDateTime::currentLocalDate()); mStartTime = startDate.toString(); } if (!end.isEmpty()) { mEndTime = end; endDate = KDateTime::fromString(end); } else { // Set the end of the period to today + 14 days. endDate = KDateTime(KDateTime::currentLocalDate()).addDays(14); mEndTime = endDate.toString(); } mResultingFreeBusy = KCalCore::FreeBusy::Ptr(new KCalCore::FreeBusy(startDate, endDate)); } FreeBusyManagerPrivate::FreeBusyProvidersRequestsQueue::FreeBusyProvidersRequestsQueue( const KDateTime &start, const KDateTime &end) : mHandlersCount(0), mResultingFreeBusy(0) { mStartTime = start.toString(); mEndTime = end.toString(); mResultingFreeBusy = KCalCore::FreeBusy::Ptr(new KCalCore::FreeBusy(start, end)); } /// FreeBusyManagerPrivate FreeBusyManagerPrivate::FreeBusyManagerPrivate(FreeBusyManager *q) : QObject(), q_ptr(q), mTimerID(0), mUploadingFreeBusy(false), mBrokenUrl(false), mParentWidgetForRetrieval(0) { connect(this, SIGNAL(freeBusyUrlRetrieved(QString,KUrl)), SLOT(finishProcessRetrieveQueue(QString,KUrl))); } QString FreeBusyManagerPrivate::freeBusyDir() const { return KStandardDirs::locateLocal("data", QLatin1String("korganizer/freebusy")); } void FreeBusyManagerPrivate::checkFreeBusyUrl() { KUrl targetURL(CalendarSettings::self()->freeBusyPublishUrl()); mBrokenUrl = targetURL.isEmpty() || !targetURL.isValid(); } static QString configFile() { static QString file = KStandardDirs::locateLocal("data", QLatin1String("korganizer/freebusyurls")); return file; } void FreeBusyManagerPrivate::fetchFreeBusyUrl(const QString &email) { // First check if there is a specific FB url for this email KConfig cfg(configFile()); KConfigGroup group = cfg.group(email); QString url = group.readEntry(QLatin1String("url")); if (!url.isEmpty()) { kDebug() << "Found cached url:" << url; KUrl cachedUrl(url); if (Akonadi::CalendarUtils::thatIsMe(email)) { cachedUrl.setUser(CalendarSettings::self()->freeBusyRetrieveUser()); cachedUrl.setPass(CalendarSettings::self()->freeBusyRetrievePassword()); } emit freeBusyUrlRetrieved(email, replaceVariablesUrl(cachedUrl, email)); return; } // Try with the url configured by preferred email in kcontactmanager Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(); job->setQuery(Akonadi::ContactSearchJob::Email, email); job->setProperty("contactEmail", QVariant::fromValue(email)); connect(job, SIGNAL(result(KJob*)), this, SLOT(contactSearchJobFinished(KJob*))); job->start(); } void FreeBusyManagerPrivate::contactSearchJobFinished(KJob *_job) { const QString email = _job->property("contactEmail").toString(); if (_job->error()) { kError() << "Error while searching for contact: " << _job->errorString() << ", email = " << email; emit freeBusyUrlRetrieved(email, KUrl()); return; } Akonadi::ContactSearchJob *job = qobject_cast(_job); KConfig cfg(configFile()); KConfigGroup group = cfg.group(email); QString url = group.readEntry(QLatin1String("url")); const KABC::Addressee::List contacts = job->contacts(); foreach(const KABC::Addressee &contact, contacts) { const QString pref = contact.preferredEmail(); if (!pref.isEmpty() && pref != email) { group = cfg.group(pref); url = group.readEntry("url"); kDebug() << "Preferred email of" << email << "is" << pref; if (!url.isEmpty()) { kDebug() << "Taken url from preferred email:" << url; emit freeBusyUrlRetrieved(email, replaceVariablesUrl(KUrl(url), email)); return; } } } // None found. Check if we do automatic FB retrieving then if (!CalendarSettings::self()->freeBusyRetrieveAuto()) { // No, so no FB list here kDebug() << "No automatic retrieving"; emit freeBusyUrlRetrieved(email, KUrl()); return; } // Sanity check: Don't download if it's not a correct email // address (this also avoids downloading for "(empty email)"). int emailpos = email.indexOf(QLatin1Char('@')); if (emailpos == -1) { kWarning() << "No '@' found in" << email; emit freeBusyUrlRetrieved(email, KUrl()); return; } const QString emailHost = email.mid(emailpos + 1); // Build the URL if (CalendarSettings::self()->freeBusyCheckHostname()) { // Don't try to fetch free/busy data for users not on the specified servers // This tests if the hostnames match, or one is a subset of the other const QString hostDomain = KUrl(CalendarSettings::self()->freeBusyRetrieveUrl()).host(); if (hostDomain != emailHost && !hostDomain.endsWith(QLatin1Char('.') + emailHost) && !emailHost.endsWith(QLatin1Char('.') + hostDomain)) { // Host names do not match kDebug() << "Host '" << hostDomain << "' doesn't match email '" << email << '\''; emit freeBusyUrlRetrieved(email, KUrl()); return; } } if (CalendarSettings::self()->freeBusyRetrieveUrl().contains(QRegExp("\\.[xiv]fb$"))) { // user specified a fullpath // do variable string replacements to the URL (MS Outlook style) const KUrl sourceUrl(CalendarSettings::self()->freeBusyRetrieveUrl()); KUrl fullpathURL = replaceVariablesUrl(sourceUrl, email); // set the User and Password part of the URL fullpathURL.setUser(CalendarSettings::self()->freeBusyRetrieveUser()); fullpathURL.setPass(CalendarSettings::self()->freeBusyRetrievePassword()); // no need to cache this URL as this is pretty fast to get from the config value. // return the fullpath URL kDebug() << "Found url. email=" << email << "; url=" << fullpathURL; emit freeBusyUrlRetrieved(email, fullpathURL); return; } // else we search for a fb file in the specified URL with known possible extensions const QStringList extensions = QStringList() << "xfb" << "ifb" << "vfb"; QStringList::ConstIterator ext; QList urlsToCheck; for (ext = extensions.constBegin(); ext != extensions.constEnd(); ++ext) { // build a url for this extension const KUrl sourceUrl = CalendarSettings::self()->freeBusyRetrieveUrl(); KUrl dirURL = replaceVariablesUrl(sourceUrl, email); if (CalendarSettings::self()->freeBusyFullDomainRetrieval()) { dirURL.addPath(email + '.' + (*ext)); } else { // Cut off everything left of the @ sign to get the user name. const QString emailName = email.left(emailpos); dirURL.addPath(emailName + '.' + (*ext)); } dirURL.setUser(CalendarSettings::self()->freeBusyRetrieveUser()); dirURL.setPass(CalendarSettings::self()->freeBusyRetrievePassword()); urlsToCheck << dirURL; } KJob *checkerJob = new FbCheckerJob(urlsToCheck, this); checkerJob->setProperty("email", email); connect(checkerJob, SIGNAL(result(KJob*)), this, SLOT(fbCheckerJobFinished(KJob*))); checkerJob->start(); } void FreeBusyManagerPrivate::fbCheckerJobFinished(KJob *job) { const QString email = job->property("email").toString(); if (!job->error()) { FbCheckerJob *checkerJob = static_cast(job); KUrl dirURL = checkerJob->validUrl(); // write the URL to the cache KConfig cfg(configFile()); KConfigGroup group = cfg.group(email); group.writeEntry("url", dirURL.prettyUrl()); // prettyURL() does not write user nor password kDebug() << "Found url email=" << email << "; url=" << dirURL; emit freeBusyUrlRetrieved(email, dirURL); } else { kDebug() << "Returning invalid url"; emit freeBusyUrlRetrieved(email, KUrl()); } } QString FreeBusyManagerPrivate::freeBusyToIcal(const KCalCore::FreeBusy::Ptr &freebusy) { return mFormat.createScheduleMessage(freebusy, KCalCore::iTIPPublish); } KCalCore::FreeBusy::Ptr FreeBusyManagerPrivate::iCalToFreeBusy(const QByteArray &freeBusyData) { const QString freeBusyVCal(QString::fromUtf8(freeBusyData)); KCalCore::FreeBusy::Ptr fb = mFormat.parseFreeBusy(freeBusyVCal); if (!fb) { kDebug() << "Error parsing free/busy"; kDebug() << freeBusyVCal; } return fb; } KCalCore::FreeBusy::Ptr FreeBusyManagerPrivate::ownerFreeBusy() { KDateTime start = KDateTime::currentUtcDateTime(); KDateTime end = start.addDays(CalendarSettings::self()->freeBusyPublishDays()); KCalCore::Event::List events = mCalendar ? mCalendar->rawEvents(start.date(), end.date()) : KCalCore::Event::List(); KCalCore::FreeBusy::Ptr freebusy(new KCalCore::FreeBusy(events, start, end)); freebusy->setOrganizer(KCalCore::Person::Ptr( new KCalCore::Person(Akonadi::CalendarUtils::fullName(), Akonadi::CalendarUtils::email()))); return freebusy; } QString FreeBusyManagerPrivate::ownerFreeBusyAsString() { return freeBusyToIcal(ownerFreeBusy()); } void FreeBusyManagerPrivate::processFreeBusyDownloadResult(KJob *_job) { Q_Q(FreeBusyManager); FreeBusyDownloadJob *job = qobject_cast(_job); Q_ASSERT(job); if (job->error()) { kError() << "Error downloading freebusy" << _job->errorString(); KMessageBox::sorry( mParentWidgetForRetrieval, i18n("Failed to download free/busy data from: %1\nReason: %2", job->url().prettyUrl(), job->errorText()), i18n("Free/busy retrieval error")); // TODO: Ask for a retry? (i.e. queue the email again when the user wants it). // Make sure we don't fill up the map with unneeded data on failures. mFreeBusyUrlEmailMap.take(job->url()); } else { KCalCore::FreeBusy::Ptr fb = iCalToFreeBusy(job->rawFreeBusyData()); Q_ASSERT(mFreeBusyUrlEmailMap.contains(job->url())); const QString email = mFreeBusyUrlEmailMap.take(job->url()); if (fb) { KCalCore::Person::Ptr p = fb->organizer(); p->setEmail(email); q->saveFreeBusy(fb, p); kDebug() << "Freebusy retrieved for " << email; emit q->freeBusyRetrieved(fb, email); } else { kError() << "Error downloading freebusy, invalid fb."; KMessageBox::sorry( mParentWidgetForRetrieval, i18n("Failed to parse free/busy information that was retrieved from: %1", job->url().prettyUrl()), i18n("Free/busy retrieval error")); } } // When downloading failed or finished, start a job for the next one in the // queue if needed. processRetrieveQueue(); } void FreeBusyManagerPrivate::processFreeBusyUploadResult(KJob *_job) { KIO::FileCopyJob *job = static_cast(_job); if (job->error()) { KMessageBox::sorry( job->ui()->window(), i18n("

The software could not upload your free/busy list to " "the URL '%1'. There might be a problem with the access " "rights, or you specified an incorrect URL. The system said: " "%2.

" "

Please check the URL or contact your system administrator." "

", job->destUrl().prettyUrl(), job->errorString())); } // Delete temp file KUrl src = job->srcUrl(); Q_ASSERT(src.isLocalFile()); if (src.isLocalFile()) { QFile::remove(src.toLocalFile()); } mUploadingFreeBusy = false; } void FreeBusyManagerPrivate::processRetrieveQueue() { if (mRetrieveQueue.isEmpty()) { return; } QString email = mRetrieveQueue.takeFirst(); // First, try to find all agents that are free-busy providers QStringList providers = getFreeBusyProviders(); kDebug() << "Got the following FreeBusy providers: " << providers; // If some free-busy providers were found let's query them first and ask them // if they manage the free-busy information for the email address we have. if (!providers.isEmpty()) { queryFreeBusyProviders(providers, email); } else { fetchFreeBusyUrl(email); } return; } void FreeBusyManagerPrivate::finishProcessRetrieveQueue(const QString &email, const KUrl &freeBusyUrlForEmail) { Q_Q(FreeBusyManager); if (!freeBusyUrlForEmail.isValid()) { kDebug() << "Invalid FreeBusy URL" << freeBusyUrlForEmail.prettyUrl() << email; return; } if (mFreeBusyUrlEmailMap.contains(freeBusyUrlForEmail)) { kDebug() << "Download already in progress for " << freeBusyUrlForEmail; return; } mFreeBusyUrlEmailMap.insert(freeBusyUrlForEmail, email); FreeBusyDownloadJob *job = new FreeBusyDownloadJob(freeBusyUrlForEmail, mParentWidgetForRetrieval); q->connect(job, SIGNAL(result(KJob*)), SLOT(processFreeBusyDownloadResult(KJob*))); job->start(); } void FreeBusyManagerPrivate::uploadFreeBusy() { Q_Q(FreeBusyManager); // user has automatic uploading disabled, bail out if (!CalendarSettings::self()->freeBusyPublishAuto() || CalendarSettings::self()->freeBusyPublishUrl().isEmpty()) { return; } if (mTimerID != 0) { // A timer is already running, so we don't need to do anything return; } int now = static_cast(QDateTime::currentDateTime().toTime_t()); int eta = static_cast(mNextUploadTime.toTime_t()) - now; if (!mUploadingFreeBusy) { // Not currently uploading if (mNextUploadTime.isNull() || QDateTime::currentDateTime() > mNextUploadTime) { // No uploading have been done in this session, or delay time is over q->publishFreeBusy(); return; } // We're in the delay time and no timer is running. Start one if (eta <= 0) { // Sanity check failed - better do the upload q->publishFreeBusy(); return; } } else { // We are currently uploading the FB list. Start the timer if (eta <= 0) { kDebug() << "This shouldn't happen! eta <= 0"; eta = 10; // whatever } } // Start the timer mTimerID = q->startTimer(eta * 1000); if (mTimerID == 0) { // startTimer failed - better do the upload q->publishFreeBusy(); } } QStringList FreeBusyManagerPrivate::getFreeBusyProviders() const { QStringList providers; Akonadi::AgentInstance::List agents = Akonadi::AgentManager::self()->instances(); foreach(const Akonadi::AgentInstance &agent, agents) { if (agent.type().capabilities().contains(QLatin1String("FreeBusyProvider"))) { providers << agent.identifier(); } } return providers; } void FreeBusyManagerPrivate::queryFreeBusyProviders(const QStringList &providers, const QString &email) { if (!mProvidersRequestsByEmail.contains(email)) { mProvidersRequestsByEmail[email] = FreeBusyProvidersRequestsQueue(); } foreach(const QString &provider, providers) { FreeBusyProviderRequest request(provider); connect(request.mInterface.data(), SIGNAL(handlesFreeBusy(QString,bool)), this, SLOT(onHandlesFreeBusy(QString,bool))); request.mInterface->call("canHandleFreeBusy", email); request.mRequestStatus = FreeBusyProviderRequest::HandlingRequested; mProvidersRequestsByEmail[email].mRequests << request; } } void FreeBusyManagerPrivate::queryFreeBusyProviders(const QStringList &providers, const QString &email, const KDateTime &start, const KDateTime &end) { if (!mProvidersRequestsByEmail.contains(email)) { mProvidersRequestsByEmail[email] = FreeBusyProvidersRequestsQueue(start, end); } queryFreeBusyProviders(providers, email); } void FreeBusyManagerPrivate::onHandlesFreeBusy(const QString &email, bool handles) { if (!mProvidersRequestsByEmail.contains(email)) { return; } QDBusInterface *iface = dynamic_cast(sender()); if (!iface) { return; } FreeBusyProvidersRequestsQueue *queue = &mProvidersRequestsByEmail[email]; QString respondingService = iface->service(); kDebug() << respondingService << "responded to our FreeBusy request:" << handles; int requestIndex = -1; for (int i = 0; i < queue->mRequests.size(); ++i) { if (queue->mRequests.at(i).mInterface->service() == respondingService) { requestIndex = i; } } if (requestIndex == -1) { return; } disconnect(iface, SIGNAL(handlesFreeBusy(QString,bool)), this, SLOT(onHandlesFreeBusy(QString,bool))); if (!handles) { queue->mRequests.removeAt(requestIndex); // If no more requests are left and no handler responded // then fall back to the URL mechanism if (queue->mRequests.isEmpty() && queue->mHandlersCount == 0) { mProvidersRequestsByEmail.remove(email); fetchFreeBusyUrl(email); } } else { ++queue->mHandlersCount; connect(iface, SIGNAL(freeBusyRetrieved(QString,QString,bool,QString)), this, SLOT(onFreeBusyRetrieved(QString,QString,bool,QString))); iface->call("retrieveFreeBusy", email, queue->mStartTime, queue->mEndTime); queue->mRequests[requestIndex].mRequestStatus = FreeBusyProviderRequest::FreeBusyRequested; } } void FreeBusyManagerPrivate::processMailSchedulerResult(Akonadi::Scheduler::Result result, const QString &errorMsg) { if (result == Scheduler::ResultSuccess) { KMessageBox::information( mParentWidgetForMailling, i18n("The free/busy information was successfully sent."), i18n("Sending Free/Busy"), "FreeBusyPublishSuccess"); } else { KMessageBox::error(mParentWidgetForMailling, i18n("Unable to publish the free/busy data: %1", errorMsg)); } sender()->deleteLater(); } void FreeBusyManagerPrivate::onFreeBusyRetrieved(const QString &email, const QString &freeBusy, bool success, const QString &errorText) { Q_Q(FreeBusyManager); Q_UNUSED(errorText); if (!mProvidersRequestsByEmail.contains(email)) { return; } QDBusInterface *iface = dynamic_cast(sender()); if (!iface) { return; } FreeBusyProvidersRequestsQueue *queue = &mProvidersRequestsByEmail[email]; QString respondingService = iface->service(); int requestIndex = -1; for (int i = 0; i < queue->mRequests.size(); ++i) { if (queue->mRequests.at(i).mInterface->service() == respondingService) { requestIndex = i; } } if (requestIndex == -1) { return; } disconnect(iface, SIGNAL(freeBusyRetrieved(QString,QString,bool,QString)), this, SLOT(onFreeBusyRetrieved(QString,QString,bool,QString))); queue->mRequests.removeAt(requestIndex); if (success) { KCalCore::FreeBusy::Ptr fb = iCalToFreeBusy(freeBusy.toUtf8()); if (!fb) { --queue->mHandlersCount; } else { queue->mResultingFreeBusy->merge(fb); } } if (queue->mRequests.isEmpty()) { if (queue->mHandlersCount == 0) { fetchFreeBusyUrl(email); } else { emit q->freeBusyRetrieved(queue->mResultingFreeBusy, email); } mProvidersRequestsByEmail.remove(email); } } /// FreeBusyManager::Singleton namespace Akonadi { class FreeBusyManagerStatic { public: FreeBusyManager instance; }; } K_GLOBAL_STATIC(FreeBusyManagerStatic, sManagerInstance) FreeBusyManager::FreeBusyManager() : d_ptr(new FreeBusyManagerPrivate(this)) { setObjectName(QLatin1String("FreeBusyManager")); connect(CalendarSettings::self(), SIGNAL(configChanged()), SLOT(checkFreeBusyUrl())); } FreeBusyManager::~FreeBusyManager() { delete d_ptr; } FreeBusyManager *FreeBusyManager::self() { return &sManagerInstance->instance; } void FreeBusyManager::setCalendar(const Akonadi::ETMCalendar::Ptr &c) { Q_D(FreeBusyManager); if (d->mCalendar) { disconnect(d->mCalendar.data(), SIGNAL(calendarChanged())); } d->mCalendar = c; if (d->mCalendar) { d->mFormat.setTimeSpec(d->mCalendar->timeSpec()); connect(d->mCalendar.data(), SIGNAL(calendarChanged()), SLOT(uploadFreeBusy())); } // Lets see if we need to update our published QTimer::singleShot(0, this, SLOT(uploadFreeBusy())); } /*! This method is called when the user has selected to publish its free/busy list or when the delay have passed. */ void FreeBusyManager::publishFreeBusy(QWidget *parentWidget) { Q_D(FreeBusyManager); // Already uploading? Skip this one then. if (d->mUploadingFreeBusy) { return; } // No calendar set yet? Don't upload to prevent losing published information that // might still be valid. if (!d->mCalendar) { return; } KUrl targetURL(CalendarSettings::self()->freeBusyPublishUrl()); if (targetURL.isEmpty()) { KMessageBox::sorry( parentWidget, i18n("

No URL configured for uploading your free/busy list. " "Please set it in KOrganizer's configuration dialog, on the " "\"Free/Busy\" page.

" "

Contact your system administrator for the exact URL and the " "account details.

"), i18n("No Free/Busy Upload URL")); return; } if (d->mBrokenUrl) { // Url is invalid, don't try again return; } if (!targetURL.isValid()) { KMessageBox::sorry( parentWidget, i18n("The target URL '%1' provided is invalid.", targetURL.prettyUrl()), i18n("Invalid URL")); d->mBrokenUrl = true; return; } targetURL.setUser(CalendarSettings::self()->freeBusyPublishUser()); targetURL.setPass(CalendarSettings::self()->freeBusyPublishPassword()); d->mUploadingFreeBusy = true; // If we have a timer running, it should be stopped now if (d->mTimerID != 0) { killTimer(d->mTimerID); d->mTimerID = 0; } // Save the time of the next free/busy uploading d->mNextUploadTime = QDateTime::currentDateTime(); if (CalendarSettings::self()->freeBusyPublishDelay() > 0) { d->mNextUploadTime = d->mNextUploadTime.addSecs(CalendarSettings::self()->freeBusyPublishDelay() * 60); } QString messageText = d->ownerFreeBusyAsString(); // We need to massage the list a bit so that Outlook understands // it. messageText = messageText.replace(QRegExp(QLatin1String("ORGANIZER\\s*:MAILTO:")), QLatin1String("ORGANIZER:")); // Create a local temp file and save the message to it KTemporaryFile tempFile; tempFile.setAutoRemove(false); if (tempFile.open()) { QTextStream textStream(&tempFile); textStream << messageText; textStream.flush(); #if 0 QString defaultEmail = KOCore()::self()->email(); QString emailHost = defaultEmail.mid(defaultEmail.indexOf('@') + 1); // Put target string together KUrl targetURL; if (CalendarSettings::self()->publishKolab()) { // we use Kolab QString server; if (CalendarSettings::self()->publishKolabServer() == QLatin1String("%SERVER%") || CalendarSettings::self()->publishKolabServer().isEmpty()) { server = emailHost; } else { server = CalendarSettings::self()->publishKolabServer(); } targetURL.setProtocol("webdavs"); targetURL.setHost(server); QString fbname = CalendarSettings::self()->publishUserName(); int at = fbname.indexOf('@'); if (at > 1 && fbname.length() > (uint)at) { fbname = fbname.left(at); } targetURL.setPath("/freebusy/" + fbname + ".ifb"); targetURL.setUser(CalendarSettings::self()->publishUserName()); targetURL.setPass(CalendarSettings::self()->publishPassword()); } else { // we use something else targetURL = CalendarSettings::self()->+publishAnyURL().replace("%SERVER%", emailHost); targetURL.setUser(CalendarSettings::self()->publishUserName()); targetURL.setPass(CalendarSettings::self()->publishPassword()); } #endif KUrl src; src.setPath(tempFile.fileName()); kDebug() << targetURL; KIO::Job *job = KIO::file_copy(src, targetURL, -1, KIO::Overwrite | KIO::HideProgressInfo); job->ui()->setWindow(parentWidget); //FIXME slot doesn't exist //connect(job, SIGNAL(result(KJob*)), SLOT(slotUploadFreeBusyResult(KJob*))); } } void FreeBusyManager::mailFreeBusy(int daysToPublish, QWidget *parentWidget) { Q_D(FreeBusyManager); // No calendar set yet? if (!d->mCalendar) { return; } KDateTime start = KDateTime::currentUtcDateTime().toTimeSpec(d->mCalendar->timeSpec()); KDateTime end = start.addDays(daysToPublish); KCalCore::Event::List events = d->mCalendar->rawEvents(start.date(), end.date()); FreeBusy::Ptr freebusy(new FreeBusy(events, start, end)); freebusy->setOrganizer(Person::Ptr( new Person(Akonadi::CalendarUtils::fullName(), Akonadi::CalendarUtils::email()))); QPointer publishdlg = new PublishDialog(); if (publishdlg->exec() == QDialog::Accepted) { // Send the mail MailScheduler *scheduler = new MailScheduler(); connect(scheduler, SIGNAL(transactionFinished(Akonadi::Scheduler::Result,QString)) , d, SLOT(processMailSchedulerResult(Akonadi::Scheduler::Result,QString))); d->mParentWidgetForMailling = parentWidget; scheduler->publish(freebusy, publishdlg->addresses()); } delete publishdlg; } bool FreeBusyManager::retrieveFreeBusy(const QString &email, bool forceDownload, QWidget *parentWidget) { Q_D(FreeBusyManager); kDebug() << email; if (email.isEmpty()) { kDebug() << "Email is empty"; return false; } d->mParentWidgetForRetrieval = parentWidget; if (Akonadi::CalendarUtils::thatIsMe(email)) { // Don't download our own free-busy list from the net kDebug() << "freebusy of owner, not downloading"; emit freeBusyRetrieved(d->ownerFreeBusy(), email); return true; } // Check for cached copy of free/busy list KCalCore::FreeBusy::Ptr fb = loadFreeBusy(email); if (fb) { kDebug() << "Found a cached copy for " << email; emit freeBusyRetrieved(fb, email); return true; } // Don't download free/busy if the user does not want it. if (!CalendarSettings::self()->freeBusyRetrieveAuto() && !forceDownload) { kDebug() << "Not downloading freebusy"; return false; } d->mRetrieveQueue.append(email); if (d->mRetrieveQueue.count() > 1) { // TODO: true should always emit kWarning() << "Returning true without emit, is this correct?"; return true; } // queued, because "true" means the download was initiated. So lets // return before starting stuff QMetaObject::invokeMethod(d, "processRetrieveQueue", Qt::QueuedConnection); return true; } void FreeBusyManager::cancelRetrieval() { Q_D(FreeBusyManager); d->mRetrieveQueue.clear(); } KCalCore::FreeBusy::Ptr FreeBusyManager::loadFreeBusy(const QString &email) { Q_D(FreeBusyManager); const QString fbd = d->freeBusyDir(); QFile f(fbd + QLatin1Char('/') + email + QLatin1String(".ifb")); if (!f.exists()) { kDebug() << f.fileName() << "doesn't exist."; return KCalCore::FreeBusy::Ptr(); } if (!f.open(QIODevice::ReadOnly)) { kDebug() << "Unable to open file" << f.fileName(); return KCalCore::FreeBusy::Ptr(); } QTextStream ts(&f); QString str = ts.readAll(); return d->iCalToFreeBusy(str.toUtf8()); } bool FreeBusyManager::saveFreeBusy(const KCalCore::FreeBusy::Ptr &freebusy, const KCalCore::Person::Ptr &person) { Q_D(FreeBusyManager); Q_ASSERT(person); kDebug() << person->fullName(); QString fbd = d->freeBusyDir(); QDir freeBusyDirectory(fbd); if (!freeBusyDirectory.exists()) { kDebug() << "Directory" << fbd <<" does not exist!"; kDebug() << "Creating directory:" << fbd; if (!freeBusyDirectory.mkpath(fbd)) { kDebug() << "Could not create directory:" << fbd; return false; } } QString filename(fbd); filename += QLatin1Char('/'); filename += person->email(); filename += QLatin1String(".ifb"); QFile f(filename); kDebug() << "filename:" << filename; freebusy->clearAttendees(); freebusy->setOrganizer(person); QString messageText = d->mFormat.createScheduleMessage(freebusy, KCalCore::iTIPPublish); if (!f.open(QIODevice::ReadWrite)) { kDebug() << "acceptFreeBusy: Can't open:" << filename << "for writing"; return false; } QTextStream t(&f); t << messageText; f.close(); return true; } void FreeBusyManager::timerEvent(QTimerEvent *) { publishFreeBusy(); } #include "moc_freebusymanager.cpp" #include "moc_freebusymanager_p.cpp"