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 <coreplugin/icore.h>
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/synchronousprocess.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/synchronousprocess.h>
|
#include <utils/synchronousprocess.h>
|
||||||
#include <projectexplorer/kitmanager.h>
|
#include <projectexplorer/kitmanager.h>
|
||||||
@@ -49,12 +50,15 @@
|
|||||||
#include <qtsupport/qtversionmanager.h>
|
#include <qtsupport/qtversionmanager.h>
|
||||||
#include <qtsupport/qtversionfactory.h>
|
#include <qtsupport/qtversionfactory.h>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QProcess>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStringList>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
@@ -74,6 +78,21 @@ namespace Internal {
|
|||||||
const QLatin1String SettingsGroup("IosConfigurations");
|
const QLatin1String SettingsGroup("IosConfigurations");
|
||||||
const QLatin1String ignoreAllDevicesKey("IgnoreAllDevices");
|
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)
|
static Core::Id deviceId(const Platform &platform)
|
||||||
{
|
{
|
||||||
if (platform.name.startsWith(QLatin1String("iphoneos-")))
|
if (platform.name.startsWith(QLatin1String("iphoneos-")))
|
||||||
@@ -246,6 +265,20 @@ static QVersionNumber findXcodeVersion()
|
|||||||
return QVersionNumber();
|
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()
|
void IosConfigurations::updateAutomaticKitList()
|
||||||
{
|
{
|
||||||
const QList<Platform> platforms = handledPlatforms();
|
const QList<Platform> platforms = handledPlatforms();
|
||||||
@@ -320,9 +353,9 @@ void IosConfigurations::updateAutomaticKitList()
|
|||||||
KitManager::deregisterKit(kit);
|
KitManager::deregisterKit(kit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static IosConfigurations *m_instance = 0;
|
static IosConfigurations *m_instance = nullptr;
|
||||||
|
|
||||||
QObject *IosConfigurations::instance()
|
IosConfigurations *IosConfigurations::instance()
|
||||||
{
|
{
|
||||||
return m_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)
|
static ClangToolChain *createToolChain(const Platform &platform, Core::Id l)
|
||||||
{
|
{
|
||||||
if (!l.isValid())
|
if (!l.isValid())
|
||||||
@@ -455,5 +619,52 @@ QList<ToolChain *> IosToolChainFactory::autoDetect(const QList<ToolChain *> &exi
|
|||||||
return Utils::transform(toolChains, [](ClangToolChain *tc) -> ToolChain * { return tc; });
|
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 Internal
|
||||||
} // namespace Ios
|
} // namespace Ios
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include <projectexplorer/toolchain.h>
|
#include <projectexplorer/toolchain.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@@ -36,11 +37,62 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QSettings;
|
class QSettings;
|
||||||
|
class QFileSystemWatcher;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Ios {
|
namespace Ios {
|
||||||
namespace Internal {
|
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
|
class IosToolChainFactory : public ProjectExplorer::ToolChainFactory
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -50,13 +102,12 @@ public:
|
|||||||
QList<ProjectExplorer::ToolChain *> autoDetect(const QList<ProjectExplorer::ToolChain *> &existingToolChains) override;
|
QList<ProjectExplorer::ToolChain *> autoDetect(const QList<ProjectExplorer::ToolChain *> &existingToolChains) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class IosConfigurations : public QObject
|
class IosConfigurations : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QObject *instance();
|
static IosConfigurations *instance();
|
||||||
static void initialize();
|
static void initialize();
|
||||||
static bool ignoreAllDevices();
|
static bool ignoreAllDevices();
|
||||||
static void setIgnoreAllDevices(bool ignoreDevices);
|
static void setIgnoreAllDevices(bool ignoreDevices);
|
||||||
@@ -64,6 +115,13 @@ public:
|
|||||||
static QVersionNumber xcodeVersion();
|
static QVersionNumber xcodeVersion();
|
||||||
static Utils::FileName lldbPath();
|
static Utils::FileName lldbPath();
|
||||||
static void updateAutomaticKitList();
|
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:
|
private:
|
||||||
IosConfigurations(QObject *parent);
|
IosConfigurations(QObject *parent);
|
||||||
@@ -71,11 +129,17 @@ private:
|
|||||||
void save();
|
void save();
|
||||||
void updateSimulators();
|
void updateSimulators();
|
||||||
static void setDeveloperPath(const Utils::FileName &devPath);
|
static void setDeveloperPath(const Utils::FileName &devPath);
|
||||||
|
void initializeProvisioningData();
|
||||||
|
void loadProvisioningData(bool notify = true);
|
||||||
|
|
||||||
Utils::FileName m_developerPath;
|
Utils::FileName m_developerPath;
|
||||||
QVersionNumber m_xcodeVersion;
|
QVersionNumber m_xcodeVersion;
|
||||||
bool m_ignoreAllDevices;
|
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 Internal
|
||||||
} // namespace Ios
|
} // namespace Ios
|
||||||
|
Reference in New Issue
Block a user