QGpgME  2.1.0-unknown
Qt API for GpgME
threadedjobmixin.h
1 /*
2  threadedjobmixin.h
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 #ifndef __QGPGME_THREADEDJOBMIXING_H__
36 #define __QGPGME_THREADEDJOBMIXING_H__
37 
38 #include <QMutex>
39 #include <QMutexLocker>
40 #include <QThread>
41 #include <QString>
42 #include <QIODevice>
43 
44 #include <gpgme++/context.h>
45 #include <gpgme++/interfaces/progressprovider.h>
46 
47 #include "job.h"
48 
49 #include <cassert>
50 #include <functional>
51 
52 namespace QGpgME
53 {
54 namespace _detail
55 {
56 
57 QString audit_log_as_html(GpgME::Context *ctx, GpgME::Error &err);
58 
60 {
61  const QList<QByteArray> m_list;
62  mutable const char **m_patterns;
63 public:
64  explicit PatternConverter(const QByteArray &ba);
65  explicit PatternConverter(const QString &s);
66  explicit PatternConverter(const QList<QByteArray> &lba);
67  explicit PatternConverter(const QStringList &sl);
69 
70  const char **patterns() const;
71 };
72 
74 {
75  QObject *const m_object;
76  QThread *const m_thread;
77 public:
78  ToThreadMover(QObject *o, QThread *t) : m_object(o), m_thread(t) {}
79  ToThreadMover(QObject &o, QThread *t) : m_object(&o), m_thread(t) {}
80  ToThreadMover(const std::shared_ptr<QObject> &o, QThread *t) : m_object(o.get()), m_thread(t) {}
81  ~ToThreadMover()
82  {
83  if (m_object && m_thread) {
84  m_object->moveToThread(m_thread);
85  }
86  }
87 };
88 
89 template <typename T_result>
90 class Thread : public QThread
91 {
92 public:
93  explicit Thread(QObject *parent = nullptr) : QThread(parent) {}
94 
95  void setFunction(const std::function<T_result()> &function)
96  {
97  const QMutexLocker locker(&m_mutex);
98  m_function = function;
99  }
100 
101  bool hasFunction()
102  {
103  const QMutexLocker locker(&m_mutex);
104  return static_cast<bool>(m_function);
105  }
106 
107  T_result result() const
108  {
109  const QMutexLocker locker(&m_mutex);
110  return m_result;
111  }
112 
113 private:
114  void run() override {
115  const QMutexLocker locker(&m_mutex);
116  m_result = m_function();
117  }
118 private:
119  mutable QMutex m_mutex;
120  std::function<T_result()> m_function;
121  T_result m_result;
122 };
123 
124 template <typename T_base, typename T_private = void, typename T_result = std::tuple<GpgME::Error, QString, GpgME::Error>>
125 class ThreadedJobMixin : public T_base, public GpgME::ProgressProvider
126 {
127 public:
129  typedef T_result result_type;
130 
131  void run()
132  {
133  Q_ASSERT(m_thread.hasFunction() && "Call setWorkerFunction() before run()");
134  m_thread.start();
135  }
136 
137 protected:
138  static_assert(std::tuple_size<T_result>::value > 2,
139  "Result tuple too small");
140  static_assert(std::is_same <
141  typename std::tuple_element <
142  std::tuple_size<T_result>::value - 2,
143  T_result
144  >::type,
145  QString
146  >::value,
147  "Second to last result type not a QString");
148  static_assert(std::is_same <
149  typename std::tuple_element <
150  std::tuple_size<T_result>::value - 1,
151  T_result
152  >::type,
153  GpgME::Error
154  >::value,
155  "Last result type not a GpgME::Error");
156 
157  // Constructor used if a private class is specified (i.e. T_private is not void)
158  template<typename T_private_ = T_private,
159  std::enable_if_t<!std::is_void_v<T_private_>, bool> = true>
160  explicit ThreadedJobMixin(GpgME::Context *ctx)
161  : T_base(std::make_unique<T_private>(), nullptr), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
162  {
163  }
164 
165  // Constructor used if no private class is specified (i.e. T_private is void)
166  template<typename T_private_ = T_private,
167  std::enable_if_t<std::is_void_v<T_private_>, bool> = true>
168  explicit ThreadedJobMixin(GpgME::Context *ctx)
169  : T_base(nullptr), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
170  {
171  }
172 
173  void lateInitialization()
174  {
175  assert(m_ctx);
176  QObject::connect(&m_thread, &QThread::finished, this,
177  &mixin_type::slotFinished);
178  m_ctx->setProgressProvider(this);
179  QGpgME::g_context_map.insert(this, m_ctx.get());
180  }
181 
183  {
184  QGpgME::g_context_map.remove(this);
185  }
186 
187  template <typename T_binder>
188  void setWorkerFunction(const T_binder &func)
189  {
190  m_thread.setFunction([this, func]() { return func(this->context()); });
191  }
192 
193 public:
194  template <typename T_binder>
195  void run(const T_binder &func)
196  {
197  m_thread.setFunction(std::bind(func, this->context()));
198  m_thread.start();
199  }
200  template <typename T_binder>
201  void run(const T_binder &func, const std::shared_ptr<QIODevice> &io)
202  {
203  if (io) {
204  io->moveToThread(&m_thread);
205  }
206  // the arguments passed here to the functor are stored in a QThread, and are not
207  // necessarily destroyed (living outside the UI thread) at the time the result signal
208  // is emitted and the signal receiver wants to clean up IO devices.
209  // To avoid such races, we pass std::weak_ptr's to the functor.
210  m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io)));
211  m_thread.start();
212  }
213  template <typename T_binder>
214  void run(const T_binder &func, const std::shared_ptr<QIODevice> &io1, const std::shared_ptr<QIODevice> &io2)
215  {
216  if (io1) {
217  io1->moveToThread(&m_thread);
218  }
219  if (io2) {
220  io2->moveToThread(&m_thread);
221  }
222  // the arguments passed here to the functor are stored in a QThread, and are not
223  // necessarily destroyed (living outside the UI thread) at the time the result signal
224  // is emitted and the signal receiver wants to clean up IO devices.
225  // To avoid such races, we pass std::weak_ptr's to the functor.
226  m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io1), std::weak_ptr<QIODevice>(io2)));
227  m_thread.start();
228  }
229 
230 protected:
231  GpgME::Context *context() const
232  {
233  return m_ctx.get();
234  }
235 
236  virtual void resultHook(const result_type &) {}
237 
238  void slotFinished()
239  {
240  const T_result r = m_thread.result();
241  m_auditLog = std::get < std::tuple_size<T_result>::value - 2 > (r);
242  m_auditLogError = std::get < std::tuple_size<T_result>::value - 1 > (r);
243  resultHook(r);
244  Q_EMIT this->done();
245  doEmitResult(r);
246  this->deleteLater();
247  }
248  void slotCancel() override {
249  if (m_ctx)
250  {
251  m_ctx->cancelPendingOperation();
252  }
253  }
254  QString auditLogAsHtml() const override
255  {
256  return m_auditLog;
257  }
258  GpgME::Error auditLogError() const override
259  {
260  return m_auditLogError;
261  }
262  void showProgress(const char *what,
263  int type, int current, int total) override {
264  QMetaObject::invokeMethod(this, [this, current, total]() {
265  Q_EMIT this->jobProgress(current, total);
266  }, Qt::QueuedConnection);
267  const QString what_ = QString::fromUtf8(what);
268  QMetaObject::invokeMethod(this, [this, what_, type, current, total]() {
269  Q_EMIT this->rawProgress(what_, type, current, total);
270  }, Qt::QueuedConnection);
271  QMetaObject::invokeMethod(this, [this, what_, current, total]() {
272  QT_WARNING_PUSH
273  QT_WARNING_DISABLE_DEPRECATED
274  Q_EMIT this->progress(what_, current, total);
275  QT_WARNING_POP
276  }, Qt::QueuedConnection);
277  }
278 private:
279  template <typename T1, typename T2>
280  void doEmitResult(const std::tuple<T1, T2> &tuple)
281  {
282  Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple));
283  }
284 
285  template <typename T1, typename T2, typename T3>
286  void doEmitResult(const std::tuple<T1, T2, T3> &tuple)
287  {
288  Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));
289  }
290 
291  template <typename T1, typename T2, typename T3, typename T4>
292  void doEmitResult(const std::tuple<T1, T2, T3, T4> &tuple)
293  {
294  Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple));
295  }
296 
297  template <typename T1, typename T2, typename T3, typename T4, typename T5>
298  void doEmitResult(const std::tuple<T1, T2, T3, T4, T5> &tuple)
299  {
300  Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple));
301  }
302 
303 private:
304  std::shared_ptr<GpgME::Context> m_ctx;
305  Thread<T_result> m_thread;
306  QString m_auditLog;
307  GpgME::Error m_auditLogError;
308 };
309 
310 }
311 }
312 
313 #endif /* __QGPGME_THREADEDJOBMIXING_H__ */
Definition: threadedjobmixin.h:59
Definition: threadedjobmixin.h:73
Definition: threadedjobmixin.h:90
Definition: threadedjobmixin.h:125
Definition: abstractimportjob.h:48