1 /**
2 * This file is part of TelepathyQt
3 *
4 * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
5 * @copyright Copyright (C) 2010 Nokia Corporation
6 * @license LGPL 2.1
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <TelepathyQt/ProfileManager>
24
25 #include "TelepathyQt/_gen/profile-manager.moc.hpp"
26 #include "TelepathyQt/debug-internal.h"
27
28 #include <TelepathyQt/ConnectionManager>
29 #include <TelepathyQt/PendingComposite>
30 #include <TelepathyQt/PendingReady>
31 #include <TelepathyQt/PendingStringList>
32 #include <TelepathyQt/Profile>
33 #include <TelepathyQt/ReadinessHelper>
34
35 #include <QFile>
36 #include <QFileInfo>
37 #include <QString>
38 #include <QStringList>
39
40 namespace Tp
41 {
42
43 struct TP_QT_NO_EXPORT ProfileManager::Private
44 {
45 Private(ProfileManager *parent, const QDBusConnection &bus);
46
47 static void introspectMain(Private *self);
48 static void introspectFakeProfiles(Private *self);
49
50 ProfileManager *parent;
51 ReadinessHelper *readinessHelper;
52 QDBusConnection bus;
53 QHash<QString, ProfilePtr> profiles;
54 QList<ConnectionManagerPtr> cms;
55 };
56
Private(ProfileManager * parent,const QDBusConnection & bus)57 ProfileManager::Private::Private(ProfileManager *parent, const QDBusConnection &bus)
58 : parent(parent),
59 readinessHelper(parent->readinessHelper()),
60 bus(bus)
61 {
62 ReadinessHelper::Introspectables introspectables;
63
64 ReadinessHelper::Introspectable introspectableCore(
65 QSet<uint>() << 0, // makesSenseForStatuses
66 Features(), // dependsOnFeatures
67 QStringList(), // dependsOnInterfaces
68 (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
69 this);
70 introspectables[FeatureCore] = introspectableCore;
71
72 ReadinessHelper::Introspectable introspectableFakeProfiles(
73 QSet<uint>() << 0, // makesSenseForStatuses
74 Features() << FeatureCore, // dependsOnFeatures
75 QStringList(), // dependsOnInterfaces
76 (ReadinessHelper::IntrospectFunc) &Private::introspectFakeProfiles,
77 this);
78 introspectables[FeatureFakeProfiles] = introspectableFakeProfiles;
79
80 readinessHelper->addIntrospectables(introspectables);
81 }
82
introspectMain(ProfileManager::Private * self)83 void ProfileManager::Private::introspectMain(ProfileManager::Private *self)
84 {
85 QStringList searchDirs = Profile::searchDirs();
86
87 foreach (const QString searchDir, searchDirs) {
88 QDir dir(searchDir);
89 dir.setFilter(QDir::Files);
90
91 QFileInfoList list = dir.entryInfoList();
92 for (int i = 0; i < list.size(); ++i) {
93 QFileInfo fi = list.at(i);
94
95 if (fi.completeSuffix() != QLatin1String("profile")) {
96 continue;
97 }
98
99 QString fileName = fi.absoluteFilePath();
100 QString serviceName = fi.baseName();
101
102 if (self->profiles.contains(serviceName)) {
103 debug() << "Profile for service" << serviceName << "already "
104 "exists. Ignoring profile file:" << fileName;
105 continue;
106 }
107
108 ProfilePtr profile = Profile::createForFileName(fileName);
109 if (!profile->isValid()) {
110 continue;
111 }
112
113 if (profile->type() != QLatin1String("IM")) {
114 debug() << "Ignoring profile for service" << serviceName <<
115 ": type != IM. Profile file:" << fileName;
116 continue;
117 }
118
119 debug() << "Found profile for service" << serviceName <<
120 "- profile file:" << fileName;
121 self->profiles.insert(serviceName, profile);
122 }
123 }
124
125 self->readinessHelper->setIntrospectCompleted(FeatureCore, true);
126 }
127
introspectFakeProfiles(ProfileManager::Private * self)128 void ProfileManager::Private::introspectFakeProfiles(ProfileManager::Private *self)
129 {
130 PendingStringList *pendingCmNames = ConnectionManager::listNames(self->bus);
131 self->parent->connect(pendingCmNames,
132 SIGNAL(finished(Tp::PendingOperation *)),
133 SLOT(onCmNamesRetrieved(Tp::PendingOperation *)));
134 }
135
136 /**
137 * \class ProfileManager
138 * \headerfile TelepathyQt/profile-manager.h <TelepathyQt/ProfileManager>
139 *
140 * \brief The ProfileManager class provides helper methods to retrieve Profile
141 * objects.
142 */
143
144 /**
145 * Feature representing the core that needs to become ready to make the ProfileManager
146 * object usable.
147 *
148 * Note that this feature must be enabled in order to use all ProfileManager methods.
149 *
150 * When calling isReady(), becomeReady(), this feature is implicitly added
151 * to the requested features.
152 */
153 const Feature ProfileManager::FeatureCore = Feature(QLatin1String(ProfileManager::staticMetaObject.className()), 0, true);
154
155 /**
156 * Enabling this feature will make ProfileManager create fake Profile objects to all protocols
157 * supported on the installed connection managers, even if they don't have .profile files installed
158 * making use of them.
159 *
160 * Fake profiles are identified by Profile::isFake() returning \c true.
161 *
162 * The fake profile will contain the following info:
163 * - Profile::type() will return "IM"
164 * - Profile::provider() will return an empty string
165 * - Profile::serviceName() will return cmName-protocolName
166 * - Profile::name() and Profile::protocolName() will return protocolName
167 * - Profile::iconName() will return "im-protocolName"
168 * - Profile::cmName() will return cmName
169 * - Profile::parameters() will return a list matching CM default parameters for protocol with name
170 * protocolName.
171 * - Profile::presences() will return an empty list and
172 * Profile::allowOtherPresences() will return \c true, meaning that CM
173 * presences should be used
174 * - Profile::unsupportedChannelClassSpecs() will return an empty list
175 *
176 * Where cmName and protocolName are the name of the connection manager and the name of the protocol
177 * for which this fake Profile is created, respectively.
178 */
179 const Feature ProfileManager::FeatureFakeProfiles = Feature(QLatin1String(ProfileManager::staticMetaObject.className()), 1);
180
181 /**
182 * Create a new ProfileManager object.
183 */
create(const QDBusConnection & bus)184 ProfileManagerPtr ProfileManager::create(const QDBusConnection &bus)
185 {
186 return ProfileManagerPtr(new ProfileManager(bus));
187 }
188
189 /**
190 * Construct a new ProfileManager object.
191 */
ProfileManager(const QDBusConnection & bus)192 ProfileManager::ProfileManager(const QDBusConnection &bus)
193 : Object(),
194 ReadyObject(this, FeatureCore),
195 mPriv(new Private(this, bus))
196 {
197 }
198
199 /**
200 * Class destructor.
201 */
~ProfileManager()202 ProfileManager::~ProfileManager()
203 {
204 delete mPriv;
205 }
206
207 /**
208 * Return a list of all available profiles.
209 *
210 * \return A list of all available profiles.
211 */
profiles() const212 QList<ProfilePtr> ProfileManager::profiles() const
213 {
214 return mPriv->profiles.values();
215 }
216
217 /**
218 * Return a list of all available profiles for a given connection manager.
219 *
220 * \param cmName Connection manager name.
221 * \return A list of all available profiles for a given connection manager.
222 */
profilesForCM(const QString & cmName) const223 QList<ProfilePtr> ProfileManager::profilesForCM(const QString &cmName) const
224 {
225 QList<ProfilePtr> ret;
226 foreach (const ProfilePtr &profile, mPriv->profiles) {
227 if (profile->cmName() == cmName) {
228 ret << profile;
229 }
230 }
231 return ret;
232 }
233
234 /**
235 * Return a list of all available profiles for a given \a protocol.
236 *
237 * \param protocolName Protocol name.
238 * \return A list of all available profiles for a given \a protocol.
239 */
profilesForProtocol(const QString & protocolName) const240 QList<ProfilePtr> ProfileManager::profilesForProtocol(
241 const QString &protocolName) const
242 {
243 QList<ProfilePtr> ret;
244 foreach (const ProfilePtr &profile, mPriv->profiles) {
245 if (profile->protocolName() == protocolName) {
246 ret << profile;
247 }
248 }
249 return ret;
250 }
251
252 /**
253 * Return the profile for a given \a service.
254 *
255 * \param serviceName Service name.
256 * \return The profile for \a service.
257 */
profileForService(const QString & serviceName) const258 ProfilePtr ProfileManager::profileForService(const QString &serviceName) const
259 {
260 return mPriv->profiles.value(serviceName);
261 }
262
onCmNamesRetrieved(Tp::PendingOperation * op)263 void ProfileManager::onCmNamesRetrieved(Tp::PendingOperation *op)
264 {
265 if (op->isError()) {
266 warning().nospace() << "Getting available CMs failed with " <<
267 op->errorName() << ":" << op->errorMessage();
268 mPriv->readinessHelper->setIntrospectCompleted(FeatureFakeProfiles, false,
269 op->errorName(), op->errorMessage());
270 return;
271 }
272
273 PendingStringList *pendingCmNames = qobject_cast<PendingStringList *>(op);
274 QStringList cmNames(pendingCmNames->result());
275 if (cmNames.isEmpty()) {
276 mPriv->readinessHelper->setIntrospectCompleted(FeatureFakeProfiles, true);
277 return;
278 }
279
280 QList<PendingOperation *> ops;
281 foreach (const QString &cmName, cmNames) {
282 ConnectionManagerPtr cm = ConnectionManager::create(mPriv->bus, cmName);
283 mPriv->cms.append(cm);
284 ops.append(cm->becomeReady());
285 }
286
287 PendingComposite *pc = new PendingComposite(ops, false, ProfileManagerPtr(this));
288 connect(pc,
289 SIGNAL(finished(Tp::PendingOperation *)),
290 SLOT(onCMsReady(Tp::PendingOperation *)));
291 }
292
onCMsReady(Tp::PendingOperation * op)293 void ProfileManager::onCMsReady(Tp::PendingOperation *op)
294 {
295 if (op->isError()) {
296 warning() << "Failed introspecting all CMs, trying to create fake profiles anyway";
297 }
298
299 ProfilePtr profile;
300 foreach (const ConnectionManagerPtr &cm, mPriv->cms) {
301 if (!cm->isReady()) {
302 continue;
303 }
304
305 foreach (const QString &protocolName, cm->supportedProtocols()) {
306 /* check if there is a profile whose service name is protocolName, and if found,
307 * check if the profile is for cm, if not check if there is a profile whose service
308 * name is cm-protocolName, and if not found create one named cm-protocolName. */
309 profile = profileForService(protocolName);
310 if (profile && profile->cmName() == cm->name()) {
311 continue;
312 }
313
314 QString serviceName = QString(QLatin1String("%1-%2")).arg(cm->name()).arg(protocolName);
315 profile = profileForService(serviceName);
316 if (profile) {
317 continue;
318 }
319
320 profile = ProfilePtr(new Profile(
321 serviceName,
322 cm->name(),
323 protocolName,
324 cm->protocol(protocolName)));
325 mPriv->profiles.insert(serviceName, profile);
326 }
327 }
328
329 mPriv->readinessHelper->setIntrospectCompleted(FeatureFakeProfiles, true);
330 }
331
332 } // Tp
333