/** * This file is part of TelepathyQt * * @copyright Copyright (C) 2008-2010 Collabora Ltd. * @copyright Copyright (C) 2008-2010 Nokia Corporation * @license LGPL 2.1 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "TelepathyQt/manager-file.h" #include "TelepathyQt/debug-internal.h" #include "TelepathyQt/key-file.h" #include #include #include #include #include #include #include namespace Tp { struct TP_QT_NO_EXPORT ManagerFile::Private { Private(); Private(const QString &cnName); void init(); bool parse(const QString &fileName); bool isValid() const; bool hasParameter(const QString &protocol, const QString ¶mName) const; ParamSpec *getParameter(const QString &protocol, const QString ¶mName); QStringList protocols() const; ParamSpecList parameters(const QString &protocol) const; QVariant valueForKey(const QString ¶m, const QString &dbusSignature); struct ProtocolInfo { ProtocolInfo() {} ProtocolInfo(const ParamSpecList ¶ms, const PresenceSpecList &statuses) : params(params), statuses(statuses) { } ParamSpecList params; QString vcardField; QString englishName; QString iconName; RequestableChannelClassList rccs; PresenceSpecList statuses; AvatarSpec avatarRequirements; QStringList addressableVCardFields; QStringList addressableUriSchemes; }; QString cmName; KeyFile keyFile; QHash protocolsMap; bool valid; }; ManagerFile::Private::Private() : valid(false) { } ManagerFile::Private::Private(const QString &cmName) : cmName(cmName), valid(false) { init(); } void ManagerFile::Private::init() { // TODO: should we cache the configDirs anywhere? QStringList configDirs; QString xdgDataHome = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME")); if (xdgDataHome.isEmpty()) { configDirs << QDir::homePath() + QLatin1String("/.local/share/data/telepathy/managers/"); } else { configDirs << xdgDataHome + QLatin1String("/telepathy/managers/"); } QString xdgDataDirsEnv = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")); if (xdgDataDirsEnv.isEmpty()) { configDirs << QLatin1String("/usr/local/share/telepathy/managers/"); configDirs << QLatin1String("/usr/share/telepathy/managers/"); } else { QStringList xdgDataDirs = xdgDataDirsEnv.split(QLatin1Char(':')); foreach (const QString xdgDataDir, xdgDataDirs) { configDirs << xdgDataDir + QLatin1String("/telepathy/managers/"); } } foreach (const QString configDir, configDirs) { QString fileName = configDir + cmName + QLatin1String(".manager"); if (QFile::exists(fileName)) { debug() << "parsing manager file" << fileName; protocolsMap.clear(); if (!parse(fileName)) { warning() << "error parsing manager file" << fileName; continue; } valid = true; return; } } } bool ManagerFile::Private::parse(const QString &fileName) { keyFile.setFileName(fileName); if (keyFile.status() != KeyFile::NoError) { return false; } /* read supported protocols and parameters */ QString protocol; QStringList groups = keyFile.allGroups(); foreach (const QString group, groups) { if (group.startsWith(QLatin1String("Protocol "))) { protocol = group.right(group.length() - 9); keyFile.setGroup(group); ParamSpecList paramSpecList; SimpleStatusSpecMap statuses; QString param; QStringList params = keyFile.keys(); foreach (param, params) { ParamSpec spec; SimpleStatusSpec status; spec.flags = 0; QStringList values = keyFile.value(param).split(QLatin1String(" ")); if (param.startsWith(QLatin1String("param-"))) { spec.name = param.right(param.length() - 6); if (values.length() == 0) { warning() << "param" << spec.name << "set but no signature defined"; return false; } if (spec.name.endsWith(QLatin1String("password"))) { spec.flags |= ConnMgrParamFlagSecret; } spec.signature = values[0]; if (values.contains(QLatin1String("secret"))) { spec.flags |= ConnMgrParamFlagSecret; } if (values.contains(QLatin1String("dbus-property"))) { spec.flags |= ConnMgrParamFlagDBusProperty; } if (values.contains(QLatin1String("required"))) { spec.flags |= ConnMgrParamFlagRequired; } if (values.contains(QLatin1String("register"))) { spec.flags |= ConnMgrParamFlagRegister; } paramSpecList.append(spec); } else if (param.startsWith(QLatin1String("status-"))) { QString statusName = param.right(param.length() - 7); if (values.length() == 0) { warning() << "status" << statusName << "set but no type defined"; return false; } bool ok; status.type = values[0].toUInt(&ok); if (!ok) { warning() << "status" << statusName << "set but type is not an uint"; return false; } if (values.contains(QLatin1String("settable"))) { status.maySetOnSelf = true; } else { status.maySetOnSelf = false; } if (values.contains(QLatin1String("message"))) { status.canHaveMessage = true; } else { status.canHaveMessage = false; } if (statuses.contains(statusName)) { warning() << "status" << statusName << "defined more than once, " "replacing it"; } statuses.insert(statusName, status); } } protocolsMap.insert(protocol, ProtocolInfo(paramSpecList, PresenceSpecList(statuses))); /* now that we have all param-* created, let's find their default values */ foreach (param, params) { if (param.startsWith(QLatin1String("default-"))) { QString paramName = param.right(param.length() - 8); if (!hasParameter(protocol, paramName)) { warning() << "param" << paramName << "has default value set, but not a definition"; continue; } ParamSpec *spec = getParameter(protocol, paramName); spec->flags |= ConnMgrParamFlagHasDefault; /* map based on the param dbus signature, otherwise use * QString */ QVariant value = valueForKey(param, spec->signature); if (value.type() == QVariant::Invalid) { warning() << "param" << paramName << "has invalid signature"; protocolsMap.clear(); return false; } spec->defaultValue = QDBusVariant(value); } } ProtocolInfo &info = protocolsMap[protocol]; info.vcardField = keyFile.value(QLatin1String("VCardField")); info.englishName = keyFile.value(QLatin1String("EnglishName")); if (info.englishName.isEmpty()) { QStringList words = protocol.split(QLatin1Char('-')); for (int i = 0; i < words.size(); ++i) { words[i][0] = words[i].at(0).toUpper(); } info.englishName = words.join(QLatin1String(" ")); } info.iconName = keyFile.value(QLatin1String("Icon")); if (info.iconName.isEmpty()) { info.iconName = QString(QLatin1String("im-%1")).arg(protocol); } QStringList supportedMimeTypes = keyFile.valueAsStringList( QLatin1String("SupportedAvatarMIMETypes")); uint minHeight = keyFile.value(QLatin1String("MinimumAvatarHeight")).toUInt(); uint maxHeight = keyFile.value(QLatin1String("MaximumAvatarHeight")).toUInt(); uint recommendedHeight = keyFile.value( QLatin1String("RecommendedAvatarHeight")).toUInt(); uint minWidth = keyFile.value(QLatin1String("MinimumAvatarWidth")).toUInt(); uint maxWidth = keyFile.value(QLatin1String("MaximumAvatarWidth")).toUInt(); uint recommendedWidth = keyFile.value( QLatin1String("RecommendedAvatarWidth")).toUInt(); uint maxBytes = keyFile.value(QLatin1String("MaximumAvatarBytes")).toUInt(); info.avatarRequirements = AvatarSpec(supportedMimeTypes, minHeight, maxHeight, recommendedHeight, minWidth, maxWidth, recommendedWidth, maxBytes); info.addressableVCardFields = keyFile.valueAsStringList( QLatin1String("AddressableVCardFields")); info.addressableUriSchemes = keyFile.valueAsStringList( QLatin1String("AddressableURISchemes")); QStringList rccGroups = keyFile.valueAsStringList( QLatin1String("RequestableChannelClasses")); RequestableChannelClass rcc; foreach (const QString &rccGroup, rccGroups) { keyFile.setGroup(rccGroup); foreach (const QString &key, keyFile.keys()) { int spaceIdx = key.indexOf(QLatin1String(" ")); if (spaceIdx == -1) { continue; } QString propertyName = key.mid(0, spaceIdx); QString signature = key.mid(spaceIdx + 1); QString param = keyFile.value(key); QVariant value = valueForKey(key, signature); rcc.fixedProperties.insert(propertyName, value); } rcc.allowedProperties = keyFile.valueAsStringList( QLatin1String("allowed")); info.rccs.append(rcc); rcc.fixedProperties.clear(); rcc.allowedProperties.clear(); } } } return true; } bool ManagerFile::Private::isValid() const { return ((keyFile.status() == KeyFile::NoError) && (valid)); } bool ManagerFile::Private::hasParameter(const QString &protocol, const QString ¶mName) const { ParamSpecList paramSpecList = protocolsMap[protocol].params; foreach (const ParamSpec ¶mSpec, paramSpecList) { if (paramSpec.name == paramName) { return true; } } return false; } ParamSpec *ManagerFile::Private::getParameter(const QString &protocol, const QString ¶mName) { ParamSpecList ¶mSpecList = protocolsMap[protocol].params; for (int i = 0; i < paramSpecList.size(); ++i) { ParamSpec ¶mSpec = paramSpecList[i]; if (paramSpec.name == paramName) { return ¶mSpec; } } return nullptr; } QStringList ManagerFile::Private::protocols() const { return protocolsMap.keys(); } ParamSpecList ManagerFile::Private::parameters(const QString &protocol) const { return protocolsMap.value(protocol).params; } QVariant ManagerFile::Private::valueForKey(const QString ¶m, const QString &dbusSignature) { QString value = keyFile.rawValue(param); return parseValueWithDBusSignature(value, dbusSignature); } /** * \class ManagerFile * \ingroup utils * \headerfile TelepathyQt/manager-file.h * * \brief The ManagerFile class provides an easy way to read Telepathy manager * files according to the \telepathy_spec. */ /** * Create a ManagerFile object used to read .manager compliant files. */ ManagerFile::ManagerFile() : mPriv(new Private()) { } /** * Create a ManagerFile object used to read .manager compliant files. * * \param cmName Name of the connection manager to read the file for. */ ManagerFile::ManagerFile(const QString &cmName) : mPriv(new Private(cmName)) { } /** * Create a ManagerFile object used to read .manager compliant files. */ ManagerFile::ManagerFile(const ManagerFile &other) : mPriv(new Private()) { mPriv->cmName = other.mPriv->cmName; mPriv->keyFile = other.mPriv->keyFile; mPriv->protocolsMap = other.mPriv->protocolsMap; mPriv->valid = other.mPriv->valid; } /** * Class destructor. */ ManagerFile::~ManagerFile() { delete mPriv; } ManagerFile &ManagerFile::operator=(const ManagerFile &other) { mPriv->cmName = other.mPriv->cmName; mPriv->keyFile = other.mPriv->keyFile; mPriv->protocolsMap = other.mPriv->protocolsMap; mPriv->valid = other.mPriv->valid; return *this; } /** * Check whether or not a ManagerFile object is valid. If the file for the * specified connection manager cannot be found it will be considered invalid. * * \return true if valid, false otherwise. */ bool ManagerFile::isValid() const { return mPriv->isValid(); } /** * Return a list of all protocols defined in the manager file. * * \return List of all protocols defined in the file. */ QStringList ManagerFile::protocols() const { return mPriv->protocols(); } /** * Return a list of parameters for the given \a protocol. * * \param protocol Name of the protocol to look for. * \return List of ParamSpec of a specific protocol defined in the file, or an * empty list if the protocol is not defined. */ ParamSpecList ManagerFile::parameters(const QString &protocol) const { return mPriv->parameters(protocol); } /** * Return the name of the most common vcard field used for the given \a protocol's * contact identifiers, normalized to lower case. * * \param protocol Name of the protocol to look for. * \return The most common vcard field used for the given protocol's contact * identifiers, or an empty string if there is no such field or the * protocol is not defined. */ QString ManagerFile::vcardField(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).vcardField; } QStringList ManagerFile::addressableVCardFields(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).addressableVCardFields; } QStringList ManagerFile::addressableUriSchemes(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).addressableUriSchemes; } /** * Return the English-language name of the given \a protocol, such as "AIM" or "Yahoo!". * * The name can be used as a fallback if an application doesn't have a localized name for the * protocol. * * If the manager file doesn't specify the english name, it is inferred from the protocol name, such * that for example "google-talk" becomes "Google Talk", but "local-xmpp" becomes "Local Xmpp". * * \param protocol Name of the protocol to look for. * \return An English-language name for the given \a protocol. */ QString ManagerFile::englishName(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).englishName; } /** * Return the name of an icon for the given \a protocol in the system's icon * theme, such as "im-msn". * * If the manager file doesn't specify the icon name, "im-" is assumed. * * \param protocol Name of the protocol to look for. * \return The likely name of an icon for the given \a protocol. */ QString ManagerFile::iconName(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).iconName; } /** * Return a list of channel classes which might be requestable from a connection * to the given \a protocol. * * \param protocol Name of the protocol to look for. * \return A list of channel classes which might be requestable from a * connection to the given \a protocol or a default constructed * RequestableChannelClassList instance if the protocol is not defined. */ RequestableChannelClassList ManagerFile::requestableChannelClasses( const QString &protocol) const { return mPriv->protocolsMap.value(protocol).rccs; } /** * Return a list of PresenceSpec representing the possible presence statuses * from a connection to the given \a protocol. * * \param protocol Name of the protocol to look for. * \return A list of PresenceSpec representing the possible presence statuses * from a connection to the given \a protocol or an empty list * if the protocol is not defined. */ PresenceSpecList ManagerFile::allowedPresenceStatuses(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).statuses; } /** * Return the requirements (size limits, supported MIME types, etc) * for avatars used on the given \a protocol. * * \param protocol Name of the protocol to look for. * \return The requirements for avatars used on the given \a protocol or an invalid * AvatarSpec if the protocol is not defined. */ AvatarSpec ManagerFile::avatarRequirements(const QString &protocol) const { return mPriv->protocolsMap.value(protocol).avatarRequirements; } } // Tp