/*
 * Copyright (C) 2005  Justin Karneges <justin@affinix.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 */

/* mozilla certdata converter.  adapted from the debian ruby script */

#include <QtCrypto>

#include <QCoreApplication>
#include <QFile>
#include <QRegularExpression>
#include <QTextStream>

QStringList splitWithQuotes(const QString &in, char c);

int main(int argc, char **argv)
{
    QCA::Initializer qcaInit;
    QCoreApplication app(argc, argv);

    if (argc < 3) {
        printf("usage: mozcerts [certdata.txt] [outfile.pem]\n");
        return 0;
    }

    QFile infile(QString::fromLocal8Bit(argv[1]));
    if (!infile.open(QFile::ReadOnly)) {
        fprintf(stderr, "Error opening input file\n");
        return 1;
    }

    QFile outfile(QString::fromLocal8Bit(argv[2]));
    if (!outfile.open(QFile::WriteOnly | QFile::Truncate)) {
        fprintf(stderr, "Error opening output file\n");
        return 1;
    }

    int         count = 0;
    QString     name;
    QTextStream ts(&infile);
    while (!ts.atEnd()) {
        QString line = ts.readLine();
        if (line.startsWith(QLatin1Char('#')))
            continue;
        line = line.trimmed();
        if (line.isEmpty())
            continue;

        if (line.startsWith(QLatin1String("CKA_LABEL"))) {
            const QStringList list = splitWithQuotes(line, ' ');
            if (list.count() != 3)
                continue;

            name = list[2];
            // make an output filename based on the name
            // outname = name.replace(QRegExp("\\/"), "_")
            //	.replace(QRegExp("\\s+"), "_")
            //	.replace(QRegExp("[()]"), "=")
            //	.replace(QRegExp(","), "_") + ".pem";
            continue;
        } else if (line == QLatin1String("CKA_VALUE MULTILINE_OCTAL")) {
            QByteArray buf;
            while (!ts.atEnd()) {
                line = ts.readLine().trimmed();
                if (line == QLatin1String("END"))
                    break;
                static const QRegularExpression rx(QStringLiteral("\\\\([0-3][0-7][0-7])"));
                int                             pos   = 0;
                QRegularExpressionMatch         match = rx.match(line, pos);
                while (match.hasMatch()) {
                    QString str = match.captured(1);
                    uchar   c   = str.toInt(nullptr, 8);
                    buf.append(c);
                    pos += match.capturedLength();
                    match = rx.match(line, pos);
                }
            }

            printf(">> [%s], %d bytes\n", qPrintable(name), int(buf.size()));

            QTextStream ts(&outfile);
            ts << "-----BEGIN CERTIFICATE-----" << '\n';
            QCA::Base64 enc;
            enc.setLineBreaksEnabled(true);
            enc.setLineBreaksColumn(64);
            ts << enc.arrayToString(buf) << '\n';
            ts << "-----END CERTIFICATE-----" << '\n';

            ++count;
        }
    }
    printf("Wrote %d certs to [%s]\n", count, argv[2]);

    return 0;
}

int find_notchar(const QString &str, char c, int offset)
{
    for (int n = offset; n < str.length(); ++n) {
        if (str[n] != QLatin1Char(c))
            return n;
    }
    return -1;
}

QStringList splitWithQuotes(const QString &in, char c)
{
    QStringList result;
    int         at = 0;
    if (in[at] == QLatin1Char(c))
        at = find_notchar(in, c, at);
    while (at != -1) {
        bool    quote = false;
        int     end;
        QString str;
        if (in[at] == QLatin1Char('\"')) {
            quote = true;
            ++at;
            end = in.indexOf(QLatin1Char('\"'), at);
            if (end == -1)
                break;
        } else
            end = in.indexOf(QLatin1Char(c), at);

        if (end != -1)
            str = in.mid(at, end - at);
        else
            str = in.mid(at);

        if (!str.isEmpty())
            result += str;

        if (quote)
            end = in.indexOf(QLatin1Char(c), end);

        if (end != -1)
            at = find_notchar(in, c, end);
        else
            at = -1;
    }
    return result;
}
