mirror of
https://github.com/QuasarApp/qca.git
synced 2025-04-27 12:04:31 +00:00
keystore api update for passive entries, initial refactor of keystore backend
svn path=/trunk/kdesupport/qca/; revision=646458
This commit is contained in:
parent
1e7fa767af
commit
66e98c2371
@ -384,12 +384,6 @@ namespace QCA
|
||||
*/
|
||||
QCA_EXPORT void setGlobalRNG(const QString &provider);
|
||||
|
||||
/**
|
||||
Return a reference to the KeyStoreManager, which is used to interface with
|
||||
system storage, PGP keyrings, and smart cards.
|
||||
*/
|
||||
QCA_EXPORT KeyStoreManager *keyStoreManager();
|
||||
|
||||
/**
|
||||
Return a reference to the %QCA Logger, which is used for diagnostics
|
||||
and error recording.
|
||||
|
@ -38,8 +38,8 @@
|
||||
namespace QCA
|
||||
{
|
||||
class KeyStoreTracker;
|
||||
class KeyStoreThread;
|
||||
class KeyStoreManagerPrivate;
|
||||
class KeyStorePrivate;
|
||||
|
||||
/**
|
||||
\class KeyStoreEntry qca_keystore.h QtCrypto
|
||||
@ -68,12 +68,18 @@ namespace QCA
|
||||
*/
|
||||
KeyStoreEntry();
|
||||
|
||||
/**
|
||||
Create a passive KeyStoreEntry based on known entry
|
||||
*/
|
||||
KeyStoreEntry(const QString &id);
|
||||
|
||||
/**
|
||||
Standard copy constructor
|
||||
|
||||
\param from the source entry
|
||||
*/
|
||||
KeyStoreEntry(const KeyStoreEntry &from);
|
||||
|
||||
~KeyStoreEntry();
|
||||
|
||||
/**
|
||||
@ -88,6 +94,9 @@ namespace QCA
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
bool isAvailable() const;
|
||||
bool isAccessible() const;
|
||||
|
||||
/**
|
||||
Determine the type of key stored in this object
|
||||
*/
|
||||
@ -99,10 +108,15 @@ namespace QCA
|
||||
QString name() const;
|
||||
|
||||
/**
|
||||
The ID associated with the key stored in this object
|
||||
The ID associated with the key stored in this object.
|
||||
|
||||
The ID is unique across all stores, and may be very long.
|
||||
*/
|
||||
QString id() const;
|
||||
|
||||
QString storeName() const;
|
||||
QString storeId() const;
|
||||
|
||||
/**
|
||||
If a KeyBundle is stored in this object, return that
|
||||
bundle.
|
||||
@ -134,9 +148,47 @@ namespace QCA
|
||||
*/
|
||||
PGPKey pgpPublicKey() const;
|
||||
|
||||
/**
|
||||
Returns true if the entry is available, otherwise false.
|
||||
Available means that the retrieval functions (like
|
||||
keyBundle(), certificate(), pgpPublicKey(), etc) will
|
||||
return non-null objects. Entries retrieved from a
|
||||
KeyStore are always available, and therefore it is not
|
||||
necessary to call this function. Calling this function
|
||||
on an already available entry may cause the entry to
|
||||
be refreshed.
|
||||
|
||||
\note This function is blocking.
|
||||
*/
|
||||
bool ensureAvailable();
|
||||
|
||||
// like ensureAvailable, but also login to the token if needed
|
||||
bool ensureAccess();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
friend class KeyStoreTracker;
|
||||
};
|
||||
|
||||
class QCA_EXPORT KeyStoreEntryWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyStoreEntryWatcher(const KeyStoreEntry &e, QObject *parent = 0);
|
||||
~KeyStoreEntryWatcher();
|
||||
|
||||
KeyStoreEntry entry() const;
|
||||
|
||||
signals:
|
||||
void available();
|
||||
void unavailable();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
friend class Private;
|
||||
Private *d;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -158,7 +210,6 @@ namespace QCA
|
||||
becomes invalid (isValid() == false), and unavailable() is emitted.
|
||||
even if the device later reappears, the KeyStore remains invalid.
|
||||
a new KeyStore will have to be created to use the device again.
|
||||
|
||||
*/
|
||||
class QCA_EXPORT KeyStore : public QObject, public Algorithm
|
||||
{
|
||||
@ -180,9 +231,9 @@ namespace QCA
|
||||
Obtain a specific KeyStore
|
||||
|
||||
\param id the identification for the key store
|
||||
\param parent the parent object for this keystore
|
||||
\param keyStoreManager the parent manager for this keystore
|
||||
*/
|
||||
KeyStore(const QString &id, QObject *parent = 0);
|
||||
KeyStore(const QString &id, KeyStoreManager *keyStoreManager);
|
||||
|
||||
~KeyStore();
|
||||
|
||||
@ -284,12 +335,10 @@ namespace QCA
|
||||
void unavailable();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
friend class KeyStorePrivate;
|
||||
KeyStorePrivate *d;
|
||||
|
||||
friend class KeyStoreTracker;
|
||||
friend class KeyStoreManager;
|
||||
void invalidate();
|
||||
friend class KeyStoreManagerPrivate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -307,15 +356,18 @@ namespace QCA
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyStoreManager(QObject *parent = 0);
|
||||
~KeyStoreManager();
|
||||
|
||||
/**
|
||||
Initialize all key store providers
|
||||
*/
|
||||
void start();
|
||||
static void start();
|
||||
|
||||
/**
|
||||
Initialize a specific key store provider
|
||||
*/
|
||||
void start(const QString &provider);
|
||||
static void start(const QString &provider);
|
||||
|
||||
/**
|
||||
Indicates if the manager is busy looking for key stores
|
||||
@ -332,25 +384,31 @@ namespace QCA
|
||||
*/
|
||||
QStringList keyStores() const;
|
||||
|
||||
/**
|
||||
The number of key stores that are currently available
|
||||
*/
|
||||
int count() const;
|
||||
|
||||
/**
|
||||
The diagnostic result of key store operations, such as
|
||||
warnings and errors
|
||||
*/
|
||||
QString diagnosticText() const;
|
||||
static QString diagnosticText();
|
||||
|
||||
/**
|
||||
Clears the diagnostic result log
|
||||
*/
|
||||
void clearDiagnosticText();
|
||||
static void clearDiagnosticText();
|
||||
|
||||
/**
|
||||
If you are not using the eventloop, call this to update
|
||||
the object state to the present
|
||||
*/
|
||||
void sync();
|
||||
|
||||
signals:
|
||||
/**
|
||||
emitted when the manager is done looking for key stores
|
||||
emitted when the manager has started looking for key stores
|
||||
*/
|
||||
void busyStarted();
|
||||
|
||||
/**
|
||||
emitted when the manager has finished looking for key stores
|
||||
*/
|
||||
void busyFinished();
|
||||
|
||||
@ -364,13 +422,15 @@ namespace QCA
|
||||
KeyStoreManagerPrivate *d;
|
||||
|
||||
friend class Global;
|
||||
friend class KeyStoreTracker;
|
||||
friend class KeyStoreThread;
|
||||
KeyStoreManager();
|
||||
~KeyStoreManager();
|
||||
friend class KeyStorePrivate;
|
||||
|
||||
void scan() const;
|
||||
static void scan();
|
||||
static void shutdown();
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QCA::KeyStoreEntry)
|
||||
Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry>)
|
||||
Q_DECLARE_METATYPE(QList<QCA::KeyStoreEntry::Type>)
|
||||
|
||||
#endif
|
||||
|
@ -56,25 +56,28 @@ public:
|
||||
QMutex manager_mutex;
|
||||
ProviderManager manager;
|
||||
Random *rng;
|
||||
KeyStoreManager *ksm;
|
||||
Logger *logger;
|
||||
Logger *logger;
|
||||
QVariantMap properties;
|
||||
QMap<QString,QVariantMap> config;
|
||||
|
||||
Global()
|
||||
{
|
||||
rng = 0;
|
||||
ksm = new KeyStoreManager;
|
||||
logger = new Logger;
|
||||
logger = new Logger;
|
||||
secmem = false;
|
||||
}
|
||||
|
||||
~Global()
|
||||
{
|
||||
delete ksm;
|
||||
delete logger;
|
||||
KeyStoreManager::shutdown();
|
||||
delete logger;
|
||||
delete rng;
|
||||
}
|
||||
|
||||
void ksm_scan()
|
||||
{
|
||||
KeyStoreManager::scan();
|
||||
}
|
||||
};
|
||||
|
||||
static Global *global = 0;
|
||||
@ -274,9 +277,12 @@ Provider *defaultProvider()
|
||||
|
||||
void scanForPlugins()
|
||||
{
|
||||
QMutexLocker lock(&global->manager_mutex);
|
||||
{
|
||||
QMutexLocker lock(&global->manager_mutex);
|
||||
|
||||
global->manager.scan();
|
||||
global->manager.scan();
|
||||
}
|
||||
global->ksm_scan();
|
||||
}
|
||||
|
||||
void unloadAllPlugins()
|
||||
@ -448,11 +454,6 @@ void setGlobalRNG(const QString &provider)
|
||||
global->rng = new Random(provider);
|
||||
}
|
||||
|
||||
KeyStoreManager *keyStoreManager()
|
||||
{
|
||||
return global->ksm;
|
||||
}
|
||||
|
||||
Logger *logger()
|
||||
{
|
||||
return global->logger;
|
||||
@ -466,13 +467,14 @@ void logText( const QString &message, Logger::Severity severity )
|
||||
bool haveSystemStore()
|
||||
{
|
||||
// ensure the system store is loaded
|
||||
global->ksm->start("default");
|
||||
global->ksm->waitForBusyFinished();
|
||||
KeyStoreManager::start("default");
|
||||
KeyStoreManager ksm;
|
||||
ksm.waitForBusyFinished();
|
||||
|
||||
QStringList list = global->ksm->keyStores();
|
||||
QStringList list = ksm.keyStores();
|
||||
for(int n = 0; n < list.count(); ++n)
|
||||
{
|
||||
KeyStore ks(list[n]);
|
||||
KeyStore ks(list[n], &ksm);
|
||||
if(ks.type() == KeyStore::System && ks.holdsTrustedCertificates())
|
||||
return true;
|
||||
}
|
||||
@ -482,14 +484,15 @@ bool haveSystemStore()
|
||||
CertificateCollection systemStore()
|
||||
{
|
||||
// ensure the system store is loaded
|
||||
global->ksm->start("default");
|
||||
global->ksm->waitForBusyFinished();
|
||||
KeyStoreManager::start("default");
|
||||
KeyStoreManager ksm;
|
||||
ksm.waitForBusyFinished();
|
||||
|
||||
CertificateCollection col;
|
||||
QStringList list = global->ksm->keyStores();
|
||||
QStringList list = ksm.keyStores();
|
||||
for(int n = 0; n < list.count(); ++n)
|
||||
{
|
||||
KeyStore ks(list[n]);
|
||||
KeyStore ks(list[n], &ksm);
|
||||
|
||||
// system store
|
||||
if(ks.type() == KeyStore::System && ks.holdsTrustedCertificates())
|
||||
|
@ -709,6 +709,65 @@ public:
|
||||
//----------------------------------------------------------------------------
|
||||
// DefaultKeyStoreEntry
|
||||
//----------------------------------------------------------------------------
|
||||
static QString escape_string(const QString &in)
|
||||
{
|
||||
QString out;
|
||||
for(int n = 0; n < in.length(); ++n)
|
||||
{
|
||||
if(in[n] == '\\')
|
||||
out += "\\\\";
|
||||
else if(in[n] == ':')
|
||||
out += "\\c";
|
||||
else
|
||||
out += in[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString unescape_string(const QString &in)
|
||||
{
|
||||
QString out;
|
||||
for(int n = 0; n < in.length(); ++n)
|
||||
{
|
||||
if(in[n] == '\\')
|
||||
{
|
||||
if(n + 1 < in.length())
|
||||
{
|
||||
if(in[n + 1] == '\\')
|
||||
out += '\\';
|
||||
else if(in[n + 1] == 'c')
|
||||
out += ':';
|
||||
}
|
||||
}
|
||||
else
|
||||
out += in[n];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static QString makeId(const QString &storeId, const QString &storeName, const QString &entryId, const QString &entryName)
|
||||
{
|
||||
QStringList out;
|
||||
out += escape_string("qca_def");
|
||||
out += escape_string(storeId);
|
||||
out += escape_string(storeName);
|
||||
out += escape_string(entryId);
|
||||
out += escape_string(entryName);
|
||||
return out.join(":");
|
||||
}
|
||||
|
||||
static bool parseId(const QString &in, QString *storeId, QString *storeName, QString *entryId, QString *entryName)
|
||||
{
|
||||
QStringList list = in.split(':');
|
||||
if(list.count() != 5)
|
||||
return false;
|
||||
*storeId = unescape_string(list[1]);
|
||||
*storeName = unescape_string(list[2]);
|
||||
*entryId = unescape_string(list[3]);
|
||||
*entryName = unescape_string(list[4]);
|
||||
return true;
|
||||
}
|
||||
|
||||
class DefaultKeyStoreEntry : public KeyStoreEntryContext
|
||||
{
|
||||
public:
|
||||
@ -717,6 +776,8 @@ public:
|
||||
Certificate _cert;
|
||||
CRL _crl;
|
||||
|
||||
QString item_name;
|
||||
|
||||
DefaultKeyStoreEntry(const Certificate &cert, const QString &storeId, const QString &storeName, Provider *p) : KeyStoreEntryContext(p)
|
||||
{
|
||||
_storeId = storeId;
|
||||
@ -757,6 +818,11 @@ public:
|
||||
}
|
||||
|
||||
virtual QString name() const
|
||||
{
|
||||
return item_name;
|
||||
}
|
||||
|
||||
QString makeName() const
|
||||
{
|
||||
// use the common name, else orgname
|
||||
if(item_type == KeyStoreEntry::TypeCertificate)
|
||||
@ -872,17 +938,38 @@ public:
|
||||
for(n = 0; n < certs.count(); ++n)
|
||||
{
|
||||
DefaultKeyStoreEntry *c = new DefaultKeyStoreEntry(certs[n], storeId(0), name(0), provider());
|
||||
c->item_id = QString::number(n);
|
||||
//c->item_id = QString::number(n);
|
||||
QString ename = c->makeName();
|
||||
QString eid = QString::number(qHash(certs[n].toDER().toByteArray()));
|
||||
c->item_name = ename;
|
||||
c->item_id = makeId(storeId(0), name(0), eid, ename);
|
||||
out.append(c);
|
||||
}
|
||||
for(n = 0; n < crls.count(); ++n)
|
||||
{
|
||||
DefaultKeyStoreEntry *c = new DefaultKeyStoreEntry(crls[n], storeId(0), name(0), provider());
|
||||
c->item_name = c->makeName();
|
||||
c->item_id = QString::number(n); // FIXME
|
||||
out.append(c);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// TODO
|
||||
KeyStoreEntryContext *entryPassive(const QString &_storeId, const QString &entryId)
|
||||
{
|
||||
Q_UNUSED(_storeId);
|
||||
QString storeId, storeName, eid, ename;
|
||||
if(parseId(entryId, &storeId, &storeName, &eid, &ename))
|
||||
{
|
||||
DefaultKeyStoreEntry *c = new DefaultKeyStoreEntry(Certificate(), storeId, storeName, provider());
|
||||
c->item_name = ename;
|
||||
c->item_id = eid;
|
||||
return c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
1556
src/qca_keystore.cpp
1556
src/qca_keystore.cpp
File diff suppressed because it is too large
Load Diff
@ -182,7 +182,8 @@ private slots:
|
||||
if(e.source() == QCA::Event::KeyStore)
|
||||
{
|
||||
QString name = "keystore";
|
||||
QCA::KeyStore ks(e.keyStoreId());
|
||||
QCA::KeyStoreManager ksm;
|
||||
QCA::KeyStore ks(e.keyStoreId(), &ksm);
|
||||
if(ks.isValid())
|
||||
name = ks.name();
|
||||
|
||||
@ -537,12 +538,12 @@ static bool open_mime_data_sig(const QString &in, QString *data, QString *sig)
|
||||
}
|
||||
|
||||
// first = ids, second = names
|
||||
static QPair<QStringList, QStringList> getKeyStoreStrings(const QStringList &list)
|
||||
static QPair<QStringList, QStringList> getKeyStoreStrings(const QStringList &list, QCA::KeyStoreManager *ksm)
|
||||
{
|
||||
QPair<QStringList, QStringList> out;
|
||||
for(int n = 0; n < list.count(); ++n)
|
||||
{
|
||||
QCA::KeyStore ks(list[n]);
|
||||
QCA::KeyStore ks(list[n], ksm);
|
||||
out.first.append(ks.id());
|
||||
out.second.append(ks.name());
|
||||
}
|
||||
@ -593,9 +594,10 @@ static int findByString(const QPair<QStringList, QStringList> &in, const QString
|
||||
|
||||
static QString getKeyStore(const QString &name)
|
||||
{
|
||||
QCA::KeyStoreManager *ksm = QCA::keyStoreManager();
|
||||
QStringList storeList = ksm->keyStores();
|
||||
int n = findByString(getKeyStoreStrings(storeList), name);
|
||||
//QCA::KeyStoreManager *ksm = QCA::keyStoreManager();
|
||||
QCA::KeyStoreManager ksm;// = QCA::keyStoreManager();
|
||||
QStringList storeList = ksm.keyStores();
|
||||
int n = findByString(getKeyStoreStrings(storeList, &ksm), name);
|
||||
if(n != -1)
|
||||
return storeList[n];
|
||||
return QString();
|
||||
@ -622,7 +624,8 @@ static QPair<QCA::PGPKey, QCA::PGPKey> getPGPSecretKey(const QString &name)
|
||||
QString storeName = name.mid(0, n);
|
||||
QString objectName = name.mid(n + 1);
|
||||
|
||||
QCA::KeyStore store(getKeyStore(storeName));
|
||||
QCA::KeyStoreManager ksm;
|
||||
QCA::KeyStore store(getKeyStore(storeName), &ksm);
|
||||
if(!store.isValid())
|
||||
{
|
||||
printf("no such store\n");
|
||||
@ -659,7 +662,8 @@ static QCA::PGPKey getPGPPublicKey(const QString &name)
|
||||
QString storeName = name.mid(0, n);
|
||||
QString objectName = name.mid(n + 1);
|
||||
|
||||
QCA::KeyStore store(getKeyStore(storeName));
|
||||
QCA::KeyStoreManager ksm;
|
||||
QCA::KeyStore store(getKeyStore(storeName), &ksm);
|
||||
if(!store.isValid())
|
||||
{
|
||||
printf("no such store\n");
|
||||
@ -699,7 +703,8 @@ static QCA::Certificate getCertificate(const QString &name)
|
||||
QString storeName = name.mid(0, n);
|
||||
QString objectName = name.mid(n + 1);
|
||||
|
||||
QCA::KeyStore store(getKeyStore(storeName));
|
||||
QCA::KeyStoreManager ksm;
|
||||
QCA::KeyStore store(getKeyStore(storeName), &ksm);
|
||||
if(!store.isValid())
|
||||
{
|
||||
printf("no such store\n");
|
||||
@ -740,7 +745,8 @@ static QCA::PrivateKey getPrivateKey(const QString &name)
|
||||
QString storeName = name.mid(0, n);
|
||||
QString objectName = name.mid(n + 1);
|
||||
|
||||
QCA::KeyStore store(getKeyStore(storeName));
|
||||
QCA::KeyStoreManager ksm;
|
||||
QCA::KeyStore store(getKeyStore(storeName), &ksm);
|
||||
if(!store.isValid())
|
||||
{
|
||||
printf("no such store\n");
|
||||
@ -806,8 +812,10 @@ static void usage()
|
||||
printf(" --pgp decrypt [encryptedfile]\n");
|
||||
printf("\n");
|
||||
printf(" --list-tlsciphers\n");
|
||||
printf(" --showentry [keystoreentry id]\n");
|
||||
printf("\n");
|
||||
|
||||
// TODO: showentry?
|
||||
/*printf("qcatool: simple qca utility\n");
|
||||
printf("usage: qcatool (--pass, --noprompt) [command]\n");
|
||||
printf("\n");
|
||||
@ -958,8 +966,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
// activate the KeyStoreManager and block until ready
|
||||
QCA::keyStoreManager()->start();
|
||||
QCA::keyStoreManager()->waitForBusyFinished();
|
||||
QCA::KeyStoreManager::start();
|
||||
{
|
||||
QCA::KeyStoreManager ksm;
|
||||
ksm.waitForBusyFinished();
|
||||
}
|
||||
//QCA::keyStoreManager()->start();
|
||||
//QCA::keyStoreManager()->waitForBusyFinished();
|
||||
|
||||
// hook a passphrase prompt onto all the KeyStores
|
||||
PassphrasePrompt passphrasePrompt;
|
||||
@ -2031,11 +2044,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
else if(args[0] == "--list-keystores")
|
||||
{
|
||||
QCA::KeyStoreManager *ksm = QCA::keyStoreManager();
|
||||
QStringList storeList = ksm->keyStores();
|
||||
//QCA::KeyStoreManager *ksm = QCA::keyStoreManager();
|
||||
QCA::KeyStoreManager ksm;
|
||||
QStringList storeList = ksm.keyStores();
|
||||
for(int n = 0; n < storeList.count(); ++n)
|
||||
{
|
||||
QCA::KeyStore ks(storeList[n]);
|
||||
QCA::KeyStore ks(storeList[n], &ksm);
|
||||
QString type;
|
||||
switch(ks.type())
|
||||
{
|
||||
@ -2057,7 +2071,8 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
QCA::KeyStore store(getKeyStore(args[1]));
|
||||
QCA::KeyStoreManager ksm;
|
||||
QCA::KeyStore store(getKeyStore(args[1]), &ksm);
|
||||
if(!store.isValid())
|
||||
{
|
||||
printf("no such store\n");
|
||||
@ -2334,6 +2349,35 @@ int main(int argc, char **argv)
|
||||
cipherList = QCA::TLS::supportedCipherSuites(QCA::TLS::SSL_v2).join("\n\t");
|
||||
printf("SSL version 2: \n\t%s\n", qPrintable(cipherList));
|
||||
}
|
||||
else if(args[0] == "--showentry")
|
||||
{
|
||||
if(args.count() < 2)
|
||||
usage();
|
||||
else
|
||||
{
|
||||
QCA::KeyStoreEntry entry(args[1]);
|
||||
if(entry.isNull())
|
||||
{
|
||||
printf("Entry id is unknown to any provider.\n");
|
||||
return 1;
|
||||
}
|
||||
QString type;
|
||||
switch(entry.type())
|
||||
{
|
||||
case QCA::KeyStoreEntry::TypeKeyBundle: type = "Key "; break;
|
||||
case QCA::KeyStoreEntry::TypeCertificate: type = "Cert"; break;
|
||||
case QCA::KeyStoreEntry::TypeCRL: type = "CRL "; break;
|
||||
case QCA::KeyStoreEntry::TypePGPSecretKey: type = "PSec"; break;
|
||||
case QCA::KeyStoreEntry::TypePGPPublicKey: type = "PPub"; break;
|
||||
}
|
||||
|
||||
printf("Entry:\n");
|
||||
printf(" Name: %s\n", qPrintable(entry.name()));
|
||||
printf(" Type: %s\n", qPrintable(type));
|
||||
printf(" Store Name: %s\n", qPrintable(entry.storeName()));
|
||||
printf(" Store Id: %s\n", qPrintable(entry.storeId()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usage();
|
||||
|
Loading…
x
Reference in New Issue
Block a user