00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "service.h"
00021 #include "private/authorizationmanager_p.h"
00022 #include "private/service_p.h"
00023 #include "private/serviceprovider_p.h"
00024
00025 #include "config-plasma.h"
00026
00027 #include <QFile>
00028 #include <QTimer>
00029
00030 #include <kdebug.h>
00031 #include <kservice.h>
00032 #include <kservicetypetrader.h>
00033 #include <ksharedconfig.h>
00034 #include <kstandarddirs.h>
00035 #include <ktemporaryfile.h>
00036 #include <dnssd/publicservice.h>
00037 #include <dnssd/servicebrowser.h>
00038
00039 #include "configloader.h"
00040 #include "version.h"
00041 #include "private/configloader_p.h"
00042 #include "private/remoteservice_p.h"
00043 #include "private/remoteservicejob_p.h"
00044
00045 namespace Plasma
00046 {
00047
00048 Service::Service(QObject *parent)
00049 : QObject(parent),
00050 d(new ServicePrivate(this))
00051 {
00052 }
00053
00054 Service::Service(QObject *parent, const QVariantList &args)
00055 : QObject(parent),
00056 d(new ServicePrivate(this))
00057 {
00058 Q_UNUSED(args)
00059 }
00060
00061 Service::~Service()
00062 {
00063 d->unpublish();
00064 delete d;
00065 }
00066
00067 Service *Service::load(const QString &name, QObject *parent)
00068 {
00069
00070 if (name.isEmpty()) {
00071 return new NullService(QString(), parent);
00072 }
00073
00074 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(name);
00075 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Service", constraint);
00076
00077 if (offers.isEmpty()) {
00078 kDebug() << "offers is empty for " << name;
00079 return new NullService(name, parent);
00080 }
00081
00082 KService::Ptr offer = offers.first();
00083 QString error;
00084 QVariantList args;
00085
00086 Service *service = 0;
00087
00088 if (Plasma::isPluginVersionCompatible(KPluginLoader(*offer).pluginVersion())) {
00089 service = offer->createInstance<Plasma::Service>(parent, args, &error);
00090 }
00091
00092 if (!service) {
00093 kDebug() << "Couldn't load Service \"" << name << "\"! reason given: " << error;
00094 return new NullService(name, parent);
00095 }
00096
00097 if (service->name().isEmpty()) {
00098 service->setName(name);
00099 }
00100
00101 return service;
00102 }
00103
00104 Service *Service::access(const KUrl &url, QObject *parent)
00105 {
00106 return new RemoteService(parent, url);
00107 }
00108
00109 void ServicePrivate::jobFinished(KJob *job)
00110 {
00111 emit q->finished(static_cast<ServiceJob*>(job));
00112 }
00113
00114 void ServicePrivate::associatedWidgetDestroyed(QObject *obj)
00115 {
00116 associatedWidgets.remove(static_cast<QWidget*>(obj));
00117 }
00118
00119 void ServicePrivate::associatedGraphicsWidgetDestroyed(QObject *obj)
00120 {
00121 associatedGraphicsWidgets.remove(static_cast<QGraphicsWidget*>(obj));
00122 }
00123
00124 void ServicePrivate::publish(AnnouncementMethods methods, const QString &name, PackageMetadata metadata)
00125 {
00126 #ifdef ENABLE_REMOTE_WIDGETS
00127 if (!serviceProvider) {
00128 AuthorizationManager::self()->d->prepareForServicePublication();
00129
00130 serviceProvider = new ServiceProvider(name, q);
00131
00132 if (methods.testFlag(ZeroconfAnnouncement) &&
00133 (DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working)) {
00134
00135 publicService = new DNSSD::PublicService(name, "_plasma._tcp", 4000);
00136
00137 QMap<QString, QByteArray> textData;
00138 textData["name"] = name.toUtf8();
00139 textData["plasmoidname"] = metadata.name().toUtf8();
00140 textData["description"] = metadata.description().toUtf8();
00141 publicService->setTextData(textData);
00142 kDebug() << "about to publish";
00143
00144 publicService->publishAsync();
00145 } else if (methods.testFlag(ZeroconfAnnouncement) &&
00146 (DNSSD::ServiceBrowser::isAvailable() != DNSSD::ServiceBrowser::Working)) {
00147 kDebug() << "sorry, but your zeroconf daemon doesn't seem to be running.";
00148 }
00149 } else {
00150 kDebug() << "already published!";
00151 }
00152 #else
00153 kWarning() << "libplasma is compiled without support for remote widgets. not publishing.";
00154 #endif
00155 }
00156
00157 void ServicePrivate::unpublish()
00158 {
00159 if (serviceProvider) {
00160 delete serviceProvider;
00161 serviceProvider = 0;
00162 }
00163
00164 if (publicService) {
00165 delete publicService;
00166 publicService = 0;
00167 }
00168 }
00169
00170 bool ServicePrivate::isPublished() const
00171 {
00172 if (serviceProvider) {
00173 return true;
00174 } else {
00175 return false;
00176 }
00177 }
00178
00179 KConfigGroup ServicePrivate::dummyGroup()
00180 {
00181 if (!dummyConfig) {
00182 if (!tempFile) {
00183 tempFile = new KTemporaryFile;
00184 tempFile->open();
00185 }
00186
00187 dummyConfig = new KConfig(tempFile->fileName());
00188 }
00189
00190 return KConfigGroup(dummyConfig, "DummyGroup");
00191 }
00192
00193 void Service::setDestination(const QString &destination)
00194 {
00195 d->destination = destination;
00196 }
00197
00198 QString Service::destination() const
00199 {
00200 return d->destination;
00201 }
00202
00203 QStringList Service::operationNames() const
00204 {
00205 if (!d->config) {
00206 kDebug() << "No valid operations scheme has been registered";
00207 return QStringList();
00208 }
00209
00210 return d->config->groupList();
00211 }
00212
00213 KConfigGroup Service::operationDescription(const QString &operationName)
00214 {
00215 if (!d->config) {
00216 kDebug() << "No valid operations scheme has been registered";
00217 return d->dummyGroup();
00218 }
00219
00220 d->config->writeConfig();
00221 KConfigGroup params(d->config->config(), operationName);
00222
00223
00224
00225 return params;
00226 }
00227
00228 QMap<QString, QVariant> Service::parametersFromDescription(const KConfigGroup &description)
00229 {
00230 QMap<QString, QVariant> params;
00231
00232 if (!d->config || !description.isValid()) {
00233 return params;
00234 }
00235
00236 const QString op = description.name();
00237 foreach (const QString &key, description.keyList()) {
00238 KConfigSkeletonItem *item = d->config->findItem(op, key);
00239 if (item) {
00240 params.insert(key, description.readEntry(key, item->property()));
00241 }
00242 }
00243
00244 return params;
00245 }
00246
00247 ServiceJob *Service::startOperationCall(const KConfigGroup &description, QObject *parent)
00248 {
00249
00250 ServiceJob *job = 0;
00251 const QString op = description.isValid() ? description.name() : QString();
00252
00253 RemoteService *rs = qobject_cast<RemoteService *>(this);
00254 if (!op.isEmpty() && rs && !rs->isReady()) {
00255
00256 kDebug() << "Remote service is not ready; queueing operation";
00257 QMap<QString, QVariant> params;
00258 job = createJob(op, params);
00259 RemoteServiceJob *rsj = qobject_cast<RemoteServiceJob *>(job);
00260 if (rsj) {
00261 rsj->setDelayedDescription(description);
00262 }
00263 } else if (!d->config) {
00264 kDebug() << "No valid operations scheme has been registered";
00265 } else if (!op.isEmpty() && d->config->hasGroup(op)) {
00266 if (d->disabledOperations.contains(op)) {
00267 kDebug() << "Operation" << op << "is disabled";
00268 } else {
00269 QMap<QString, QVariant> params = parametersFromDescription(description);
00270 job = createJob(op, params);
00271 }
00272 } else {
00273 kDebug() << "Not a valid group!";
00274 }
00275
00276 if (!job) {
00277 job = new NullServiceJob(destination(), op, this);
00278 }
00279
00280 job->setParent(parent ? parent : this);
00281 connect(job, SIGNAL(finished(KJob*)), this, SLOT(jobFinished(KJob*)));
00282 QTimer::singleShot(0, job, SLOT(slotStart()));
00283 return job;
00284 }
00285
00286 void Service::associateWidget(QWidget *widget, const QString &operation)
00287 {
00288 disassociateWidget(widget);
00289 d->associatedWidgets.insert(widget, operation);
00290 connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(associatedWidgetDestroyed(QObject*)));
00291
00292 widget->setEnabled(!d->disabledOperations.contains(operation));
00293 }
00294
00295 void Service::disassociateWidget(QWidget *widget)
00296 {
00297 disconnect(widget, SIGNAL(destroyed(QObject*)),
00298 this, SLOT(associatedWidgetDestroyed(QObject*)));
00299 d->associatedWidgets.remove(widget);
00300 }
00301
00302 void Service::associateWidget(QGraphicsWidget *widget, const QString &operation)
00303 {
00304 disassociateWidget(widget);
00305 d->associatedGraphicsWidgets.insert(widget, operation);
00306 connect(widget, SIGNAL(destroyed(QObject*)),
00307 this, SLOT(associatedGraphicsWidgetDestroyed(QObject*)));
00308
00309 widget->setEnabled(!d->disabledOperations.contains(operation));
00310 }
00311
00312 void Service::disassociateWidget(QGraphicsWidget *widget)
00313 {
00314 disconnect(widget, SIGNAL(destroyed(QObject*)),
00315 this, SLOT(associatedGraphicsWidgetDestroyed(QObject*)));
00316 d->associatedGraphicsWidgets.remove(widget);
00317 }
00318
00319 QString Service::name() const
00320 {
00321 return d->name;
00322 }
00323
00324 void Service::setName(const QString &name)
00325 {
00326 d->name = name;
00327
00328
00329 delete d->config;
00330 d->config = 0;
00331
00332 delete d->tempFile;
00333 d->tempFile = 0;
00334
00335 delete d->dummyConfig;
00336 d->dummyConfig = 0;
00337
00338 registerOperationsScheme();
00339
00340 emit serviceReady(this);
00341 }
00342
00343 void Service::setOperationEnabled(const QString &operation, bool enable)
00344 {
00345 if (!d->config || !d->config->hasGroup(operation)) {
00346 return;
00347 }
00348
00349 if (enable) {
00350 d->disabledOperations.remove(operation);
00351 } else {
00352 d->disabledOperations.insert(operation);
00353 }
00354
00355 {
00356 QHashIterator<QWidget *, QString> it(d->associatedWidgets);
00357 while (it.hasNext()) {
00358 it.next();
00359 if (it.value() == operation) {
00360 it.key()->setEnabled(enable);
00361 }
00362 }
00363 }
00364
00365 {
00366 QHashIterator<QGraphicsWidget *, QString> it(d->associatedGraphicsWidgets);
00367 while (it.hasNext()) {
00368 it.next();
00369 if (it.value() == operation) {
00370 it.key()->setEnabled(enable);
00371 }
00372 }
00373 }
00374 }
00375
00376 bool Service::isOperationEnabled(const QString &operation) const
00377 {
00378 return d->config && d->config->hasGroup(operation) && !d->disabledOperations.contains(operation);
00379 }
00380
00381 void Service::setOperationsScheme(QIODevice *xml)
00382 {
00383 delete d->config;
00384 delete d->tempFile;
00385
00386 delete d->dummyConfig;
00387 d->dummyConfig = 0;
00388
00389
00390
00391 d->tempFile = new KTemporaryFile;
00392 d->tempFile->open();
00393
00394 KSharedConfigPtr c = KSharedConfig::openConfig(d->tempFile->fileName(), KConfig::NoGlobals);
00395 d->config = new ConfigLoader(c, xml, this);
00396 d->config->d->setWriteDefaults(true);
00397
00398 emit operationsChanged();
00399
00400 {
00401 QHashIterator<QWidget *, QString> it(d->associatedWidgets);
00402 while (it.hasNext()) {
00403 it.next();
00404 it.key()->setEnabled(d->config->hasGroup(it.value()));
00405 }
00406 }
00407
00408 {
00409 QHashIterator<QGraphicsWidget *, QString> it(d->associatedGraphicsWidgets);
00410 while (it.hasNext()) {
00411 it.next();
00412 it.key()->setEnabled(d->config->hasGroup(it.value()));
00413 }
00414 }
00415 }
00416
00417 void Service::registerOperationsScheme()
00418 {
00419 if (d->config) {
00420
00421 return;
00422 }
00423
00424 if (d->name.isEmpty()) {
00425 kDebug() << "No name found";
00426 return;
00427 }
00428
00429 QString path = KStandardDirs::locate("data", "plasma/services/" + d->name + ".operations");
00430
00431 if (path.isEmpty()) {
00432 kDebug() << "Cannot find operations description:" << d->name << ".operations";
00433 return;
00434 }
00435
00436 QFile file(path);
00437 setOperationsScheme(&file);
00438 }
00439
00440 }
00441
00442 #include "service.moc"
00443