File indexing completed on 2024-04-28 16:37:52

0001 
0002 #include "kmime_headers.h"
0003 #include "kmime_header_parsing.h"
0004 
0005 #include <QFile>
0006 #include <QByteArray>
0007 #include <QMap>
0008 #include <iostream>
0009 #include <cstdlib>
0010 #include <cassert>
0011 
0012 #include <getopt.h>
0013 
0014 using namespace KMime::HeaderParsing;
0015 using namespace std;
0016 
0017 static const char *tokenTypes[] = {
0018     "encoded-word",
0019     "atom",
0020     "token",
0021     "quoted-string",
0022     "domain-literal",
0023     "comment",
0024     "phrase",
0025     "dot-atom",
0026     "domain",
0027     "obs-route",
0028     "addr-spec",
0029     "angle-addr",
0030     "mailbox",
0031     "group",
0032     "address",
0033     "address-list",
0034     "parameter-list",
0035     "time",
0036     "date-time"
0037 };
0038 static const int tokenTypesLen = sizeof tokenTypes / sizeof * tokenTypes;
0039 
0040 void usage(const char *msg = nullptr)
0041 {
0042     if (msg && *msg) {
0043         cerr << msg << endl;
0044     }
0045     cerr <<
0046          "usage: test_kmime_header_parsing "
0047          "(--token <tokentype>|--headerfield <fieldtype>|--header)\n"
0048          "\n"
0049          "  --token <tokentype>       interpret input as <tokentype> and output\n"
0050          "  (-t)                      in parsed form. Currently defined values of\n"
0051          "                            <tokentype> are:" << endl;
0052     for (int i = 0 ; i < tokenTypesLen ; ++i) {
0053         cerr << "                               " << tokenTypes[i]
0054              << endl;
0055     }
0056     cerr << "\n"
0057          "  --headerfield <fieldtype> interpret input as header field <fieldtype>\n"
0058          "  (-f)                      and output in parsed form.\n"
0059          "\n"
0060          "  --header                  parse an RFC2822 header. Iterates over all\n"
0061          "  (-h)                      header fields and outputs them in parsed form."
0062          << endl;
0063     exit(1);
0064 }
0065 
0066 ostream &operator<<(ostream &stream, const QString &str)
0067 {
0068     return stream << str.toUtf8().data();
0069 }
0070 
0071 int main(int argc, char *argv[])
0072 {
0073     if (argc == 1 || argc > 3) {
0074         usage();
0075     }
0076     //
0077     // process options:
0078     //
0079     enum { None, Token, HeaderField, Header } action = None;
0080     const char *argument = nullptr;
0081     bool withCRLF = false;
0082     while (true) {
0083         int option_index = 0;
0084         static const struct option long_options[] = {
0085             // actions:
0086             { "token", 1, nullptr, 't' },
0087             { "headerfield", 1, nullptr, 'f' },
0088             { "header", 0, nullptr, 'h' },
0089             { "crlf", 0, nullptr, 'c' },
0090             { nullptr, 0, nullptr, 0 }
0091         };
0092 
0093         int c = getopt_long(argc, argv, "cf:ht:", long_options, &option_index);
0094         if (c == -1) {
0095             break;
0096         }
0097 
0098         switch (c) {
0099         case 'c': // --crlf
0100             withCRLF = true;
0101             break;
0102         case 't': // --token <tokentype>
0103             action = Token;
0104             argument = optarg;
0105             break;
0106         case 'f': // --headerfield <headertype>
0107             usage("--headerfield is not yet implemented!");
0108             break;
0109         case 'h': // --header
0110             usage("--header is not yet implemented!");
0111             break;
0112         default:
0113             usage("unknown option encountered!");
0114         }
0115     }
0116 
0117     if (optind < argc) {
0118         usage("non-option argument encountered!");
0119     }
0120 
0121     assert(action == Token);
0122     Q_UNUSED(action) // avoid warning in release mode
0123 
0124     int index;
0125     for (index = 0 ; index < tokenTypesLen ; ++index) {
0126         if (!qstricmp(tokenTypes[index], argument)) {
0127             break;
0128         }
0129     }
0130 
0131     if (index >= tokenTypesLen) {
0132         usage("unknown token type");
0133     }
0134 
0135     //QT5 KComponentData componentData( "test_kmime_header_parsing" );
0136 
0137     QFile stdIn;
0138     stdIn.open(stdin, QIODevice::ReadOnly);
0139     const QByteArray indata = stdIn.readAll();
0140     stdIn.close();
0141     QByteArray::ConstIterator iit = indata.begin();
0142     const QByteArray::ConstIterator iend = indata.end();
0143 
0144     switch (index) {
0145     case 0: {
0146         // encoded-word
0147         QString result;
0148         QByteArray language;
0149         QByteArray charset;
0150         // must have checked for initial '=' already:
0151         bool ok = indata.size() >= 1 && *iit++ == '=' &&
0152                   parseEncodedWord(iit, iend, result, language, charset);
0153 
0154         cout << (ok ? "OK" : "BAD") << endl
0155              << "result:\n" << result << endl
0156              << "language:\n" << language.data() << endl;
0157     }
0158     break;
0159     case 1: {
0160         // atom
0161         cout << "with 8bit: " << endl;
0162         QByteArray result;
0163         bool ok = parseAtom(iit, iend, result, true);
0164 
0165         cout << (ok ? "OK" : "BAD") << endl
0166              << "result:\n" << result.constData()
0167              << endl;
0168 
0169         cout << "without 8bit: " << endl;
0170 #ifdef COMPILE_FAIL
0171         ok = parseAtom(indata.begin(), iend, result, false);
0172 #else
0173         iit = indata.begin();
0174         ok = parseAtom(iit, iend, result, false);
0175 #endif
0176 
0177         cout << (ok ? "OK" : "BAD") << endl
0178              << "result:\n" << result.constData()
0179              << endl;
0180     }
0181     break;
0182     case 2: {
0183         // token
0184         cout << "with 8bit: " << endl;
0185         QByteArray result;
0186         bool ok = parseToken(iit, iend, result, ParseTokenAllow8Bit);
0187 
0188         cout << (ok ? "OK" : "BAD") << endl
0189              << "result:\n" << result.constData()
0190              << endl;
0191 
0192         cout << "without 8bit: " << endl;
0193 #ifdef COMPILE_FAIL
0194         ok = parseToken(indata.begin(), iend, result, ParseTokenNoFlag);
0195 #else
0196         iit = indata.begin();
0197         ok = parseToken(iit, iend, result, ParseTokenNoFlag);
0198 #endif
0199 
0200         cout << (ok ? "OK" : "BAD") << endl
0201              << "result:\n" << result.constData()
0202              << endl;
0203     }
0204     break;
0205     case 3: {
0206         // quoted-string
0207         QString result;
0208         // must have checked for initial '"' already:
0209         bool ok = *iit++ == '"' &&
0210                   parseGenericQuotedString(iit, iend, result, withCRLF, '"', '"');
0211 
0212         cout << (ok ? "OK" : "BAD") << endl
0213              << "result:\n" << result
0214              << endl;
0215     }
0216     break;
0217     case 4: {
0218         // domain-literal
0219         QString result;
0220         // must have checked for initial '[' already:
0221         bool ok = *iit++ == '[' &&
0222                   parseGenericQuotedString(iit, iend, result, withCRLF, '[', ']');
0223 
0224         cout << (ok ? "OK" : "BAD") << endl
0225              << "result:\n" << result
0226              << endl;
0227     }
0228     break;
0229     case 5: {
0230         // comment
0231         QString result;
0232         // must have checked for initial '(' already:
0233         bool ok = *iit++ == '(' &&
0234                   parseComment(iit, iend, result, withCRLF, true);
0235 
0236         cout << (ok ? "OK" : "BAD") << endl
0237              << "result:\n" << result
0238              << endl;
0239     }
0240     break;
0241     case 6: {
0242         // phrase
0243         QString result;
0244         bool ok = parsePhrase(iit, iend, result, withCRLF);
0245 
0246         cout << (ok ? "OK" : "BAD") << endl
0247              << "result:\n" << result
0248              << endl;
0249     }
0250     break;
0251     case 7: {
0252         // dot-atom
0253         QByteArray result;
0254         bool ok = parseDotAtom(iit, iend, result, withCRLF);
0255 
0256         cout << (ok ? "OK" : "BAD") << endl
0257              << "result:\n" << result.constData()
0258              << endl;
0259     }
0260     break;
0261     case 8: {
0262         // domain
0263         QString result;
0264         bool ok = parseDomain(iit, iend, result, withCRLF);
0265 
0266         cout << (ok ? "OK" : "BAD") << endl
0267              << "result:\n" << result
0268              << endl;
0269     }
0270     break;
0271     case 9: {
0272         // obs-route
0273         QStringList result;
0274         bool ok = parseObsRoute(iit, iend, result, withCRLF, true /*save*/);
0275 
0276         cout << (ok ? "OK" : "BAD") << endl
0277              << "result: " << result.count() << " domains:"
0278              << endl;
0279         for (QStringList::ConstIterator it = result.constBegin() ;
0280                 it != result.constEnd() ; ++it) {
0281             cout << (*it) << endl;
0282         }
0283     }
0284     break;
0285     case 10: {
0286         // addr-spec
0287         KMime::Types::AddrSpec result;
0288         bool ok = parseAddrSpec(iit, iend, result, withCRLF);
0289 
0290         cout << (ok ? "OK" : "BAD") << endl
0291              << "result.localPart:\n" << result.localPart << endl
0292              << "result.domain:\n" << result.domain
0293              << endl;
0294     }
0295     break;
0296     case 11: {
0297         // angle-addr
0298         KMime::Types::AddrSpec result;
0299         bool ok = parseAngleAddr(iit, iend, result, withCRLF);
0300 
0301         cout << (ok ? "OK" : "BAD") << endl
0302              << "result.localPart:\n" << result.localPart << endl
0303              << "result.domain:\n" << result.domain
0304              << endl;
0305     }
0306     break;
0307     case 12: {
0308         // mailbox
0309         KMime::Types::Mailbox result;
0310         bool ok = parseMailbox(iit, iend, result, withCRLF);
0311 
0312         cout << (ok ? "OK" : "BAD") << endl
0313              << "result.displayName:\n" << result.name() << endl
0314              << "result.addrSpec.localPart:\n" << result.addrSpec().localPart << endl
0315              << "result.addrSpec.domain:\n" << result.addrSpec().domain
0316              << endl;
0317     }
0318     break;
0319     case 13: {
0320         // group
0321         KMime::Types::Address result;
0322         bool ok = parseGroup(iit, iend, result, withCRLF);
0323 
0324         cout << (ok ? "OK" : "BAD") << endl
0325              << "result.displayName:\n" << result.displayName
0326              << endl;
0327         int i = 0;
0328         for (const auto &it : std::as_const(result.mailboxList)) {
0329             cout << "result.mailboxList[" << i << "].displayName:\n"
0330                  << (it).name() << endl
0331                  << "result.mailboxList[" << i << "].addrSpec.localPart:\n"
0332                  << (it).addrSpec().localPart << endl
0333                  << "result.mailboxList[" << i << "].addrSpec.domain:\n"
0334                  << (it).addrSpec().domain << endl;
0335             ++i;
0336         }
0337     }
0338     break;
0339     case 14: {
0340         // address
0341         KMime::Types::Address result;
0342         bool ok = parseAddress(iit, iend, result, withCRLF);
0343 
0344         cout << (ok ? "OK" : "BAD") << endl
0345              << "result.displayName:\n"
0346              << endl;
0347         int i = 0;
0348         const auto mailboxList = result.mailboxList;
0349         for (const auto &it : mailboxList) {
0350             cout << "result.mailboxList[" << i << "].displayName:\n"
0351                  << (it).name() << endl
0352                  << "result.mailboxList[" << i << "].addrSpec.localPart:\n"
0353                  << (it).addrSpec().localPart << endl
0354                  << "result.mailboxList[" << i << "].addrSpec.domain:\n"
0355                  << (it).addrSpec().domain
0356                  << endl;
0357             ++i;
0358         }
0359     }
0360     break;
0361     case 15: {
0362         // address-list
0363         KMime::Types::AddressList result;
0364         bool ok = parseAddressList(iit, iend, result, withCRLF);
0365 
0366         cout << (ok ? "OK" : "BAD") << endl;
0367         int j = 0;
0368         for (const auto &jt : std::as_const(result)) {
0369             cout << "result[" << j << "].displayName:\n"
0370                  << (jt).displayName
0371                  << endl;
0372             int i = 0;
0373             const auto mailboxList = (jt).mailboxList;
0374             for (const auto &it : mailboxList) {
0375                 cout << "result[" << j << "].mailboxList[" << i << "].displayName:\n"
0376                      << (it).name() << endl
0377                      << "result[" << j << "].mailboxList[" << i << "].addrSpec.localPart:\n"
0378                      << (it).addrSpec().localPart << endl
0379                      << "result[" << j << "].mailboxList[" << i << "].addrSpec.domain:\n"
0380                      << (it).addrSpec().domain
0381                      << endl;
0382                 ++i;
0383             }
0384             ++j;
0385         }
0386     }
0387     break;
0388     case 16: {
0389         // parameter-list
0390         QMap<QString, QString> result;
0391         bool ok = parseParameterList(iit, iend, result, withCRLF);
0392 
0393         cout << (ok ? "OK" : "BAD") << endl
0394              << "result: " << result.count() << " parameters:"
0395              << endl;
0396         int i = 0;
0397         for (QMap<QString, QString>::Iterator it = result.begin() ;
0398                 it != result.end() ; ++it, ++i) {
0399             cout << "result[" << i << "].key() (attribute):\n"
0400                  << it.key() << endl
0401                  << "result[" << i << "].data() (value):\n"
0402                  << it.value()
0403                  << endl;
0404         }
0405     }
0406     break;
0407     case 17: {
0408         // time
0409         int hour;
0410         int mins;
0411         int secs;
0412         long int secsEastOfGMT;
0413         bool timeZoneKnown = true;
0414 
0415         bool ok = parseTime(iit, iend, hour, mins, secs,
0416                             secsEastOfGMT, timeZoneKnown, withCRLF);
0417 
0418         cout << (ok ? "OK" : "BAD") << endl
0419              << "result.hour: " << hour << endl
0420              << "result.mins: " << mins << endl
0421              << "result.secs: " << secs << endl
0422              << "result.secsEastOfGMT: " << secsEastOfGMT << endl
0423              << "result.timeZoneKnown: " << timeZoneKnown
0424              << endl;
0425     }
0426     break;
0427     case 18: {
0428         // date-time
0429         QDateTime result;
0430         bool ok =  parseDateTime(iit, iend, result, withCRLF);
0431         time_t timet = result.toSecsSinceEpoch();
0432 
0433         cout << (ok ? "OK" : "BAD") << endl
0434              << "result.time (in local timezone): " << ctime(&timet)
0435              << "result.secsEastOfGMT: " << result.offsetFromUtc()
0436              << " (" << result.offsetFromUtc() / 60 << "mins)"
0437              << endl;
0438     }
0439     break;
0440     default:
0441         assert(0);
0442     }
0443 }