File indexing completed on 2024-04-21 14:56:12

0001 /* This file is part of the KDE libraries
0002     Copyright (C) 2000,2001 Dawit Alemayehu <adawit@kde.org>
0003 
0004     This library is free software; you can redistribute it and/or
0005     modify it under the terms of the GNU Library General Public
0006     License as published by the Free Software Foundation; either
0007     version 2 of the License, or (at your option) any later version.
0008 
0009     This library is distributed in the hope that it will be useful,
0010     but WITHOUT ANY WARRANTY; without even the implied warranty of
0011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012     Library General Public License for more details.
0013 
0014     You should have received a copy of the GNU Library General Public License
0015     along with this library; see the file COPYING.LIB.  If not, write to
0016     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017     Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #undef QT_NO_CAST_FROM_ASCII
0021 
0022 #include <unistd.h>
0023 
0024 #include <iostream>
0025 #include <QCoreApplication>
0026 #include <QBuffer>
0027 #include <QFile>
0028 #include <QDateTime>
0029 
0030 #include <kdebug.h>
0031 #include <klocalizedstring.h>
0032 #include <kcmdlineargs.h>
0033 
0034 #include <kcodecs.h>
0035 #include <kmd5.h>
0036 
0037 using namespace std;
0038 
0039 #define TEST_BLOCK_LEN 1000             // Length of test blocks.
0040 #define TEST_BLOCK_COUNT 10000          // Number of test blocks.
0041 #define MAX_READ_BUF_SIZE 8192
0042 
0043 enum Codec {
0044     Unspecified = 0,
0045     Base64Encode,
0046     Base64Decode,
0047     UUEncode,
0048     UUDecode,
0049     QPEncode,
0050     QPDecode
0051 };
0052 
0053 void MD5_timeTrial();
0054 void MD5_testSuite();
0055 void testCodec(const char *, Codec, bool);
0056 void MD5_verify(const char *, const char *, bool);
0057 void MD5_file(const char *, bool rawOutput = false);
0058 void MD5_string(const char *, const char *expected = nullptr, bool rawOutput = false);
0059 
0060 long readContent(const QFile &f, long count, QByteArray &buf)
0061 {
0062     long result;
0063     int old_size;
0064 
0065     old_size = buf.size();
0066     buf.resize(old_size + count);
0067 
0068     result = read(f.handle(), buf.data() + old_size, count);
0069 
0070     if (result > 0 && result < count) {
0071         buf.resize(old_size + result);
0072     } else if (result == 0) {
0073         buf.resize(old_size);
0074     } else if (result == -1) {
0075         kError() << "Could not read the file!" << endl;
0076     }
0077 
0078     return result;
0079 }
0080 
0081 void testCodec(const char *msg, Codec type, bool isFile)
0082 {
0083     QByteArray output;
0084 
0085     if (isFile) {
0086         int count;
0087         QByteArray data;
0088 
0089         QFile f(QFile::encodeName(msg));
0090 
0091         if (!f.exists()) {
0092             kError() << "Could not find: " << qPrintable(f.fileName()) << endl;
0093             return;
0094         }
0095 
0096         if (!f.open(QIODevice::ReadOnly)) {
0097             f.close();
0098             kError() << "Could not open: " << qPrintable(f.fileName()) << endl;
0099             return;
0100         }
0101 
0102         // Read contents of file...
0103         count = 0;
0104 
0105         while ((count = readContent(f, MAX_READ_BUF_SIZE, data)) > 0) {
0106             ;
0107         }
0108 
0109         // Error! Exit!
0110         if (count == -1) {
0111             kError() << "Error reading from: " << qPrintable(f.fileName()) << endl;
0112             f.close();
0113             return;
0114         }
0115 
0116         f.close();
0117 
0118         // Perform the requested encoding or decoding...
0119         switch (type) {
0120         case Base64Encode:
0121             KCodecs::base64Encode(data, output, true);
0122             break;
0123         case Base64Decode:
0124             KCodecs::base64Decode(data, output);
0125             break;
0126         case UUEncode:
0127             KCodecs::uuencode(data, output);
0128             break;
0129         case UUDecode:
0130             KCodecs::uudecode(data, output);
0131             break;
0132         case QPEncode:
0133             KCodecs::quotedPrintableEncode(data, output, true);
0134             break;
0135         case QPDecode:
0136             KCodecs::quotedPrintableDecode(data, output);
0137             break;
0138         default:
0139             break;
0140         }
0141 
0142         cout << "Result: " << endl << output.data() << endl;
0143     } else {
0144         QByteArray result;
0145 
0146         memcpy(output.data(), msg, strlen(msg));
0147 
0148         switch (type) {
0149         case Base64Encode:
0150             result = KCodecs::base64Encode(output);
0151             break;
0152         case Base64Decode:
0153             result = KCodecs::base64Decode(output);
0154             break;
0155         case UUEncode:
0156             result = KCodecs::uuencode(output);
0157             break;
0158         case UUDecode:
0159             result = KCodecs::uudecode(output);
0160             break;
0161         case QPEncode:
0162             result = KCodecs::quotedPrintableEncode(output);
0163             break;
0164         case QPDecode:
0165             result = KCodecs::quotedPrintableDecode(output);
0166             break;
0167         default:
0168             break;
0169         }
0170         cout << result.data() << endl;
0171     }
0172 }
0173 
0174 void MD5_timeTrial()
0175 {
0176     KMD5 context;
0177 
0178     QDateTime endTime;
0179     QDateTime startTime;
0180 
0181     quint8 block[TEST_BLOCK_LEN];
0182     quint32 i;
0183 
0184     cout << "Timing test. Digesting " << TEST_BLOCK_COUNT << " blocks of "
0185          << TEST_BLOCK_LEN << "-byte..." << endl;
0186 
0187     // Initialize block
0188     for (i = 0; i < TEST_BLOCK_LEN; ++i) {
0189         block[i] = (quint8)(i & 0xff);
0190     }
0191 
0192     // Start timer
0193     startTime = QDateTime::currentDateTime();
0194 
0195     // Digest blocks
0196     for (i = 0; i < TEST_BLOCK_COUNT; ++i) {
0197         context.update(block, TEST_BLOCK_LEN);
0198     }
0199 
0200     // Stop timer
0201     endTime = QDateTime::currentDateTime();
0202 
0203     long duration = startTime.secsTo(endTime);
0204     long speed;
0205     if (duration) {
0206         speed = (TEST_BLOCK_LEN * (TEST_BLOCK_COUNT / duration));
0207     } else {
0208         speed = TEST_BLOCK_COUNT;
0209     }
0210 
0211     cout << "Result: " << endl;
0212     cout << "  Time   = " << duration << " seconds" << endl;
0213     cout << "  Speed  = " << speed << " bytes/second" << endl;
0214     cout << "  Digest = " << context.hexDigest().data() << endl;
0215 }
0216 
0217 void MD5_testSuite()
0218 {
0219     cout << "MD5 preset test suite as defined in RFC 1321:" << endl;
0220     MD5_string("", "d41d8cd98f00b204e9800998ecf8427e");
0221     MD5_string("a", "0cc175b9c0f1b6a831c399e269772661");
0222     MD5_string("abc", "900150983cd24fb0d6963f7d28e17f72");
0223     MD5_string("message digest", "f96b697d7cb7938d525a2f31aaf161d0");
0224     MD5_string("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b");
0225     MD5_string("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
0226                "d174ab98d277d9f5a5611c2c9f419d9f");
0227     MD5_string("12345678901234567890123456789012345678901234567890123456789012"
0228                "345678901234567890", "57edf4a22be3c955ac49da2e2107b67a");
0229 }
0230 
0231 void MD5_verify(const char *input, const char *digest, bool isFile)
0232 {
0233     bool result;
0234     KMD5 context;
0235 
0236     if (!isFile) {
0237         context.update(QByteArray(input));
0238         result = context.verify(digest);
0239         cout << "Input string: " << input << endl;
0240     } else {
0241         QFile f(input);
0242 
0243         if (!f.open(QIODevice::ReadOnly)) {
0244             f.close();
0245             kFatal() << "Cannot open file for reading!";
0246         }
0247 
0248         result = context.verify(digest);
0249         f.close();
0250 
0251         cout << "Input filename: " << input << endl;
0252     }
0253 
0254     cout << "Calculated Digest = " <<  context.hexDigest().data() << endl;
0255     cout << "Supplied Digest   = " << digest << endl;
0256     cout << "Matches: " << (result ? "TRUE" : "FALSE") << endl;
0257 }
0258 
0259 void MD5_file(const char *filename, bool rawOutput)
0260 {
0261     QFile f(QFile::encodeName(filename));
0262 
0263     if (!f.open(QIODevice::ReadOnly)) {
0264         f.close();
0265         kError() << "(" << filename << ") cannot be opened!" << endl;
0266         return;
0267     }
0268 
0269     KMD5 context;
0270     context.update(f);
0271 
0272     if (rawOutput) {
0273         cout << "MD5 (" << filename << ") = " << context.rawDigest() << endl;
0274     } else {
0275         cout << "MD5 (" << filename << ") = " << context.hexDigest().data() << endl;
0276     }
0277 
0278     f.close();
0279 }
0280 
0281 void MD5_string(const char *input, const char *expected, bool rawOutput)
0282 {
0283     KMD5 context;
0284     context.update(QByteArray(input));
0285 
0286     cout << "Checking MD5 for: " << input << endl;
0287 
0288     if (rawOutput) {
0289         cout << "Result: " << context.rawDigest() << endl;
0290     } else {
0291         cout << "Result: " << context.hexDigest().data() << endl;
0292     }
0293 
0294     if (expected) {
0295         cout << "Expected: " << expected << endl;
0296         cout << "Status: " << context.verify(expected) << endl;
0297     }
0298 }
0299 
0300 int main(int argc, char *argv[])
0301 {
0302     KCmdLineOptions options;
0303     options.add("c <digest>", ki18n("compare <digest> with the calculated digest for a string or file."));
0304     options.add("d", ki18n("decode the given string or file using base64"));
0305     options.add("e", ki18n("encode the given string or file using base64"));
0306     options.add("f", ki18n("the filename to be used as input"), "default");
0307     options.add("p", ki18n("encode the given string or file using quoted-printable"));
0308     options.add("q", ki18n("decode the given string or file using quoted-printable"));
0309     options.add("r", ki18n("calculate the raw md5 for the given string or file"));
0310     options.add("s", ki18n("the string to be used as input"));
0311     options.add("t", ki18n("perform a timed message-digest test"));
0312     options.add("u", ki18n("uuencode the given string or file"));
0313     options.add("x", ki18n("uudecode the given string or file"));
0314     options.add("z", ki18n("run a preset message-digest test"));
0315     options.add("+command", ki18n("[input1, input2,...]"));
0316 
0317     KCmdLineArgs::init(argc, argv, "kmdcodectest", nullptr,
0318                        ki18n("KMDCodecTest"), "1.0",
0319                        ki18n("Unit test for md5, base64 encode/decode "
0320                              "and uuencode/decode facilities"));
0321     KCmdLineArgs::addCmdLineOptions(options);
0322     KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
0323     int count = args->count();
0324 
0325     //QCoreApplication app;
0326 
0327     if (!count) {
0328         if (args->isSet("t")) {
0329             MD5_timeTrial();
0330         } else if (args->isSet("z")) {
0331             MD5_testSuite();
0332         } else {
0333             args->usage();
0334         }
0335     } else {
0336         bool isVerify = args->isSet("c");
0337         bool isString = args->isSet("s");
0338         bool isFile = args->isSet("f");
0339         Codec type = Unspecified;
0340         if (args->isSet("d")) {
0341             type = Base64Decode;
0342         } else if (args->isSet("e")) {
0343             type = Base64Encode;
0344         } else if (args->isSet("u")) {
0345             type = UUEncode;
0346         } else if (args->isSet("x")) {
0347             type = UUDecode;
0348         } else if (args->isSet("p")) {
0349             type = QPEncode;
0350         } else if (args->isSet("q")) {
0351             type = QPDecode;
0352         }
0353         if (isVerify) {
0354             const char *opt = args->getOption("c").toLocal8Bit().data();
0355             for (int i = 0; i < count; i++) {
0356                 MD5_verify(args->arg(i).toLocal8Bit().constData(), opt, (isString || !isFile));
0357             }
0358         } else {
0359             for (int i = 0; i < count; i++) {
0360                 if (type != Unspecified) {
0361                     testCodec(args->arg(i).toLocal8Bit().constData(), type, isFile);
0362                 } else {
0363                     if (isString) {
0364                         MD5_string(args->arg(i).toLocal8Bit().constData(), nullptr, args->isSet("r"));
0365                     } else {
0366                         MD5_file(args->arg(i).toLocal8Bit().constData(), args->isSet("r"));
0367                     }
0368                 }
0369             }
0370         }
0371     }
0372     args->clear();
0373     return (0);
0374 }