File indexing completed on 2024-12-22 04:56:57
0001 /* 0002 SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "setupwizard.h" 0008 0009 #include <KAuthorized> 0010 #include <KDAV/DavCollectionsMultiFetchJob> 0011 #include <KDesktopFile> 0012 #include <KFileUtils> 0013 #include <KLocalizedString> 0014 #include <KPasswordLineEdit> 0015 #include <KService> 0016 #include <QIcon> 0017 #include <QLineEdit> 0018 #include <QTextBrowser> 0019 0020 #include <QButtonGroup> 0021 #include <QCheckBox> 0022 #include <QComboBox> 0023 #include <QFormLayout> 0024 #include <QHBoxLayout> 0025 #include <QLabel> 0026 #include <QPushButton> 0027 #include <QRadioButton> 0028 #include <QRegularExpressionValidator> 0029 #include <QStandardPaths> 0030 #include <QUrl> 0031 0032 enum GroupwareServers { 0033 Citadel, 0034 DAVical, 0035 eGroupware, 0036 OpenGroupware, 0037 ScalableOGo, 0038 Scalix, 0039 Zarafa, 0040 Zimbra, 0041 }; 0042 0043 static QString settingsToUrl(const QWizard *wizard, const QString &protocol) 0044 { 0045 const QString desktopFilePath = wizard->property("providerDesktopFilePath").toString(); 0046 if (desktopFilePath.isEmpty()) { 0047 return {}; 0048 } 0049 0050 KService::Ptr service = KService::serviceByStorageId(desktopFilePath); 0051 if (!service) { 0052 return {}; 0053 } 0054 0055 const QStringList supportedProtocols = service->property<QStringList>(QStringLiteral("X-DavGroupware-SupportedProtocols")); 0056 if (!supportedProtocols.contains(protocol)) { 0057 return {}; 0058 } 0059 0060 const QString pathPropertyName(QStringLiteral("X-DavGroupware-") + protocol + QStringLiteral("Path")); 0061 if (service->property<QString>(pathPropertyName).isEmpty()) { 0062 return {}; 0063 } 0064 0065 QString pathPattern = service->property<QString>(pathPropertyName) + QLatin1Char('/'); 0066 0067 const QString username = wizard->field(QStringLiteral("credentialsUserName")).toString(); 0068 QString localPart(username); 0069 localPart.remove(QRegularExpression(QStringLiteral("@.*$"))); 0070 pathPattern.replace(QLatin1StringView("$user$"), username); 0071 pathPattern.replace(QLatin1StringView("$localpart$"), localPart); 0072 QString providerName = service->property<QString>(QStringLiteral("X-DavGroupware-Provider")); 0073 const QString localPath = wizard->field(QStringLiteral("installationPath")).toString(); 0074 if (!localPath.isEmpty()) { 0075 if (providerName == QLatin1StringView("davical")) { 0076 if (!localPath.endsWith(QLatin1Char('/'))) { 0077 pathPattern.append(localPath + QLatin1Char('/')); 0078 } else { 0079 pathPattern.append(localPath); 0080 } 0081 } else { 0082 if (!localPath.startsWith(QLatin1Char('/'))) { 0083 pathPattern.prepend(QLatin1Char('/') + localPath); 0084 } else { 0085 pathPattern.prepend(localPath); 0086 } 0087 } 0088 } 0089 QUrl url; 0090 0091 if (!wizard->property("usePredefinedProvider").isNull()) { 0092 if (service->property<bool>(QStringLiteral("X-DavGroupware-ProviderUsesSSL"))) { 0093 url.setScheme(QStringLiteral("https")); 0094 } else { 0095 url.setScheme(QStringLiteral("http")); 0096 } 0097 0098 const QString hostPropertyName(QStringLiteral("X-DavGroupware-") + protocol + QStringLiteral("Host")); 0099 if (service->property<QString>(hostPropertyName).isEmpty()) { 0100 return {}; 0101 } 0102 0103 url.setHost(service->property<QString>(hostPropertyName)); 0104 url.setPath(pathPattern); 0105 } else { 0106 if (wizard->field(QStringLiteral("connectionUseSecureConnection")).toBool()) { 0107 url.setScheme(QStringLiteral("https")); 0108 } else { 0109 url.setScheme(QStringLiteral("http")); 0110 } 0111 0112 const QString host = wizard->field(QStringLiteral("connectionHost")).toString(); 0113 if (host.isEmpty()) { 0114 return {}; 0115 } 0116 const QStringList hostParts = host.split(QLatin1Char(':')); 0117 url.setHost(hostParts.at(0)); 0118 url.setPath(pathPattern); 0119 0120 if (hostParts.size() == 2) { 0121 int port = hostParts.at(1).toInt(); 0122 if (port) { 0123 url.setPort(port); 0124 } 0125 } 0126 } 0127 return url.toString(); 0128 } 0129 0130 /* 0131 * SetupWizard 0132 */ 0133 0134 SetupWizard::SetupWizard(QWidget *parent) 0135 : QWizard(parent) 0136 { 0137 setWindowTitle(i18nc("@title:window", "DAV groupware configuration wizard")); 0138 setWindowIcon(QIcon::fromTheme(QStringLiteral("folder-remote"))); 0139 setPage(W_CredentialsPage, new CredentialsPage); 0140 setPage(W_PredefinedProviderPage, new PredefinedProviderPage); 0141 setPage(W_ServerTypePage, new ServerTypePage); 0142 setPage(W_ConnectionPage, new ConnectionPage); 0143 setPage(W_CheckPage, new CheckPage); 0144 } 0145 0146 QString SetupWizard::displayName() const 0147 { 0148 const QString desktopFilePath = property("providerDesktopFilePath").toString(); 0149 if (desktopFilePath.isEmpty()) { 0150 return {}; 0151 } 0152 0153 KService::Ptr service = KService::serviceByStorageId(desktopFilePath); 0154 if (!service) { 0155 return {}; 0156 } 0157 0158 return service->name(); 0159 } 0160 0161 SetupWizard::Url::List SetupWizard::urls() const 0162 { 0163 Url::List urls; 0164 0165 const QString desktopFilePath = property("providerDesktopFilePath").toString(); 0166 if (desktopFilePath.isEmpty()) { 0167 return urls; 0168 } 0169 0170 KService::Ptr service = KService::serviceByStorageId(desktopFilePath); 0171 if (!service) { 0172 return urls; 0173 } 0174 0175 const QStringList supportedProtocols = service->property<QStringList>(QStringLiteral("X-DavGroupware-SupportedProtocols")); 0176 for (const QString &protocol : supportedProtocols) { 0177 Url url; 0178 0179 if (protocol == QLatin1StringView("CalDav")) { 0180 url.protocol = KDAV::CalDav; 0181 } else if (protocol == QLatin1StringView("CardDav")) { 0182 url.protocol = KDAV::CardDav; 0183 } else if (protocol == QLatin1StringView("GroupDav")) { 0184 url.protocol = KDAV::GroupDav; 0185 } else { 0186 return urls; 0187 } 0188 0189 const QString urlStr = settingsToUrl(this, protocol); 0190 0191 if (!urlStr.isEmpty()) { 0192 url.url = urlStr; 0193 url.userName = QStringLiteral("$default$"); 0194 urls << url; 0195 } 0196 } 0197 0198 return urls; 0199 } 0200 0201 /* 0202 * CredentialsPage 0203 */ 0204 0205 CredentialsPage::CredentialsPage(QWidget *parent) 0206 : QWizardPage(parent) 0207 , mUserName(new QLineEdit(this)) 0208 , mPassword(new KPasswordLineEdit(this)) 0209 { 0210 setTitle(i18n("Login Credentials")); 0211 setSubTitle(i18n("Enter your credentials to login to the groupware server")); 0212 0213 auto layout = new QFormLayout(this); 0214 0215 layout->addRow(i18n("User:"), mUserName); 0216 registerField(QStringLiteral("credentialsUserName*"), mUserName); 0217 0218 mPassword->setRevealPasswordAvailable(KAuthorized::authorize(QStringLiteral("lineedit_reveal_password"))); 0219 layout->addRow(i18n("Password:"), mPassword); 0220 registerField(QStringLiteral("credentialsPassword*"), mPassword, "password", SIGNAL(passwordChanged(QString))); 0221 } 0222 0223 int CredentialsPage::nextId() const 0224 { 0225 QString userName = field(QStringLiteral("credentialsUserName")).toString(); 0226 if (userName.endsWith(QLatin1StringView("@yahoo.com"))) { 0227 const QString maybeYahooFile = 0228 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("akonadi/davgroupware-providers/yahoo.desktop")); 0229 0230 if (maybeYahooFile.isEmpty()) { 0231 return SetupWizard::W_ServerTypePage; 0232 } 0233 0234 const KDesktopFile yahooProvider(maybeYahooFile); 0235 0236 wizard()->setProperty("usePredefinedProvider", true); 0237 wizard()->setProperty("predefinedProviderName", yahooProvider.readName()); 0238 wizard()->setProperty("providerDesktopFilePath", maybeYahooFile); 0239 return SetupWizard::W_PredefinedProviderPage; 0240 } else { 0241 return SetupWizard::W_ServerTypePage; 0242 } 0243 } 0244 0245 /* 0246 * PredefinedProviderPage 0247 */ 0248 0249 PredefinedProviderPage::PredefinedProviderPage(QWidget *parent) 0250 : QWizardPage(parent) 0251 , mLabel(new QLabel(this)) 0252 , mProviderGroup(new QButtonGroup(this)) 0253 , mUseProvider(new QRadioButton(this)) 0254 , mDontUseProvider(new QRadioButton(i18n("No, choose another server"), this)) 0255 { 0256 setTitle(i18n("Predefined provider found")); 0257 setSubTitle(i18n("Select if you want to use the auto-detected provider")); 0258 0259 auto layout = new QVBoxLayout(this); 0260 0261 layout->addWidget(mLabel); 0262 0263 mProviderGroup->setExclusive(true); 0264 0265 mProviderGroup->addButton(mUseProvider); 0266 mUseProvider->setChecked(true); 0267 layout->addWidget(mUseProvider); 0268 0269 mProviderGroup->addButton(mDontUseProvider); 0270 layout->addWidget(mDontUseProvider); 0271 } 0272 0273 void PredefinedProviderPage::initializePage() 0274 { 0275 mLabel->setText( 0276 i18n("Based on the email address you used as a login, this wizard\n" 0277 "can configure automatically an account for %1 services.\n" 0278 "Do you wish to do so?", 0279 wizard()->property("predefinedProviderName").toString())); 0280 0281 mUseProvider->setText(i18n("Yes, use %1 as provider", wizard()->property("predefinedProviderName").toString())); 0282 } 0283 0284 int PredefinedProviderPage::nextId() const 0285 { 0286 if (mUseProvider->isChecked()) { 0287 return SetupWizard::W_CheckPage; 0288 } else { 0289 wizard()->setProperty("usePredefinedProvider", QVariant()); 0290 wizard()->setProperty("providerDesktopFilePath", QVariant()); 0291 return SetupWizard::W_ServerTypePage; 0292 } 0293 } 0294 0295 /* 0296 * ServerTypePage 0297 */ 0298 0299 bool compareServiceOffers(const QPair<QString, QString> &off1, const QPair<QString, QString> &off2) 0300 { 0301 return off1.first.toLower() < off2.first.toLower(); 0302 } 0303 0304 ServerTypePage::ServerTypePage(QWidget *parent) 0305 : QWizardPage(parent) 0306 { 0307 setTitle(i18n("Groupware Server")); 0308 setSubTitle(i18n("Select the groupware server the resource shall be configured for")); 0309 0310 mProvidersCombo = new QComboBox(this); 0311 mProvidersCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0312 0313 const QStringList dirs = 0314 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("akonadi/davgroupware-providers"), QStandardPaths::LocateDirectory); 0315 const QStringList providers = KFileUtils::findAllUniqueFiles(dirs, QStringList{QStringLiteral("*.desktop")}); 0316 0317 QList<QPair<QString, QString>> offers; 0318 offers.reserve(providers.count()); 0319 for (const QString &fileName : providers) { 0320 const KDesktopFile provider(fileName); 0321 offers.append(QPair<QString, QString>(provider.readName(), fileName)); 0322 } 0323 std::sort(offers.begin(), offers.end(), compareServiceOffers); 0324 QListIterator<QPair<QString, QString>> it(offers); 0325 while (it.hasNext()) { 0326 QPair<QString, QString> p = it.next(); 0327 mProvidersCombo->addItem(p.first, p.second); 0328 } 0329 registerField(QStringLiteral("provider"), mProvidersCombo, "currentText"); 0330 0331 auto layout = new QVBoxLayout(this); 0332 0333 mServerGroup = new QButtonGroup(this); 0334 mServerGroup->setExclusive(true); 0335 0336 auto hLayout = new QHBoxLayout; 0337 auto button = new QRadioButton(i18n("Use one of those servers:"), this); 0338 registerField(QStringLiteral("templateConfiguration"), button); 0339 mServerGroup->addButton(button); 0340 mServerGroup->setId(button, 0); 0341 button->setChecked(true); 0342 hLayout->addWidget(button); 0343 hLayout->addWidget(mProvidersCombo); 0344 layout->addLayout(hLayout); 0345 0346 button = new QRadioButton(i18n("Configure the resource manually"), this); 0347 connect(button, &QRadioButton::toggled, this, &ServerTypePage::manualConfigToggled); 0348 registerField(QStringLiteral("manualConfiguration"), button); 0349 mServerGroup->addButton(button); 0350 mServerGroup->setId(button, 1); 0351 layout->addWidget(button); 0352 0353 layout->addStretch(1); 0354 } 0355 0356 void ServerTypePage::manualConfigToggled(bool state) 0357 { 0358 setFinalPage(state); 0359 wizard()->button(QWizard::NextButton)->setEnabled(!state); 0360 } 0361 0362 bool ServerTypePage::validatePage() 0363 { 0364 QVariant desktopFilePath = mProvidersCombo->itemData(mProvidersCombo->currentIndex()); 0365 if (desktopFilePath.isNull()) { 0366 return false; 0367 } else { 0368 wizard()->setProperty("providerDesktopFilePath", desktopFilePath); 0369 return true; 0370 } 0371 } 0372 0373 /* 0374 * ConnectionPage 0375 */ 0376 0377 ConnectionPage::ConnectionPage(QWidget *parent) 0378 : QWizardPage(parent) 0379 { 0380 setTitle(i18n("Connection")); 0381 setSubTitle(i18n("Enter the connection information for the groupware server")); 0382 0383 mLayout = new QFormLayout(this); 0384 const QRegularExpression hostnameRegexp(QStringLiteral("^[a-z0-9][.a-z0-9-]*[a-z0-9](?::[0-9]+)?$")); 0385 mHost = new QLineEdit(this); 0386 registerField(QStringLiteral("connectionHost*"), mHost); 0387 mHost->setValidator(new QRegularExpressionValidator(hostnameRegexp, this)); 0388 mLayout->addRow(i18n("Host"), mHost); 0389 0390 mPath = new QLineEdit(this); 0391 mLayout->addRow(i18n("Installation path"), mPath); 0392 registerField(QStringLiteral("installationPath"), mPath); 0393 0394 mUseSecureConnection = new QCheckBox(i18n("Use secure connection")); 0395 mUseSecureConnection->setChecked(true); 0396 registerField(QStringLiteral("connectionUseSecureConnection"), mUseSecureConnection); 0397 mLayout->addRow(QString(), mUseSecureConnection); 0398 0399 connect(mHost, &QLineEdit::textChanged, this, &ConnectionPage::urlElementChanged); 0400 connect(mPath, &QLineEdit::textChanged, this, &ConnectionPage::urlElementChanged); 0401 connect(mUseSecureConnection, &QCheckBox::toggled, this, &ConnectionPage::urlElementChanged); 0402 } 0403 0404 void ConnectionPage::initializePage() 0405 { 0406 KService::Ptr service = KService::serviceByStorageId(wizard()->property("providerDesktopFilePath").toString()); 0407 if (!service) { 0408 return; 0409 } 0410 0411 const QString providerInstallationPath = service->property<QString>(QStringLiteral("X-DavGroupware-InstallationPath")); 0412 if (!providerInstallationPath.isEmpty()) { 0413 mPath->setText(providerInstallationPath); 0414 } 0415 0416 const QStringList supportedProtocols = service->property<QStringList>(QStringLiteral("X-DavGroupware-SupportedProtocols")); 0417 0418 mPreviewLayout = new QFormLayout; 0419 mLayout->addRow(mPreviewLayout); 0420 0421 if (supportedProtocols.contains(QLatin1StringView("CalDav"))) { 0422 mCalDavUrlLabel = new QLabel(i18n("Final URL (CalDav)")); 0423 mCalDavUrlPreview = new QLabel; 0424 mPreviewLayout->addRow(mCalDavUrlLabel, mCalDavUrlPreview); 0425 } 0426 if (supportedProtocols.contains(QLatin1StringView("CardDav"))) { 0427 mCardDavUrlLabel = new QLabel(i18n("Final URL (CardDav)")); 0428 mCardDavUrlPreview = new QLabel; 0429 mPreviewLayout->addRow(mCardDavUrlLabel, mCardDavUrlPreview); 0430 } 0431 if (supportedProtocols.contains(QLatin1StringView("GroupDav"))) { 0432 mGroupDavUrlLabel = new QLabel(i18n("Final URL (GroupDav)")); 0433 mGroupDavUrlPreview = new QLabel; 0434 mPreviewLayout->addRow(mGroupDavUrlLabel, mGroupDavUrlPreview); 0435 } 0436 } 0437 0438 void ConnectionPage::cleanupPage() 0439 { 0440 delete mPreviewLayout; 0441 0442 if (mCalDavUrlPreview) { 0443 delete mCalDavUrlLabel; 0444 delete mCalDavUrlPreview; 0445 mCalDavUrlPreview = nullptr; 0446 } 0447 0448 if (mCardDavUrlPreview) { 0449 delete mCardDavUrlLabel; 0450 delete mCardDavUrlPreview; 0451 mCardDavUrlPreview = nullptr; 0452 } 0453 0454 if (mGroupDavUrlPreview) { 0455 delete mGroupDavUrlLabel; 0456 delete mGroupDavUrlPreview; 0457 mGroupDavUrlPreview = nullptr; 0458 } 0459 0460 QWizardPage::cleanupPage(); 0461 } 0462 0463 void ConnectionPage::urlElementChanged() 0464 { 0465 if (mHost->text().isEmpty()) { 0466 if (mCalDavUrlPreview) { 0467 mCalDavUrlPreview->setText(QStringLiteral("-")); 0468 } 0469 if (mCardDavUrlPreview) { 0470 mCardDavUrlPreview->setText(QStringLiteral("-")); 0471 } 0472 if (mGroupDavUrlPreview) { 0473 mGroupDavUrlPreview->setText(QStringLiteral("-")); 0474 } 0475 } else { 0476 if (mCalDavUrlPreview) { 0477 mCalDavUrlPreview->setText(settingsToUrl(this->wizard(), QStringLiteral("CalDav"))); 0478 } 0479 if (mCardDavUrlPreview) { 0480 mCardDavUrlPreview->setText(settingsToUrl(this->wizard(), QStringLiteral("CardDav"))); 0481 } 0482 if (mGroupDavUrlPreview) { 0483 mGroupDavUrlPreview->setText(settingsToUrl(this->wizard(), QStringLiteral("GroupDav"))); 0484 } 0485 } 0486 } 0487 0488 /* 0489 * CheckPage 0490 */ 0491 0492 CheckPage::CheckPage(QWidget *parent) 0493 : QWizardPage(parent) 0494 , mStatusLabel(new QTextBrowser(this)) 0495 { 0496 setTitle(i18n("Test Connection")); 0497 setSubTitle(i18n("You can test now whether the groupware server can be accessed with the current configuration")); 0498 setFinalPage(true); 0499 0500 auto layout = new QVBoxLayout(this); 0501 0502 auto button = new QPushButton(i18n("Test Connection"), this); 0503 layout->addWidget(button); 0504 0505 layout->addWidget(mStatusLabel); 0506 0507 connect(button, &QRadioButton::clicked, this, &CheckPage::checkConnection); 0508 } 0509 0510 void CheckPage::checkConnection() 0511 { 0512 mStatusLabel->clear(); 0513 0514 KDAV::DavUrl::List davUrls; 0515 0516 // convert list of SetupWizard::Url to list of KDAV::DavUrl 0517 const SetupWizard::Url::List urls = static_cast<SetupWizard *>(wizard())->urls(); 0518 for (const SetupWizard::Url &url : urls) { 0519 KDAV::DavUrl davUrl; 0520 davUrl.setProtocol(url.protocol); 0521 0522 QUrl serverUrl(url.url); 0523 serverUrl.setUserName(wizard()->field(QStringLiteral("credentialsUserName")).toString()); 0524 serverUrl.setPassword(wizard()->field(QStringLiteral("credentialsPassword")).toString()); 0525 davUrl.setUrl(serverUrl); 0526 0527 davUrls << davUrl; 0528 } 0529 0530 // start the dav collections fetch job to test connectivity 0531 auto job = new KDAV::DavCollectionsMultiFetchJob(davUrls, this); 0532 connect(job, &KDAV::DavCollectionsMultiFetchJob::result, this, &CheckPage::onFetchDone); 0533 job->start(); 0534 } 0535 0536 void CheckPage::onFetchDone(KJob *job) 0537 { 0538 QString msg; 0539 QPixmap icon; 0540 0541 if (job->error()) { 0542 msg = i18n("An error occurred: %1", job->errorText()); 0543 icon = QIcon::fromTheme(QStringLiteral("dialog-close")).pixmap(16, 16); 0544 } else { 0545 msg = i18n("Connected successfully"); 0546 icon = QIcon::fromTheme(QStringLiteral("dialog-ok-apply")).pixmap(16, 16); 0547 } 0548 0549 mStatusLabel->setHtml(QStringLiteral("<html><body><img src=\"icon\"> %1</body></html>").arg(msg)); 0550 mStatusLabel->document()->addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("icon")), QVariant(icon)); 0551 } 0552 0553 #include "moc_setupwizard.cpp"