qt: Handle encoding for diagnostics
[gpgme.git] / lang / qt / src / threadedjobmixin.cpp
1 /*
2     threadedjobmixin.cpp
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2008 Klarälvdalens Datakonsult AB
6     Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
7     Software engineering by Intevation GmbH
8
9     QGpgME is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.
13
14     QGpgME is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17     General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
23     In addition, as a special exception, the copyright holders give
24     permission to link the code of this program with any edition of
25     the Qt library by Trolltech AS, Norway (or with modified versions
26     of Qt that use the same license as Qt), and distribute linked
27     combinations including the two.  You must obey the GNU General
28     Public License in all respects for all of the code used other than
29     Qt.  If you modify this file, you may extend this exception to
30     your version of the file, but you are not obligated to do so.  If
31     you do not wish to do so, delete this exception statement from
32     your version.
33 */
34
35 #ifdef HAVE_CONFIG_H
36  #include "config.h"
37 #endif
38
39 #include "threadedjobmixin.h"
40
41 #include "dataprovider.h"
42
43 #include "data.h"
44
45 #include <QString>
46 #include <QStringList>
47 #include <QByteArray>
48
49
50 #include <algorithm>
51 #include <iterator>
52
53 using namespace QGpgME;
54 using namespace GpgME;
55
56 #ifdef Q_OS_WIN
57 #include <windows.h>
58
59 static QString fromEncoding (unsigned int src_encoding, const char *data)
60 {
61     int n = MultiByteToWideChar(src_encoding, 0, data, -1, NULL, 0);
62     if (n < 0) {
63         return QString();
64     }
65
66     wchar_t *result = (wchar_t *) malloc ((n+1) * sizeof *result);
67
68     n = MultiByteToWideChar(src_encoding, 0, data, -1, result, n);
69     if (n < 0) {
70         free(result);
71         return QString();
72     }
73     const auto ret = QString::fromWCharArray(result, n);
74     free(result);
75     return ret;
76 }
77 #endif
78
79 static QString stringFromGpgOutput(const QByteArray &ba)
80 {
81 #ifdef Q_OS_WIN
82     /* Qt on Windows uses GetACP while GnuPG prefers
83      * GetConsoleOutputCP.
84      *
85      * As we are not a console application GetConsoleOutputCP
86      * usually returns 0.
87      * From experience the closest thing that let's us guess
88      * what GetConsoleOutputCP returns for a console application
89      * it appears to be the OEMCP.
90      */
91     unsigned int cpno = GetConsoleOutputCP ();
92     if (!cpno) {
93         cpno = GetOEMCP();
94     }
95     if (!cpno) {
96         cpno = GetACP();
97     }
98     if (!cpno) {
99         return QString();
100     }
101
102     return fromEncoding(cpno, ba.constData());
103 #else
104     return QString::fromLocal8Bit(ba);
105 #endif
106 }
107
108 static QString markupDiagnostics(const QString &data)
109 {
110     // First ensure that we don't have html in the diag.
111     QString ret = QStringLiteral("<pre>%1</pre>").arg(data.toHtmlEscaped());
112
113     return ret;
114 }
115
116 static const unsigned int CMSAuditLogFlags = Context::AuditLogWithHelp | Context::HtmlAuditLog;
117 static const unsigned int OpenPGPAuditLogFlags = Context::DiagnosticAuditLog;
118
119 QString _detail::audit_log_as_html(Context *ctx, GpgME::Error &err)
120 {
121     assert(ctx);
122     QGpgME::QByteArrayDataProvider dp;
123     Data data(&dp);
124     assert(!data.isNull());
125
126     if (ctx->protocol() == OpenPGP) {
127         if ((err = ctx->getAuditLog(data, OpenPGPAuditLogFlags))) {
128             return QString::fromLocal8Bit(err.asString());
129         }
130         const QByteArray ba = dp.data();
131         return markupDiagnostics(stringFromGpgOutput(ba));
132     }
133
134     if (ctx->protocol() == CMS) {
135         if ((err = ctx->lastError()) || (err = ctx->getAuditLog(data, CMSAuditLogFlags))) {
136             return QString::fromLocal8Bit(err.asString());
137         }
138         const QByteArray ba = dp.data();
139         return QString::fromUtf8(ba.data(), ba.size());
140     }
141
142     return QStringLiteral("Unsupported protocol for Audit Log");
143 }
144
145 static QList<QByteArray> from_sl(const QStringList &sl)
146 {
147     QList<QByteArray> result;
148     Q_FOREACH (const QString &str, sl) {
149         result.append(str.toUtf8());
150     }
151
152 #if 0
153     std::transform(sl.begin(), sl.end(), std::back_inserter(result),
154                    mem_fn(static_cast<QByteArray()const>(&QString::toUtf8)));
155 #endif
156     return result;
157 }
158
159 static QList<QByteArray> single(const QByteArray &ba)
160 {
161     QList<QByteArray> result;
162     result.push_back(ba);
163     return result;
164 }
165
166 _detail::PatternConverter::PatternConverter(const QByteArray &ba)
167     : m_list(single(ba)), m_patterns(0) {}
168 _detail::PatternConverter::PatternConverter(const QString &s)
169     : m_list(single(s.toUtf8())), m_patterns(0) {}
170 _detail::PatternConverter::PatternConverter(const QList<QByteArray> &lba)
171     : m_list(lba), m_patterns(0) {}
172 _detail::PatternConverter::PatternConverter(const QStringList &sl)
173     :  m_list(from_sl(sl)), m_patterns(0) {}
174
175 const char **_detail::PatternConverter::patterns() const
176 {
177     if (!m_patterns) {
178         m_patterns = new const char *[ m_list.size() + 1 ];
179         const char **end = std::transform(m_list.begin(), m_list.end(), m_patterns,
180                                           std::mem_fn(&QByteArray::constData));
181         *end = 0;
182     }
183     return m_patterns;
184 }
185
186 _detail::PatternConverter::~PatternConverter()
187 {
188     delete [] m_patterns;
189 }