forked from qt-creator/qt-creator
iOS: Add API's to get provisioning data
Change-Id: I927b2dbaa9e6c175d90b1407418570bbd2a3d96e Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
@@ -49,12 +50,15 @@
|
||||
#include <qtsupport/qtversionmanager.h>
|
||||
#include <qtsupport/qtversionfactory.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDomDocument>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
@@ -74,6 +78,21 @@ namespace Internal {
|
||||
const QLatin1String SettingsGroup("IosConfigurations");
|
||||
const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
|
||||
|
||||
const char provisioningTeamsTag[] = "IDEProvisioningTeams";
|
||||
const char freeTeamTag[] = "isFreeProvisioningTeam";
|
||||
const char emailTag[] = "eMail";
|
||||
const char teamNameTag[] = "teamName";
|
||||
const char teamIdTag[] = "teamID";
|
||||
|
||||
const char udidTag[] = "UUID";
|
||||
const char profileNameTag[] = "Name";
|
||||
const char appIdTag[] = "AppIDName";
|
||||
const char expirationDateTag[] = "ExpirationDate";
|
||||
const char profileTeamIdTag[] = "TeamIdentifier";
|
||||
|
||||
static const QString xcodePlistPath = QDir::homePath() + "/Library/Preferences/com.apple.dt.Xcode.plist";
|
||||
static const QString provisioningProfileDirPath = QDir::homePath() + "/Library/MobileDevice/Provisioning Profiles";
|
||||
|
||||
static Core::Id deviceId(const Platform &platform)
|
||||
{
|
||||
if (platform.name.startsWith(QLatin1String("iphoneos-")))
|
||||
@@ -246,6 +265,20 @@ static QVersionNumber findXcodeVersion()
|
||||
return QVersionNumber();
|
||||
}
|
||||
|
||||
static QByteArray decodeProvisioningProfile(const QString &path)
|
||||
{
|
||||
QTC_ASSERT(!path.isEmpty(), return QByteArray());
|
||||
|
||||
Utils::SynchronousProcess p;
|
||||
p.setTimeoutS(3);
|
||||
// path is assumed to be valid file path to .mobileprovision
|
||||
const QStringList args = {"smime", "-inform", "der", "-verify", "-in", path};
|
||||
Utils::SynchronousProcessResponse res = p.runBlocking("openssl", args);
|
||||
if (res.result != Utils::SynchronousProcessResponse::Finished)
|
||||
qCDebug(iosCommonLog) << "Reading signed provisioning file failed" << path;
|
||||
return res.stdOut().toLatin1();
|
||||
}
|
||||
|
||||
void IosConfigurations::updateAutomaticKitList()
|
||||
{
|
||||
const QList<Platform> platforms = handledPlatforms();
|
||||
@@ -320,9 +353,9 @@ void IosConfigurations::updateAutomaticKitList()
|
||||
KitManager::deregisterKit(kit);
|
||||
}
|
||||
|
||||
static IosConfigurations *m_instance = 0;
|
||||
static IosConfigurations *m_instance = nullptr;
|
||||
|
||||
QObject *IosConfigurations::instance()
|
||||
IosConfigurations *IosConfigurations::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
@@ -409,6 +442,137 @@ void IosConfigurations::setDeveloperPath(const FileName &devPath)
|
||||
}
|
||||
}
|
||||
|
||||
void IosConfigurations::initializeProvisioningData()
|
||||
{
|
||||
// Initialize provisioning data only on demand. i.e. when first call to a provisioing data API
|
||||
// is made.
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
m_instance->loadProvisioningData(false);
|
||||
|
||||
// Watch the provisioing profiles folder and xcode plist for changes and
|
||||
// update the content accordingly.
|
||||
m_provisioningDataWatcher = new QFileSystemWatcher(this);
|
||||
m_provisioningDataWatcher->addPath(xcodePlistPath);
|
||||
m_provisioningDataWatcher->addPath(provisioningProfileDirPath);
|
||||
connect(m_provisioningDataWatcher, &QFileSystemWatcher::directoryChanged,
|
||||
std::bind(&IosConfigurations::loadProvisioningData, this, true));
|
||||
connect(m_provisioningDataWatcher, &QFileSystemWatcher::fileChanged,
|
||||
std::bind(&IosConfigurations::loadProvisioningData, this, true));
|
||||
}
|
||||
|
||||
void IosConfigurations::loadProvisioningData(bool notify)
|
||||
{
|
||||
m_developerTeams.clear();
|
||||
m_provisioningProfiles.clear();
|
||||
|
||||
// Populate Team id's
|
||||
const QSettings xcodeSettings(xcodePlistPath, QSettings::NativeFormat);
|
||||
const QVariantMap teamMap = xcodeSettings.value(provisioningTeamsTag).toMap();
|
||||
QList<QVariantMap> teams;
|
||||
QMapIterator<QString, QVariant> accountiterator(teamMap);
|
||||
while (accountiterator.hasNext()) {
|
||||
accountiterator.next();
|
||||
QVariantMap teamInfo = accountiterator.value().toMap();
|
||||
int provisioningTeamIsFree = teamInfo.value(freeTeamTag).toBool() ? 1 : 0;
|
||||
teamInfo[freeTeamTag] = provisioningTeamIsFree;
|
||||
teamInfo[emailTag] = accountiterator.key();
|
||||
teams.append(teamInfo);
|
||||
}
|
||||
|
||||
// Sort team id's to move the free provisioning teams at last of the list.
|
||||
Utils::sort(teams, [](const QVariantMap &teamInfo1, const QVariantMap &teamInfo2) {
|
||||
return teamInfo1.value(freeTeamTag).toInt() < teamInfo2.value(freeTeamTag).toInt();
|
||||
});
|
||||
|
||||
foreach (auto teamInfo, teams) {
|
||||
auto team = std::shared_ptr<DevelopmentTeam>::make_shared();
|
||||
team->m_name = teamInfo.value(teamNameTag).toString();
|
||||
team->m_email = teamInfo.value(emailTag).toString();
|
||||
team->m_identifier = teamInfo.value(teamIdTag).toString();
|
||||
team->m_freeTeam = teamInfo.value(freeTeamTag).toInt() == 1;
|
||||
m_developerTeams.append(team);
|
||||
}
|
||||
|
||||
const QDir provisioningProflesDir(provisioningProfileDirPath);
|
||||
foreach (QFileInfo fileInfo, provisioningProflesDir.entryInfoList({"*.mobileprovision"}, QDir::NoDotAndDotDot | QDir::Files)) {
|
||||
QDomDocument provisioningDoc;
|
||||
auto profile = ProvisioningProfilePtr::make_shared();;
|
||||
QString teamID;
|
||||
if (provisioningDoc.setContent(decodeProvisioningProfile(fileInfo.absoluteFilePath()))) {
|
||||
QDomNodeList nodes = provisioningDoc.elementsByTagName("key");
|
||||
for (int i = 0;i<nodes.count(); ++i) {
|
||||
QDomElement e = nodes.at(i).toElement();
|
||||
|
||||
if (e.text().compare(udidTag) == 0)
|
||||
profile->m_identifier = e.nextSiblingElement().text();
|
||||
|
||||
if (e.text().compare(profileNameTag) == 0)
|
||||
profile->m_name = e.nextSiblingElement().text();
|
||||
|
||||
if (e.text().compare(appIdTag) == 0)
|
||||
profile->m_appID = e.nextSiblingElement().text();
|
||||
|
||||
if (e.text().compare(expirationDateTag) == 0)
|
||||
profile->m_expirationDate = QDateTime::fromString(e.nextSiblingElement().text(),
|
||||
Qt::ISODate).toUTC();
|
||||
|
||||
if (e.text().compare(profileTeamIdTag) == 0) {
|
||||
teamID = e.nextSibling().firstChildElement().text();
|
||||
auto team = developmentTeam(teamID);
|
||||
if (team) {
|
||||
profile->m_team = team;
|
||||
team->m_profiles.append(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qCDebug(iosCommonLog) << "Error reading provisoing profile" << fileInfo.absoluteFilePath();
|
||||
}
|
||||
|
||||
if (profile->m_team)
|
||||
m_provisioningProfiles.append(profile);
|
||||
else
|
||||
qCDebug(iosCommonLog) << "Skipping profile. No corresponding team found" << profile;
|
||||
}
|
||||
|
||||
if (notify)
|
||||
emit provisioningDataChanged();
|
||||
}
|
||||
|
||||
const DevelopmentTeams &IosConfigurations::developmentTeams()
|
||||
{
|
||||
QTC_CHECK(m_instance);
|
||||
m_instance->initializeProvisioningData();
|
||||
return m_instance->m_developerTeams;
|
||||
}
|
||||
|
||||
DevelopmentTeamPtr IosConfigurations::developmentTeam(const QString &teamID)
|
||||
{
|
||||
QTC_CHECK(m_instance);
|
||||
m_instance->initializeProvisioningData();
|
||||
return findOrDefault(m_instance->m_developerTeams,
|
||||
Utils::equal(&DevelopmentTeam::identifier, teamID));
|
||||
}
|
||||
|
||||
const ProvisioningProfiles &IosConfigurations::provisioningProfiles()
|
||||
{
|
||||
QTC_CHECK(m_instance);
|
||||
m_instance->initializeProvisioningData();
|
||||
return m_instance->m_provisioningProfiles;
|
||||
}
|
||||
|
||||
ProvisioningProfilePtr IosConfigurations::provisioningProfile(const QString &profileID)
|
||||
{
|
||||
QTC_CHECK(m_instance);
|
||||
m_instance->initializeProvisioningData();
|
||||
return Utils::findOrDefault(m_instance->m_provisioningProfiles,
|
||||
Utils::equal(&ProvisioningProfile::identifier, profileID));
|
||||
}
|
||||
|
||||
static ClangToolChain *createToolChain(const Platform &platform, Core::Id l)
|
||||
{
|
||||
if (!l.isValid())
|
||||
@@ -455,5 +619,52 @@ QList<ToolChain *> IosToolChainFactory::autoDetect(const QList<ToolChain *> &exi
|
||||
return Utils::transform(toolChains, [](ClangToolChain *tc) -> ToolChain * { return tc; });
|
||||
}
|
||||
|
||||
QString DevelopmentTeam::identifier() const
|
||||
{
|
||||
return m_identifier;
|
||||
}
|
||||
|
||||
QString DevelopmentTeam::displayName() const
|
||||
{
|
||||
return QString("%1 - %2").arg(m_email).arg(m_name);
|
||||
}
|
||||
|
||||
QString DevelopmentTeam::details() const
|
||||
{
|
||||
return tr("%1 - Free Provisioning Team : %2")
|
||||
.arg(m_identifier).arg(m_freeTeam ? tr("Yes") : tr("No"));
|
||||
}
|
||||
|
||||
QDebug &operator<<(QDebug &stream, DevelopmentTeamPtr team)
|
||||
{
|
||||
QTC_ASSERT(team, return stream);
|
||||
stream << team->displayName() << team->identifier() << team->isFreeProfile();
|
||||
foreach (auto profile, team->m_profiles)
|
||||
stream << "Profile:" << profile;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QString ProvisioningProfile::identifier() const
|
||||
{
|
||||
return m_identifier;
|
||||
}
|
||||
|
||||
QString ProvisioningProfile::displayName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QString ProvisioningProfile::details() const
|
||||
{
|
||||
return tr("Team: %1\nApp ID: %2\nExpiration date: %3").arg(m_team->identifier()).arg(m_appID)
|
||||
.arg(m_expirationDate.toLocalTime().toString(Qt::SystemLocaleShortDate));
|
||||
}
|
||||
|
||||
QDebug &operator<<(QDebug &stream, std::shared_ptr<ProvisioningProfile> profile)
|
||||
{
|
||||
QTC_ASSERT(profile, return stream);
|
||||
return stream << profile->displayName() << profile->identifier() << profile->details();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <projectexplorer/toolchain.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
@@ -36,11 +37,62 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
class QFileSystemWatcher;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Ios {
|
||||
namespace Internal {
|
||||
|
||||
class DevelopmentTeam;
|
||||
|
||||
class ProvisioningProfile
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(ProvisioningProfile)
|
||||
public:
|
||||
ProvisioningProfile() {}
|
||||
std::shared_ptr<DevelopmentTeam> developmentTeam() { return m_team; }
|
||||
QString identifier() const;
|
||||
QString displayName() const;
|
||||
QString details() const;
|
||||
const QDateTime &expirationDate() const { return m_expirationDate; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<DevelopmentTeam> m_team;
|
||||
QString m_identifier;
|
||||
QString m_name;
|
||||
QString m_appID;
|
||||
QDateTime m_expirationDate;
|
||||
friend class IosConfigurations;
|
||||
friend QDebug &operator<<(QDebug &stream, std::shared_ptr<ProvisioningProfile> profile);
|
||||
};
|
||||
|
||||
using ProvisioningProfilePtr = std::shared_ptr<ProvisioningProfile>;
|
||||
using ProvisioningProfiles = QList<ProvisioningProfilePtr>;
|
||||
|
||||
class DevelopmentTeam
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(DevelopmentTeam)
|
||||
public:
|
||||
DevelopmentTeam() {}
|
||||
QString identifier() const;
|
||||
QString displayName() const;
|
||||
QString details() const;
|
||||
bool isFreeProfile() const { return m_freeTeam; }
|
||||
bool hasProvisioningProfile() const { return !m_profiles.isEmpty(); }
|
||||
|
||||
private:
|
||||
QString m_identifier;
|
||||
QString m_name;
|
||||
QString m_email;
|
||||
bool m_freeTeam = false;
|
||||
ProvisioningProfiles m_profiles;
|
||||
friend class IosConfigurations;
|
||||
friend QDebug &operator<<(QDebug &stream, std::shared_ptr<DevelopmentTeam> team);
|
||||
};
|
||||
|
||||
using DevelopmentTeamPtr = std::shared_ptr<DevelopmentTeam>;
|
||||
using DevelopmentTeams = QList<DevelopmentTeamPtr>;
|
||||
|
||||
class IosToolChainFactory : public ProjectExplorer::ToolChainFactory
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -50,13 +102,12 @@ public:
|
||||
QList<ProjectExplorer::ToolChain *> autoDetect(const QList<ProjectExplorer::ToolChain *> &existingToolChains) override;
|
||||
};
|
||||
|
||||
|
||||
class IosConfigurations : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static QObject *instance();
|
||||
static IosConfigurations *instance();
|
||||
static void initialize();
|
||||
static bool ignoreAllDevices();
|
||||
static void setIgnoreAllDevices(bool ignoreDevices);
|
||||
@@ -64,6 +115,13 @@ public:
|
||||
static QVersionNumber xcodeVersion();
|
||||
static Utils::FileName lldbPath();
|
||||
static void updateAutomaticKitList();
|
||||
static const DevelopmentTeams &developmentTeams();
|
||||
static DevelopmentTeamPtr developmentTeam(const QString &teamID);
|
||||
static const ProvisioningProfiles &provisioningProfiles();
|
||||
static ProvisioningProfilePtr provisioningProfile(const QString &profileID);
|
||||
|
||||
signals:
|
||||
void provisioningDataChanged();
|
||||
|
||||
private:
|
||||
IosConfigurations(QObject *parent);
|
||||
@@ -71,11 +129,17 @@ private:
|
||||
void save();
|
||||
void updateSimulators();
|
||||
static void setDeveloperPath(const Utils::FileName &devPath);
|
||||
void initializeProvisioningData();
|
||||
void loadProvisioningData(bool notify = true);
|
||||
|
||||
Utils::FileName m_developerPath;
|
||||
QVersionNumber m_xcodeVersion;
|
||||
bool m_ignoreAllDevices;
|
||||
QFileSystemWatcher *m_provisioningDataWatcher;
|
||||
ProvisioningProfiles m_provisioningProfiles;
|
||||
DevelopmentTeams m_developerTeams;
|
||||
};
|
||||
|
||||
QDebug &operator<<(QDebug &stream, std::shared_ptr<ProvisioningProfile> profile);
|
||||
QDebug &operator<<(QDebug &stream, std::shared_ptr<DevelopmentTeam> team);
|
||||
} // namespace Internal
|
||||
} // namespace Ios
|
||||
|
Reference in New Issue
Block a user