File indexing completed on 2024-05-12 05:22:53

0001 /*
0002     test_cryptoconfig.cpp
0003 
0004     This file is part of libkleopatra's test suite.
0005     SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-only
0008 */
0009 
0010 #include <utils/compat.h>
0011 
0012 #include <qgpgme/qgpgmenewcryptoconfig.h>
0013 
0014 #include <QCoreApplication>
0015 #include <iostream>
0016 
0017 using namespace std;
0018 using namespace QGpgME;
0019 
0020 #include <gpgme++/engineinfo.h>
0021 #include <gpgme++/error.h>
0022 #include <gpgme++/global.h>
0023 
0024 #include <stdlib.h>
0025 
0026 int main(int argc, char **argv)
0027 {
0028     if (GpgME::initializeLibrary(0)) {
0029         return 1;
0030     }
0031 
0032     if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.2.2") {
0033         cerr << "This test requires GnuPG 2.2.2 or later.";
0034         return 1;
0035     }
0036 
0037     QCoreApplication::setApplicationName(QStringLiteral("test_cryptoconfig"));
0038     QCoreApplication app(argc, argv);
0039 
0040     QGpgME::CryptoConfig *config = new QGpgMENewCryptoConfig;
0041 
0042     // Dynamic querying of the options
0043     cout << "Components:" << endl;
0044     QStringList components = config->componentList();
0045 
0046     for (QStringList::Iterator compit = components.begin(); compit != components.end(); ++compit) {
0047         cout << "Component " << (*compit).toLocal8Bit().constData() << ":" << endl;
0048         const QGpgME::CryptoConfigComponent *comp = config->component(*compit);
0049         Q_ASSERT(comp);
0050         QStringList groups = comp->groupList();
0051         for (QStringList::Iterator groupit = groups.begin(); groupit != groups.end(); ++groupit) {
0052             const QGpgME::CryptoConfigGroup *group = comp->group(*groupit);
0053             Q_ASSERT(group);
0054             cout << " Group " << (*groupit).toLocal8Bit().constData() << ": descr=\"" << group->description().toLocal8Bit().constData() << "\""
0055                  << " level=" << group->level() << endl;
0056             QStringList entries = group->entryList();
0057             for (QStringList::Iterator entryit = entries.begin(); entryit != entries.end(); ++entryit) {
0058                 const QGpgME::CryptoConfigEntry *entry = group->entry(*entryit);
0059                 Q_ASSERT(entry);
0060                 cout << "  Entry " << (*entryit).toLocal8Bit().constData() << ":"
0061                      << " descr=\"" << entry->description().toLocal8Bit().constData() << "\""
0062                      << " " << (entry->isSet() ? "is set" : "is not set");
0063                 if (!entry->isList()) {
0064                     switch (entry->argType()) {
0065                     case QGpgME::CryptoConfigEntry::ArgType_None:
0066                         break;
0067                     case QGpgME::CryptoConfigEntry::ArgType_Int:
0068                         cout << " int value=" << entry->intValue();
0069                         break;
0070                     case QGpgME::CryptoConfigEntry::ArgType_UInt:
0071                         cout << " uint value=" << entry->uintValue();
0072                         break;
0073                     case QGpgME::CryptoConfigEntry::ArgType_LDAPURL:
0074                     case QGpgME::CryptoConfigEntry::ArgType_Path:
0075                     // fallthrough
0076                     case QGpgME::CryptoConfigEntry::ArgType_DirPath:
0077                     // fallthrough
0078                     case QGpgME::CryptoConfigEntry::ArgType_String:
0079 
0080                         cout << " string value=" << entry->stringValue().toLocal8Bit().constData();
0081                         break;
0082                     case QGpgME::CryptoConfigEntry::NumArgType:
0083                         // just metadata and should never actually occur in the switch
0084                         break;
0085                     }
0086                 } else { // lists
0087                     switch (entry->argType()) {
0088                     case QGpgME::CryptoConfigEntry::ArgType_None: {
0089                         cout << " set " << entry->numberOfTimesSet() << " times";
0090                         break;
0091                     }
0092                     case QGpgME::CryptoConfigEntry::ArgType_Int: {
0093                         // (marc) if an entry isn't optional, you have to unset it for the default to take effect, so this Q_ASSERT is wrong:
0094                         // Q_ASSERT( entry->isOptional() ); // empty lists must be allowed (see https://www.intevation.de/roundup/aegypten/issue121)
0095                         std::vector<int> lst = entry->intValueList();
0096                         QString str;
0097                         for (std::vector<int>::const_iterator it = lst.begin(); it != lst.end(); ++it) {
0098                             str += QString::number(*it);
0099                         }
0100                         cout << " int values=" << str.toLocal8Bit().constData();
0101                         break;
0102                     }
0103                     case QGpgME::CryptoConfigEntry::ArgType_UInt: {
0104                         // (marc) if an entry isn't optional, you have to unset it for the default to take effect, so this Q_ASSERT is wrong:
0105                         // Q_ASSERT( entry->isOptional() ); // empty lists must be allowed (see https://www.intevation.de/roundup/aegypten/issue121)
0106                         std::vector<uint> lst = entry->uintValueList();
0107                         QString str;
0108                         for (std::vector<uint>::const_iterator it = lst.begin(); it != lst.end(); ++it) {
0109                             str += QString::number(*it);
0110                         }
0111                         cout << " uint values=" << str.toLocal8Bit().constData();
0112                         break;
0113                     }
0114                     case QGpgME::CryptoConfigEntry::ArgType_LDAPURL: {
0115                         // (marc) if an entry isn't optional, you have to unset it for the default to take effect, so this Q_ASSERT is wrong:
0116                         // Q_ASSERT( entry->isOptional() ); // empty lists must be allowed (see https://www.intevation.de/roundup/aegypten/issue121)
0117                         const QList<QUrl> urls = entry->urlValueList();
0118                         cout << " url values ";
0119                         for (const QUrl &url : urls) {
0120                             cout << url.toString().toLocal8Bit().constData() << " ";
0121                         }
0122                         cout << endl;
0123                     }
0124                     // fallthrough
0125                     case QGpgME::CryptoConfigEntry::ArgType_Path:
0126                     // fallthrough
0127                     case QGpgME::CryptoConfigEntry::ArgType_DirPath:
0128                     // fallthrough
0129                     case QGpgME::CryptoConfigEntry::ArgType_String:
0130                     // fallthrough string value lists were removed from
0131                     // gpgconf in 2008
0132                     case QGpgME::CryptoConfigEntry::NumArgType:
0133                         // just metadata and should never actually occur in the switch
0134                         break;
0135                     }
0136                 }
0137                 cout << endl;
0138             }
0139             // ...
0140         }
0141     }
0142 
0143     {
0144         // Static querying of a single boolean option
0145         static const char *s_entryName = "quiet";
0146         QGpgME::CryptoConfigEntry *entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0147         if (entry) {
0148             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_None);
0149             bool val = entry->boolValue();
0150             cout << "quiet option initially: " << (val ? "is set" : "is not set") << endl;
0151 
0152             entry->setBoolValue(!val);
0153             Q_ASSERT(entry->isDirty());
0154             config->sync(true);
0155 
0156             // Clear cached values!
0157             config->clear();
0158 
0159             // Check new value
0160             entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0161             Q_ASSERT(entry);
0162             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_None);
0163             cout << "quiet option now: " << (val ? "is set" : "is not set") << endl;
0164             Q_ASSERT(entry->boolValue() == !val);
0165 
0166             // Set to default
0167             entry->resetToDefault();
0168             Q_ASSERT(entry->boolValue() == false); // that's the default
0169             Q_ASSERT(entry->isDirty());
0170             Q_ASSERT(!entry->isSet());
0171             config->sync(true);
0172             config->clear();
0173 
0174             // Check value
0175             entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0176             Q_ASSERT(!entry->isDirty());
0177             Q_ASSERT(!entry->isSet());
0178             cout << "quiet option reset to default: " << (entry->boolValue() ? "is set" : "is not set") << endl;
0179             Q_ASSERT(entry->boolValue() == false);
0180 
0181             // Reset old value
0182             entry->setBoolValue(val);
0183             Q_ASSERT(entry->isDirty());
0184             config->sync(true);
0185 
0186             cout << "quiet option reset to initial: " << (val ? "is set" : "is not set") << endl;
0187         } else {
0188             cout << "Entry 'dirmngr/" << s_entryName << "' not found" << endl;
0189         }
0190     }
0191 
0192     {
0193         // Static querying and setting of a single int option
0194         static const char *s_entryName = "ldaptimeout";
0195         QGpgME::CryptoConfigEntry *entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0196         if (entry) {
0197             // type of entry should be int (since 2.3) or uint (until 2.2)
0198             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int || entry->argType() == QGpgME::CryptoConfigEntry::ArgType_UInt);
0199             const int initialValue = entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int ? entry->intValue() : static_cast<int>(entry->uintValue());
0200             cout << "LDAP timeout initially: " << initialValue << " seconds." << endl;
0201 
0202             // Test setting the option directly, then querying again
0203             // system( "echo 'ldaptimeout:0:101' | gpgconf --change-options dirmngr" );
0204             // Now let's do it with the C++ API instead
0205             if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int) {
0206                 entry->setIntValue(101);
0207             } else {
0208                 entry->setUIntValue(101);
0209             }
0210             Q_ASSERT(entry->isDirty());
0211             config->sync(true);
0212 
0213             // Clear cached values!
0214             config->clear();
0215 
0216             // Check new value
0217             {
0218                 entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0219                 Q_ASSERT(entry);
0220                 const int newValue = entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int ? entry->intValue() : static_cast<int>(entry->uintValue());
0221                 cout << "LDAP timeout now: " << newValue << " seconds." << endl;
0222                 Q_ASSERT(newValue == 101);
0223             }
0224 
0225             // Set to default
0226             {
0227                 entry->resetToDefault();
0228                 const int defaultValue = entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int ? entry->intValue() : static_cast<int>(entry->uintValue());
0229                 cout << "LDAP timeout reset to default, " << defaultValue << " seconds." << endl;
0230                 Q_ASSERT(defaultValue == 15);
0231                 Q_ASSERT(entry->isDirty());
0232                 Q_ASSERT(!entry->isSet());
0233                 config->sync(true);
0234                 config->clear();
0235             }
0236 
0237             // Check value
0238             {
0239                 entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0240                 Q_ASSERT(!entry->isDirty());
0241                 Q_ASSERT(!entry->isSet());
0242                 const int defaultValue = entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int ? entry->intValue() : static_cast<int>(entry->uintValue());
0243                 cout << "LDAP timeout reset to default, " << defaultValue << " seconds." << endl;
0244                 Q_ASSERT(defaultValue == 15);
0245             }
0246 
0247             // Reset old value
0248             if (entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Int) {
0249                 entry->setIntValue(initialValue);
0250             } else {
0251                 entry->setUIntValue(initialValue);
0252             }
0253             Q_ASSERT(entry->isDirty());
0254             config->sync(true);
0255 
0256             cout << "LDAP timeout reset to initial " << initialValue << " seconds." << endl;
0257         } else {
0258             cout << "Entry 'dirmngr/" << s_entryName << "' not found" << endl;
0259         }
0260     }
0261 
0262     {
0263         // Static querying and setting of a single string option
0264         static const char *s_entryName = "log-file";
0265         QGpgME::CryptoConfigEntry *entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0266         if (entry) {
0267             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Path);
0268             QString val = entry->stringValue();
0269             cout << "Log-file initially: " << val.toLocal8Bit().constData() << endl;
0270 
0271             // Test setting the option, sync'ing, then querying again
0272             entry->setStringValue(QStringLiteral("/tmp/test:%e5ä"));
0273             Q_ASSERT(entry->isDirty());
0274             config->sync(true);
0275 
0276             // Let's see how it prints it
0277             system("gpgconf --list-options dirmngr | grep log-file");
0278 
0279             // Clear cached values!
0280             config->clear();
0281 
0282             // Check new value
0283             entry = Kleo::getCryptoConfigEntry(config, "dirmngr", s_entryName);
0284             Q_ASSERT(entry);
0285             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_Path);
0286             cout << "Log-file now: " << entry->stringValue().toLocal8Bit().constData() << endl;
0287             Q_ASSERT(entry->stringValue() == QStringLiteral("/tmp/test:%e5ä")); // (or even with %e5 decoded)
0288 
0289             // Reset old value
0290 #if 0
0291             QString arg(val);
0292             if (!arg.isEmpty()) {
0293                 arg.prepend('"');
0294             }
0295             Q3CString sys;
0296             sys.sprintf("echo 'log-file:%s' | gpgconf --change-options dirmngr", arg.local8Bit().data());
0297             system(sys.data());
0298 #endif
0299             entry->setStringValue(val);
0300             Q_ASSERT(entry->isDirty());
0301             config->sync(true);
0302 
0303             cout << "Log-file reset to initial " << val.toLocal8Bit().constData() << endl;
0304         } else {
0305             cout << "Entry 'dirmngr/" << s_entryName << "' not found" << endl;
0306         }
0307     }
0308 
0309     {
0310         // Static querying and setting of the keyserver list option
0311         static const char *s_entryName = "keyserver";
0312         QGpgME::CryptoConfigEntry *entry = Kleo::getCryptoConfigEntry(config, "gpgsm", s_entryName);
0313         if (entry) {
0314             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_LDAPURL);
0315             Q_ASSERT(entry->isList());
0316             const QList<QUrl> val = entry->urlValueList();
0317             cout << "URL list initially: ";
0318             for (const QUrl &url : val) {
0319                 cout << url.toString().toLocal8Bit().constData() << ", ";
0320             }
0321             cout << endl;
0322 
0323             // Test setting the option, sync'ing, then querying again
0324             QList<QUrl> lst;
0325             lst << QUrl(QStringLiteral("ldap://a:389?b"));
0326             Q_ASSERT(lst[0].query() == QLatin1Char('b'));
0327             lst << QUrl(QStringLiteral("ldap://foo:389?a:b c"));
0328             Q_ASSERT(lst[1].query() == QStringLiteral("a:b c")); // see, the space got _not_escaped
0329             lst << QUrl(QStringLiteral("ldap://server:389?a=b,c=DE"));
0330             Q_ASSERT(lst[2].query() == QStringLiteral("a=b,c=DE")); // the query contains a literal ','
0331             lst << QUrl(QStringLiteral("ldap://foo:389?a#ldaps"));
0332             Q_ASSERT(lst[3].fragment() == QStringLiteral("ldaps"));
0333             // cout << " trying to set: " << lst.toStringList().join(", ").local8Bit() << endl;
0334             entry->setURLValueList(lst);
0335             Q_ASSERT(entry->isDirty());
0336             config->sync(true);
0337 
0338             // Let's see how it prints it
0339             system("gpgconf --list-options gpgsm | grep 'keyserver'");
0340 
0341             // Clear cached values!
0342             config->clear();
0343 
0344             // Check new value
0345             entry = Kleo::getCryptoConfigEntry(config, "gpgsm", s_entryName);
0346             Q_ASSERT(entry);
0347             Q_ASSERT(entry->argType() == QGpgME::CryptoConfigEntry::ArgType_LDAPURL);
0348             Q_ASSERT(entry->isList());
0349             // Get QUrl form
0350             const QList<QUrl> newlst = entry->urlValueList();
0351             cout << "URL list now: ";
0352             for (const QUrl &url : newlst) {
0353                 cout << url.toString().toLocal8Bit().constData() << ", ";
0354             }
0355             cout << endl;
0356             Q_ASSERT(newlst.size() == lst.size());
0357             Q_ASSERT(newlst[0].url() == lst[0].url());
0358             Q_ASSERT(newlst[1].url() == lst[1].url());
0359             Q_ASSERT(newlst[2].url() == lst[2].url());
0360             Q_ASSERT(newlst[3].url() == lst[3].url());
0361 
0362             // Reset old value
0363             entry->setURLValueList(val);
0364             Q_ASSERT(entry->isDirty());
0365             config->sync(true);
0366 
0367             const QList<QUrl> resetList = entry->urlValueList();
0368             cout << "URL list reset to initial: ";
0369             for (const QUrl &url : resetList) {
0370                 cout << url.toString().toLocal8Bit().constData() << ", ";
0371             }
0372             cout << endl;
0373         } else {
0374             cout << "Entry 'gpgsm/" << s_entryName << "' not found" << endl;
0375         }
0376     }
0377 
0378     cout << "Done." << endl;
0379 }