1 /*
2  * activeprofiles_dbus.cpp - Class for interacting with other psi instances
3  * Copyright (C) 2006-2007  Martin Hostettler
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  */
20 
21 #include "activeprofiles.h"
22 
23 #include <QCoreApplication>
24 #include <QString>
25 #include <QStringList>
26 #include <QByteArray>
27 #include <QDBusConnection>
28 #include <QDBusConnectionInterface>
29 #include <QDBusInterface>
30 
31 
32 #include <QLabel>
33 #include <QTimer>
34 
35 
36 #include "applicationinfo.h"
37 #include "dbus.h"
38 
39 
40 
41 
42 
43 /** \brief encodes a string to "[A-Z][a-z][0-9]_-" ascii subset
44  * [A-Z][a-z][0-9] -> [A-Z][a-z][0-9]
45  * / -> _
46  * everything else to "-XX" with XX hex code of char
47  * (slow)
48  */
encodeAlNumD(QString in)49 static QString encodeAlNumD(QString in)
50 {
51 	QString out;
52 	QByteArray chars = in.toUtf8();
53 	bool first = true;
54 	foreach(char c, chars) {
55 		if (('A' <= c) && (c <= 'z')) {
56 			out += (char)c;
57 		} else if (('0' <= c) && (c <= '9') && !first) {
58 			out += (char)c;
59 		} else if ('/' == c) {
60 			out += "_";
61 		} else {
62 			out += QString("-%1").arg(c, 2, 16, QChar('0'));
63 		}
64 		first = false;
65 	}
66 	return out;
67 }
68 
69 
70 /** \brief DBus busname registration helper
71  * \param dbusIface interface of bus
72  * \param name busname to register
73  * \param queue try queueing?
74  * \return got dbus name?
75  */
registerBusname(QDBusConnectionInterface * dbusIface,QString name,bool queue)76 static bool registerBusname(QDBusConnectionInterface *dbusIface, QString name, bool queue)
77 {
78 	bool ok = false;
79 	QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply;
80 	reply = dbusIface->registerService(name,
81 		queue ?	QDBusConnectionInterface::QueueService : QDBusConnectionInterface::DontQueueService,
82 			QDBusConnectionInterface::AllowReplacement);
83 	if (reply.isValid()) {
84 		switch (reply.value()) {
85 			case QDBusConnectionInterface::ServiceNotRegistered:
86 				qWarning("failed to register dbus name %s:", qPrintable(name));
87 				break;
88 			case QDBusConnectionInterface::ServiceQueued:
89 				qDebug("dbus name %s already taken, queueing", qPrintable(name));
90 				break;
91 			case QDBusConnectionInterface::ServiceRegistered:
92 				ok = true;
93 		}
94 	} else {
95 		qWarning("failed to register dbus name %s: %s", qPrintable(name), qPrintable(reply.error().message()));
96 	}
97 	return ok;
98 }
99 
100 
101 class ActiveProfiles::Private {
102 public:
103 	QString profile;
104 	QStringList busNames;
105 	bool registerBusnames(QString prof);
106 
107 	QString dbusName(QString prof);
108 };
109 
110 
dbusName(QString prof)111 QString ActiveProfiles::Private::dbusName(QString prof)
112 {
113 	QString name = PSIDBUSNAME;
114 	name += ".";
115 	name += encodeAlNumD(ApplicationInfo::homeDir(ApplicationInfo::ConfigLocation)).right(qMax(0,200-name.size()));
116 	if (!prof.isEmpty()) {
117 		name += ".";
118 		name += encodeAlNumD(prof).right(qMax(0,250-name.size()));
119 	}
120 	return name;
121 }
122 
123 
124 
registerBusnames(QString prof)125 bool ActiveProfiles::Private::registerBusnames(QString prof)
126 {
127 	// FIXME move where?
128 	if (!QDBusConnection::sessionBus().isConnected()) {
129 		qWarning("can't connect to dbus");
130 		return true;
131 	}
132 
133 
134 	QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
135 
136 	QString name = PSIDBUSNAME;
137 	registerBusname(dbusIface, name, true);
138 	busNames << name;
139 	name = dbusName(QString());
140 	registerBusname(dbusIface, name, true);
141 	busNames << name;
142 	name = dbusName(prof);
143 	busNames << name;
144 	return registerBusname(dbusIface, name, false);
145 
146 }
147 
148 
isActive(const QString & profile) const149 bool ActiveProfiles::isActive(const QString &profile) const
150 {
151 	if (!QDBusConnection::sessionBus().isConnected()) {
152 		qWarning("can't connect to dbus");
153 		return false;
154 	}
155 
156 	QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
157 	return dbusIface->isServiceRegistered(d->dbusName(profile));
158 }
159 
isAnyActive() const160 bool ActiveProfiles::isAnyActive() const
161 {
162 	return isActive("");
163 }
164 
165 
setThisProfile(const QString & profile)166 bool ActiveProfiles::setThisProfile(const QString &profile)
167 {
168 	if (profile == d->profile)
169 		return true;
170 
171 	if (profile.isEmpty()) {
172 		unsetThisProfile();
173 		return true;
174 	}
175 	bool ok = d->registerBusnames(profile);
176 	if (ok) {
177 		d->profile = profile;
178 	} else {
179 		unsetThisProfile();
180 	}
181 	return ok;
182 }
183 
unsetThisProfile()184 void ActiveProfiles::unsetThisProfile()
185 {
186 	QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
187 	foreach(QString name, d->busNames) {
188 		dbusIface->unregisterService(name);
189 	}
190 	d->busNames.clear();
191 	d->profile = QString();
192 }
193 
thisProfile() const194 QString ActiveProfiles::thisProfile() const
195 {
196 	return d->profile;
197 }
198 
ActiveProfiles()199 ActiveProfiles::ActiveProfiles()
200 	: QObject(QCoreApplication::instance())
201 {
202 	d = new ActiveProfiles::Private;
203 }
204 
~ActiveProfiles()205 ActiveProfiles::~ActiveProfiles()
206 {
207 	delete d;
208 	d = 0;
209 }
210 
setStatus(const QString & profile,const QString & status,const QString & message) const211 bool ActiveProfiles::setStatus(const QString &profile, const QString &status, const QString &message) const
212 {
213 	QDBusInterface(d->dbusName(profile), "/Main", PSIDBUSMAINIF).call(QDBus::NoBlock,
214 			"setStatus", status, message);
215 	return true;
216 }
217 
openUri(const QString & profile,const QString & uri) const218 bool ActiveProfiles::openUri(const QString &profile, const QString &uri) const
219 {
220 	QDBusInterface(d->dbusName(profile), "/Main", PSIDBUSMAINIF).call(QDBus::NoBlock,
221 			"openURI", uri);
222 	return true;
223 }
224 
raise(const QString & profile,bool withUI) const225 bool ActiveProfiles::raise(const QString &profile, bool withUI) const
226 {
227 	QLabel *lab=0;
228 	QDBusMessage msg = QDBusMessage::createMethodCall ( d->dbusName(profile), "/Main", PSIDBUSMAINIF, "raise" );
229 	if (withUI) {
230 		lab = new QLabel(tr("This psi profile is already running...<br>please wait..."));
231 		QTimer::singleShot(250, lab, SLOT(show()));
232 	}
233 	QDBusMessage rmsg = QDBusConnection::sessionBus().call(msg, QDBus::BlockWithGui, 10000);
234 	if (withUI) {
235 		lab->hide();
236 		delete lab;
237 	}
238 	if (rmsg.type() == QDBusMessage::ReplyMessage) {
239 		return true;
240 	} else return false;
241 }
242