1/* poppler-form.h: qt interface to poppler
2 * Copyright (C) 2007-2008, 2011, Pino Toscano <pino@kde.org>
3 * Copyright (C) 2008, 2011, 2012, 2015-2023 Albert Astals Cid <aacid@kde.org>
4 * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org>
5 * Copyright (C) 2012, Adam Reichold <adamreichold@myopera.com>
6 * Copyright (C) 2016, Hanno Meyer-Thurow <h.mth@web.de>
7 * Copyright (C) 2017, Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
8 * Copyright (C) 2018, Andre Heinecke <aheinecke@intevation.de>
9 * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
10 * Copyright (C) 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
11 * Copyright (C) 2018, 2020, 2021 Oliver Sander <oliver.sander@tu-dresden.de>
12 * Copyright (C) 2019 João Netto <joaonetto901@gmail.com>
13 * Copyright (C) 2020 David García Garzón <voki@canvoki.net>
14 * Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
15 * Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
16 * Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
17 * Copyright (C) 2021 Theofilos Intzoglou <int.teo@gmail.com>
18 * Copyright (C) 2022 Alexander Sulfrian <asulfrian@zedat.fu-berlin.de>
19 * Copyright (C) 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2, or (at your option)
24 * any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
34 */
35
36#include "poppler-form.h"
37
38#include <config.h>
39
40#include <QtCore/QSizeF>
41#include <QUrl>
42
43#include <Form.h>
44#include <Object.h>
45#include <Link.h>
46#include <SignatureInfo.h>
47#include <CertificateInfo.h>
48#include <CryptoSignBackend.h>
49#ifdef ENABLE_NSS3
50# include <NSSCryptoSignBackend.h>
51#endif
52
53#include "poppler-page-private.h"
54#include "poppler-private.h"
55#include "poppler-annotation-helper.h"
56
57#include <cmath>
58#include <cctype>
59
60namespace {
61
62Qt::Alignment formTextAlignment(::FormWidget *fm)
63{
64 Qt::Alignment qtalign = Qt::AlignLeft;
65 switch (fm->getField()->getTextQuadding()) {
66 case VariableTextQuadding::centered:
67 qtalign = Qt::AlignHCenter;
68 break;
69 case VariableTextQuadding::rightJustified:
70 qtalign = Qt::AlignRight;
71 break;
72 case VariableTextQuadding::leftJustified:
73 qtalign = Qt::AlignLeft;
74 }
75 return qtalign;
76}
77
78}
79
80namespace Poppler {
81
82FormFieldIcon::FormFieldIcon(FormFieldIconData *data) : d_ptr(data) { }
83
84FormFieldIcon::FormFieldIcon(const FormFieldIcon &ffIcon)
85{
86 d_ptr = new FormFieldIconData;
87 d_ptr->icon = ffIcon.d_ptr->icon;
88}
89
90FormFieldIcon &FormFieldIcon::operator=(const FormFieldIcon &ffIcon)
91{
92 if (this != &ffIcon) {
93 delete d_ptr;
94 d_ptr = nullptr;
95
96 d_ptr = new FormFieldIconData;
97 *d_ptr = *ffIcon.d_ptr;
98 }
99
100 return *this;
101}
102
103FormFieldIcon::~FormFieldIcon()
104{
105 delete d_ptr;
106}
107
108FormField::FormField(std::unique_ptr<FormFieldData> dd) : m_formData(std::move(dd))
109{
110 if (m_formData->page) {
111 const int rotation = m_formData->page->getRotate();
112 // reading the coords
113 double left, top, right, bottom;
114 m_formData->fm->getRect(x1: &left, y1: &bottom, x2: &right, y2: &top);
115 // build a normalized transform matrix for this page at 100% scale
116 GfxState gfxState(72.0, 72.0, m_formData->page->getCropBox(), rotation, true);
117 const double *gfxCTM = gfxState.getCTM();
118 double MTX[6];
119 double pageWidth = m_formData->page->getCropWidth();
120 double pageHeight = m_formData->page->getCropHeight();
121 // landscape and seascape page rotation: be sure to use the correct (== rotated) page size
122 if (((rotation / 90) % 2) == 1) {
123 qSwap(value1&: pageWidth, value2&: pageHeight);
124 }
125 for (int i = 0; i < 6; i += 2) {
126 MTX[i] = gfxCTM[i] / pageWidth;
127 MTX[i + 1] = gfxCTM[i + 1] / pageHeight;
128 }
129 QPointF topLeft;
130 XPDFReader::transform(M: MTX, x: qMin(a: left, b: right), y: qMax(a: top, b: bottom), res&: topLeft);
131 QPointF bottomRight;
132 XPDFReader::transform(M: MTX, x: qMax(a: left, b: right), y: qMin(a: top, b: bottom), res&: bottomRight);
133 m_formData->box = QRectF(topLeft, QSizeF(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y()));
134 }
135}
136
137FormField::~FormField() = default;
138
139QRectF FormField::rect() const
140{
141 return m_formData->box;
142}
143
144int FormField::id() const
145{
146 return m_formData->fm->getID();
147}
148
149QString FormField::name() const
150{
151 QString name;
152 if (const GooString *goo = m_formData->fm->getPartialName()) {
153 name = UnicodeParsedString(s1: goo);
154 }
155 return name;
156}
157
158void FormField::setName(const QString &name) const
159{
160 GooString *goo = QStringToGooString(s: name);
161 m_formData->fm->setPartialName(*goo);
162 delete goo;
163}
164
165QString FormField::fullyQualifiedName() const
166{
167 QString name;
168 if (GooString *goo = m_formData->fm->getFullyQualifiedName()) {
169 name = UnicodeParsedString(s1: goo);
170 }
171 return name;
172}
173
174QString FormField::uiName() const
175{
176 QString name;
177 if (const GooString *goo = m_formData->fm->getAlternateUiName()) {
178 name = UnicodeParsedString(s1: goo);
179 }
180 return name;
181}
182
183bool FormField::isReadOnly() const
184{
185 return m_formData->fm->isReadOnly();
186}
187
188void FormField::setReadOnly(bool value)
189{
190 m_formData->fm->setReadOnly(value);
191}
192
193bool FormField::isVisible() const
194{
195 const unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags();
196 if (flags & Annot::flagHidden) {
197 return false;
198 }
199 if (flags & Annot::flagNoView) {
200 return false;
201 }
202 return true;
203}
204
205void FormField::setVisible(bool value)
206{
207 unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags();
208 if (value) {
209 flags &= ~Annot::flagHidden;
210 flags &= ~Annot::flagNoView;
211 } else {
212 flags |= Annot::flagHidden;
213 }
214 m_formData->fm->getWidgetAnnotation()->setFlags(flags);
215}
216
217bool FormField::isPrintable() const
218{
219 return (m_formData->fm->getWidgetAnnotation()->getFlags() & Annot::flagPrint);
220}
221
222void FormField::setPrintable(bool value)
223{
224 unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags();
225 if (value) {
226 flags |= Annot::flagPrint;
227 } else {
228 flags &= ~Annot::flagPrint;
229 }
230 m_formData->fm->getWidgetAnnotation()->setFlags(flags);
231}
232
233std::unique_ptr<Link> FormField::activationAction() const
234{
235 if (::LinkAction *act = m_formData->fm->getActivationAction()) {
236 return PageData::convertLinkActionToLink(a: act, parentDoc: m_formData->doc, linkArea: QRectF());
237 }
238
239 return {};
240}
241
242std::unique_ptr<Link> FormField::additionalAction(AdditionalActionType type) const
243{
244 Annot::FormAdditionalActionsType actionType = Annot::actionFieldModified;
245 switch (type) {
246 case FieldModified:
247 actionType = Annot::actionFieldModified;
248 break;
249 case FormatField:
250 actionType = Annot::actionFormatField;
251 break;
252 case ValidateField:
253 actionType = Annot::actionValidateField;
254 break;
255 case CalculateField:
256 actionType = Annot::actionCalculateField;
257 break;
258 }
259
260 if (std::unique_ptr<::LinkAction> act = m_formData->fm->getAdditionalAction(type: actionType)) {
261 return PageData::convertLinkActionToLink(a: act.get(), parentDoc: m_formData->doc, linkArea: QRectF());
262 }
263
264 return {};
265}
266
267std::unique_ptr<Link> FormField::additionalAction(Annotation::AdditionalActionType type) const
268{
269 ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation();
270 if (!w) {
271 return {};
272 }
273
274 const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type);
275
276 if (std::unique_ptr<::LinkAction> act = w->getAdditionalAction(type: actionType)) {
277 return PageData::convertLinkActionToLink(a: act.get(), parentDoc: m_formData->doc, linkArea: QRectF());
278 }
279
280 return {};
281}
282
283FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(std::make_unique<FormFieldData>(args&: doc, args&: p, args&: w)) { }
284
285FormFieldButton::~FormFieldButton() { }
286
287FormFieldButton::FormType FormFieldButton::type() const
288{
289 return FormField::FormButton;
290}
291
292FormFieldButton::ButtonType FormFieldButton::buttonType() const
293{
294 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
295 switch (fwb->getButtonType()) {
296 case formButtonCheck:
297 return FormFieldButton::CheckBox;
298 break;
299 case formButtonPush:
300 return FormFieldButton::Push;
301 break;
302 case formButtonRadio:
303 return FormFieldButton::Radio;
304 break;
305 }
306 return FormFieldButton::CheckBox;
307}
308
309QString FormFieldButton::caption() const
310{
311 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
312 QString ret;
313 if (fwb->getButtonType() == formButtonPush) {
314 Dict *dict = m_formData->fm->getObj()->getDict();
315 Object obj1 = dict->lookup(key: "MK");
316 if (obj1.isDict()) {
317 AnnotAppearanceCharacs appearCharacs(obj1.getDict());
318 if (appearCharacs.getNormalCaption()) {
319 ret = UnicodeParsedString(s1: appearCharacs.getNormalCaption());
320 }
321 }
322 } else {
323 if (const char *goo = fwb->getOnStr()) {
324 ret = QString::fromUtf8(utf8: goo);
325 }
326 }
327 return ret;
328}
329
330FormFieldIcon FormFieldButton::icon() const
331{
332 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
333 if (fwb->getButtonType() == formButtonPush) {
334 Dict *dict = m_formData->fm->getObj()->getDict();
335 FormFieldIconData *data = new FormFieldIconData;
336 data->icon = dict;
337 return FormFieldIcon(data);
338 }
339 return FormFieldIcon(nullptr);
340}
341
342void FormFieldButton::setIcon(const FormFieldIcon &icon)
343{
344 if (FormFieldIconData::getData(f: icon) == nullptr) {
345 return;
346 }
347
348 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
349 if (fwb->getButtonType() == formButtonPush) {
350 ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation();
351 FormFieldIconData *data = FormFieldIconData::getData(f: icon);
352 if (data->icon != nullptr) {
353 w->setNewAppearance(data->icon->lookup(key: "AP"));
354 }
355 }
356}
357
358bool FormFieldButton::state() const
359{
360 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
361 return fwb->getState();
362}
363
364void FormFieldButton::setState(bool state)
365{
366 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
367 fwb->setState((bool)state);
368}
369
370QList<int> FormFieldButton::siblings() const
371{
372 FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm);
373 ::FormFieldButton *ffb = static_cast<::FormFieldButton *>(fwb->getField());
374 if (fwb->getButtonType() == formButtonPush) {
375 return QList<int>();
376 }
377
378 QList<int> ret;
379 for (int i = 0; i < ffb->getNumSiblings(); ++i) {
380 ::FormFieldButton *sibling = static_cast<::FormFieldButton *>(ffb->getSibling(i));
381 for (int j = 0; j < sibling->getNumWidgets(); ++j) {
382 FormWidget *w = sibling->getWidget(i: j);
383 if (w) {
384 ret.append(t: w->getID());
385 }
386 }
387 }
388
389 return ret;
390}
391
392FormFieldText::FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w) : FormField(std::make_unique<FormFieldData>(args&: doc, args&: p, args&: w)) { }
393
394FormFieldText::~FormFieldText() { }
395
396FormField::FormType FormFieldText::type() const
397{
398 return FormField::FormText;
399}
400
401FormFieldText::TextType FormFieldText::textType() const
402{
403 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
404 if (fwt->isFileSelect()) {
405 return FormFieldText::FileSelect;
406 } else if (fwt->isMultiline()) {
407 return FormFieldText::Multiline;
408 }
409 return FormFieldText::Normal;
410}
411
412QString FormFieldText::text() const
413{
414 const GooString *goo = static_cast<FormWidgetText *>(m_formData->fm)->getContent();
415 return UnicodeParsedString(s1: goo);
416}
417
418void FormFieldText::setText(const QString &text)
419{
420 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
421 GooString *goo = QStringToUnicodeGooString(s: text);
422 fwt->setContent(goo);
423 delete goo;
424}
425
426void FormFieldText::setAppearanceText(const QString &text)
427{
428 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
429 GooString *goo = QStringToUnicodeGooString(s: text);
430 fwt->setAppearanceContent(goo);
431 delete goo;
432}
433
434bool FormFieldText::isPassword() const
435{
436 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
437 return fwt->isPassword();
438}
439
440bool FormFieldText::isRichText() const
441{
442 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
443 return fwt->isRichText();
444}
445
446int FormFieldText::maximumLength() const
447{
448 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
449 const int maxlen = fwt->getMaxLen();
450 return maxlen > 0 ? maxlen : -1;
451}
452
453Qt::Alignment FormFieldText::textAlignment() const
454{
455 return formTextAlignment(fm: m_formData->fm);
456}
457
458bool FormFieldText::canBeSpellChecked() const
459{
460 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
461 return !fwt->noSpellCheck();
462}
463
464double FormFieldText::getFontSize() const
465{
466 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
467 return fwt->getTextFontSize();
468}
469
470void FormFieldText::setFontSize(int fontSize)
471{
472 FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm);
473 fwt->setTextFontSize(fontSize);
474}
475
476FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) : FormField(std::make_unique<FormFieldData>(args&: doc, args&: p, args&: w)) { }
477
478FormFieldChoice::~FormFieldChoice() { }
479
480FormFieldChoice::FormType FormFieldChoice::type() const
481{
482 return FormField::FormChoice;
483}
484
485FormFieldChoice::ChoiceType FormFieldChoice::choiceType() const
486{
487 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
488 if (fwc->isCombo()) {
489 return FormFieldChoice::ComboBox;
490 }
491 return FormFieldChoice::ListBox;
492}
493
494QStringList FormFieldChoice::choices() const
495{
496 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
497 QStringList ret;
498 int num = fwc->getNumChoices();
499 ret.reserve(asize: num);
500 for (int i = 0; i < num; ++i) {
501 ret.append(t: UnicodeParsedString(s1: fwc->getChoice(i)));
502 }
503 return ret;
504}
505
506QVector<QPair<QString, QString>> FormFieldChoice::choicesWithExportValues() const
507{
508 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
509 QVector<QPair<QString, QString>> ret;
510 const int num = fwc->getNumChoices();
511 ret.reserve(asize: num);
512 for (int i = 0; i < num; ++i) {
513 const QString display = UnicodeParsedString(s1: fwc->getChoice(i));
514 const GooString *exportValueG = fwc->getExportVal(i);
515 const QString exportValue = exportValueG ? UnicodeParsedString(s1: exportValueG) : display;
516 ret.append(t: { display, exportValue });
517 }
518 return ret;
519}
520
521bool FormFieldChoice::isEditable() const
522{
523 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
524 return fwc->isCombo() ? fwc->hasEdit() : false;
525}
526
527bool FormFieldChoice::multiSelect() const
528{
529 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
530 return !fwc->isCombo() ? fwc->isMultiSelect() : false;
531}
532
533QList<int> FormFieldChoice::currentChoices() const
534{
535 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
536 int num = fwc->getNumChoices();
537 QList<int> choices;
538 for (int i = 0; i < num; ++i) {
539 if (fwc->isSelected(i)) {
540 choices.append(t: i);
541 }
542 }
543 return choices;
544}
545
546void FormFieldChoice::setCurrentChoices(const QList<int> &choice)
547{
548 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
549 fwc->deselectAll();
550 for (int i = 0; i < choice.count(); ++i) {
551 fwc->select(i: choice.at(i));
552 }
553}
554
555QString FormFieldChoice::editChoice() const
556{
557 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
558
559 if (fwc->isCombo() && fwc->hasEdit()) {
560 return UnicodeParsedString(s1: fwc->getEditChoice());
561 } else {
562 return QString();
563 }
564}
565
566void FormFieldChoice::setEditChoice(const QString &text)
567{
568 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
569
570 if (fwc->isCombo() && fwc->hasEdit()) {
571 GooString *goo = QStringToUnicodeGooString(s: text);
572 fwc->setEditChoice(goo);
573 delete goo;
574 }
575}
576
577Qt::Alignment FormFieldChoice::textAlignment() const
578{
579 return formTextAlignment(fm: m_formData->fm);
580}
581
582bool FormFieldChoice::canBeSpellChecked() const
583{
584 FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm);
585 return !fwc->noSpellCheck();
586}
587
588class CertificateInfoPrivate
589{
590public:
591 struct EntityInfo
592 {
593 QString common_name;
594 QString email_address;
595 QString org_name;
596 QString distinguished_name;
597 };
598
599 EntityInfo issuer_info;
600 EntityInfo subject_info;
601 QString nick_name;
602 QByteArray certificate_der;
603 QByteArray serial_number;
604 QByteArray public_key;
605 QDateTime validity_start;
606 QDateTime validity_end;
607 int public_key_type;
608 int public_key_strength;
609 int ku_extensions;
610 int version;
611 bool is_self_signed;
612 bool is_null;
613 CertificateInfo::KeyLocation keyLocation;
614};
615
616CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate())
617{
618 d_ptr->is_null = true;
619}
620
621CertificateInfo::CertificateInfo(CertificateInfoPrivate *priv) : d_ptr(priv) { }
622
623CertificateInfo::CertificateInfo(const CertificateInfo &other) : d_ptr(other.d_ptr) { }
624
625CertificateInfo::~CertificateInfo() = default;
626
627CertificateInfo &CertificateInfo::operator=(const CertificateInfo &other)
628{
629 if (this != &other) {
630 d_ptr = other.d_ptr;
631 }
632
633 return *this;
634}
635
636bool CertificateInfo::isNull() const
637{
638 Q_D(const CertificateInfo);
639 return d->is_null;
640}
641
642int CertificateInfo::version() const
643{
644 Q_D(const CertificateInfo);
645 return d->version;
646}
647
648QByteArray CertificateInfo::serialNumber() const
649{
650 Q_D(const CertificateInfo);
651 return d->serial_number;
652}
653
654QString CertificateInfo::issuerInfo(EntityInfoKey key) const
655{
656 Q_D(const CertificateInfo);
657 switch (key) {
658 case CommonName:
659 return d->issuer_info.common_name;
660 case DistinguishedName:
661 return d->issuer_info.distinguished_name;
662 case EmailAddress:
663 return d->issuer_info.email_address;
664 case Organization:
665 return d->issuer_info.org_name;
666 default:
667 return QString();
668 }
669}
670
671QString CertificateInfo::subjectInfo(EntityInfoKey key) const
672{
673 Q_D(const CertificateInfo);
674 switch (key) {
675 case CommonName:
676 return d->subject_info.common_name;
677 case DistinguishedName:
678 return d->subject_info.distinguished_name;
679 case EmailAddress:
680 return d->subject_info.email_address;
681 case Organization:
682 return d->subject_info.org_name;
683 default:
684 return QString();
685 }
686}
687
688QString CertificateInfo::nickName() const
689{
690 Q_D(const CertificateInfo);
691 return d->nick_name;
692}
693
694QDateTime CertificateInfo::validityStart() const
695{
696 Q_D(const CertificateInfo);
697 return d->validity_start;
698}
699
700QDateTime CertificateInfo::validityEnd() const
701{
702 Q_D(const CertificateInfo);
703 return d->validity_end;
704}
705
706CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const
707{
708 Q_D(const CertificateInfo);
709
710 KeyUsageExtensions kuExtensions = KuNone;
711 if (d->ku_extensions & KU_DIGITAL_SIGNATURE) {
712 kuExtensions |= KuDigitalSignature;
713 }
714 if (d->ku_extensions & KU_NON_REPUDIATION) {
715 kuExtensions |= KuNonRepudiation;
716 }
717 if (d->ku_extensions & KU_KEY_ENCIPHERMENT) {
718 kuExtensions |= KuKeyEncipherment;
719 }
720 if (d->ku_extensions & KU_DATA_ENCIPHERMENT) {
721 kuExtensions |= KuDataEncipherment;
722 }
723 if (d->ku_extensions & KU_KEY_AGREEMENT) {
724 kuExtensions |= KuKeyAgreement;
725 }
726 if (d->ku_extensions & KU_KEY_CERT_SIGN) {
727 kuExtensions |= KuKeyCertSign;
728 }
729 if (d->ku_extensions & KU_CRL_SIGN) {
730 kuExtensions |= KuClrSign;
731 }
732 if (d->ku_extensions & KU_ENCIPHER_ONLY) {
733 kuExtensions |= KuEncipherOnly;
734 }
735
736 return kuExtensions;
737}
738
739CertificateInfo::KeyLocation CertificateInfo::keyLocation() const
740{
741 Q_D(const CertificateInfo);
742 return d->keyLocation;
743}
744
745QByteArray CertificateInfo::publicKey() const
746{
747 Q_D(const CertificateInfo);
748 return d->public_key;
749}
750
751CertificateInfo::PublicKeyType CertificateInfo::publicKeyType() const
752{
753 Q_D(const CertificateInfo);
754 switch (d->public_key_type) {
755 case RSAKEY:
756 return RsaKey;
757 case DSAKEY:
758 return DsaKey;
759 case ECKEY:
760 return EcKey;
761 default:
762 return OtherKey;
763 }
764}
765
766int CertificateInfo::publicKeyStrength() const
767{
768 Q_D(const CertificateInfo);
769 return d->public_key_strength;
770}
771
772bool CertificateInfo::isSelfSigned() const
773{
774 Q_D(const CertificateInfo);
775 return d->is_self_signed;
776}
777
778QByteArray CertificateInfo::certificateData() const
779{
780 Q_D(const CertificateInfo);
781 return d->certificate_der;
782}
783
784bool CertificateInfo::checkPassword(const QString &password) const
785{
786#ifdef ENABLE_SIGNATURES
787 auto backend = CryptoSign::Factory::createActive();
788 if (!backend) {
789 return false;
790 }
791 Q_D(const CertificateInfo);
792 auto sigHandler = backend->createSigningHandler(certID: d->nick_name.toStdString(), digestAlgTag: HashAlgorithm::Sha256);
793 unsigned char buffer[5];
794 memcpy(dest: buffer, src: "test", n: 5);
795 sigHandler->addData(data_block: buffer, data_len: 5);
796 std::optional<GooString> tmpSignature = sigHandler->signDetached(password: password.toStdString());
797 return tmpSignature.has_value();
798#else
799 return false;
800#endif
801}
802
803class SignatureValidationInfoPrivate
804{
805public:
806 explicit SignatureValidationInfoPrivate(CertificateInfo &&ci) : cert_info(ci) { }
807
808 SignatureValidationInfo::SignatureStatus signature_status;
809 SignatureValidationInfo::CertificateStatus certificate_status;
810 CertificateInfo cert_info;
811
812 QByteArray signature;
813 QString signer_name;
814 QString signer_subject_dn;
815 QString location;
816 QString reason;
817 HashAlgorithm hash_algorithm;
818 time_t signing_time;
819 QList<qint64> range_bounds;
820 qint64 docLength;
821};
822
823SignatureValidationInfo::SignatureValidationInfo(SignatureValidationInfoPrivate *priv) : d_ptr(priv) { }
824
825SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) : d_ptr(other.d_ptr) { }
826
827SignatureValidationInfo::~SignatureValidationInfo() { }
828
829SignatureValidationInfo::SignatureStatus SignatureValidationInfo::signatureStatus() const
830{
831 Q_D(const SignatureValidationInfo);
832 return d->signature_status;
833}
834
835SignatureValidationInfo::CertificateStatus SignatureValidationInfo::certificateStatus() const
836{
837 Q_D(const SignatureValidationInfo);
838 return d->certificate_status;
839}
840
841QString SignatureValidationInfo::signerName() const
842{
843 Q_D(const SignatureValidationInfo);
844 return d->signer_name;
845}
846
847QString SignatureValidationInfo::signerSubjectDN() const
848{
849 Q_D(const SignatureValidationInfo);
850 return d->signer_subject_dn;
851}
852
853QString SignatureValidationInfo::location() const
854{
855 Q_D(const SignatureValidationInfo);
856 return d->location;
857}
858
859QString SignatureValidationInfo::reason() const
860{
861 Q_D(const SignatureValidationInfo);
862 return d->reason;
863}
864
865SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const
866{
867#ifdef ENABLE_SIGNATURES
868 Q_D(const SignatureValidationInfo);
869
870 switch (d->hash_algorithm) {
871 case ::HashAlgorithm::Md2:
872 return HashAlgorithmMd2;
873 case ::HashAlgorithm::Md5:
874 return HashAlgorithmMd5;
875 case ::HashAlgorithm::Sha1:
876 return HashAlgorithmSha1;
877 case ::HashAlgorithm::Sha256:
878 return HashAlgorithmSha256;
879 case ::HashAlgorithm::Sha384:
880 return HashAlgorithmSha384;
881 case ::HashAlgorithm::Sha512:
882 return HashAlgorithmSha512;
883 case ::HashAlgorithm::Sha224:
884 return HashAlgorithmSha224;
885 case ::HashAlgorithm::Unknown:
886 return HashAlgorithmUnknown;
887 }
888#endif
889 return HashAlgorithmUnknown;
890}
891
892time_t SignatureValidationInfo::signingTime() const
893{
894 Q_D(const SignatureValidationInfo);
895 return d->signing_time;
896}
897
898QByteArray SignatureValidationInfo::signature() const
899{
900 Q_D(const SignatureValidationInfo);
901 return d->signature;
902}
903
904QList<qint64> SignatureValidationInfo::signedRangeBounds() const
905{
906 Q_D(const SignatureValidationInfo);
907 return d->range_bounds;
908}
909
910bool SignatureValidationInfo::signsTotalDocument() const
911{
912 Q_D(const SignatureValidationInfo);
913 if (d->range_bounds.size() == 4 && d->range_bounds.value(i: 0) == 0 && d->range_bounds.value(i: 1) >= 0 && d->range_bounds.value(i: 2) > d->range_bounds.value(i: 1) && d->range_bounds.value(i: 3) >= d->range_bounds.value(i: 2)) {
914 // The range from d->range_bounds.value(1) to d->range_bounds.value(2) is
915 // not authenticated by the signature and should only contain the signature
916 // itself padded with 0 bytes. This has been checked in readSignature().
917 // If it failed, d->signature is empty.
918 // A potential range after d->range_bounds.value(3) would be also not
919 // authenticated. Therefore d->range_bounds.value(3) should coincide with
920 // the end of the document.
921 if (d->docLength == d->range_bounds.value(i: 3) && !d->signature.isEmpty()) {
922 return true;
923 }
924 }
925 return false;
926}
927
928CertificateInfo SignatureValidationInfo::certificateInfo() const
929{
930 Q_D(const SignatureValidationInfo);
931 return d->cert_info;
932}
933
934SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValidationInfo &other)
935{
936 if (this != &other) {
937 d_ptr = other.d_ptr;
938 }
939
940 return *this;
941}
942
943FormFieldSignature::FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w) : FormField(std::make_unique<FormFieldData>(args&: doc, args&: p, args&: w)) { }
944
945FormFieldSignature::~FormFieldSignature() { }
946
947FormField::FormType FormFieldSignature::type() const
948{
949 return FormField::FormSignature;
950}
951
952FormFieldSignature::SignatureType FormFieldSignature::signatureType() const
953{
954 SignatureType sigType = AdbePkcs7detached;
955 FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
956 switch (fws->signatureType()) {
957 case adbe_pkcs7_sha1:
958 sigType = AdbePkcs7sha1;
959 break;
960 case adbe_pkcs7_detached:
961 sigType = AdbePkcs7detached;
962 break;
963 case ETSI_CAdES_detached:
964 sigType = EtsiCAdESdetached;
965 break;
966 case unknown_signature_type:
967 sigType = UnknownSignatureType;
968 break;
969 case unsigned_signature_field:
970 sigType = UnsignedSignature;
971 break;
972 }
973 return sigType;
974}
975
976SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const
977{
978 auto tempResult = validateAsync(opt);
979 tempResult.first.d_ptr->certificate_status = validateResult();
980 return tempResult.first;
981}
982
983static CertificateInfo::KeyLocation fromPopplerCore(KeyLocation location)
984{
985 switch (location) {
986 case KeyLocation::Computer:
987 return CertificateInfo::KeyLocation::Computer;
988 case KeyLocation::Other:
989 return CertificateInfo::KeyLocation::Other;
990 case KeyLocation::Unknown:
991 return CertificateInfo::KeyLocation::Unknown;
992 case KeyLocation::HardwareToken:
993 return CertificateInfo::KeyLocation::HardwareToken;
994 }
995 return CertificateInfo::KeyLocation::Unknown;
996}
997
998static CertificateInfoPrivate *createCertificateInfoPrivate(const X509CertificateInfo *ci)
999{
1000 CertificateInfoPrivate *certPriv = new CertificateInfoPrivate;
1001 certPriv->is_null = true;
1002 if (ci) {
1003 certPriv->version = ci->getVersion();
1004 certPriv->ku_extensions = ci->getKeyUsageExtensions();
1005 certPriv->keyLocation = fromPopplerCore(location: ci->getKeyLocation());
1006
1007 const GooString &certSerial = ci->getSerialNumber();
1008 certPriv->serial_number = QByteArray(certSerial.c_str(), certSerial.getLength());
1009
1010 const X509CertificateInfo::EntityInfo &issuerInfo = ci->getIssuerInfo();
1011 certPriv->issuer_info.common_name = issuerInfo.commonName.c_str();
1012 certPriv->issuer_info.distinguished_name = issuerInfo.distinguishedName.c_str();
1013 certPriv->issuer_info.email_address = issuerInfo.email.c_str();
1014 certPriv->issuer_info.org_name = issuerInfo.organization.c_str();
1015
1016 const X509CertificateInfo::EntityInfo &subjectInfo = ci->getSubjectInfo();
1017 certPriv->subject_info.common_name = subjectInfo.commonName.c_str();
1018 certPriv->subject_info.distinguished_name = subjectInfo.distinguishedName.c_str();
1019 certPriv->subject_info.email_address = subjectInfo.email.c_str();
1020 certPriv->subject_info.org_name = subjectInfo.organization.c_str();
1021
1022 certPriv->nick_name = ci->getNickName().c_str();
1023
1024 X509CertificateInfo::Validity certValidity = ci->getValidity();
1025 certPriv->validity_start = QDateTime::fromSecsSinceEpoch(secs: certValidity.notBefore, spec: Qt::UTC);
1026 certPriv->validity_end = QDateTime::fromSecsSinceEpoch(secs: certValidity.notAfter, spec: Qt::UTC);
1027
1028 const X509CertificateInfo::PublicKeyInfo &pkInfo = ci->getPublicKeyInfo();
1029 certPriv->public_key = QByteArray(pkInfo.publicKey.c_str(), pkInfo.publicKey.getLength());
1030 certPriv->public_key_type = static_cast<int>(pkInfo.publicKeyType);
1031 certPriv->public_key_strength = pkInfo.publicKeyStrength;
1032
1033 const GooString &certDer = ci->getCertificateDER();
1034 certPriv->certificate_der = QByteArray(certDer.c_str(), certDer.getLength());
1035
1036 certPriv->is_null = false;
1037 }
1038
1039 return certPriv;
1040}
1041
1042static SignatureValidationInfo::CertificateStatus fromInternal(CertificateValidationStatus status)
1043{
1044 switch (status) {
1045 case CERTIFICATE_TRUSTED:
1046 return SignatureValidationInfo::CertificateTrusted;
1047 case CERTIFICATE_UNTRUSTED_ISSUER:
1048 return SignatureValidationInfo::CertificateUntrustedIssuer;
1049 case CERTIFICATE_UNKNOWN_ISSUER:
1050 return SignatureValidationInfo::CertificateUnknownIssuer;
1051 case CERTIFICATE_REVOKED:
1052 return SignatureValidationInfo::CertificateRevoked;
1053 case CERTIFICATE_EXPIRED:
1054 return SignatureValidationInfo::CertificateExpired;
1055 default:
1056 case CERTIFICATE_GENERIC_ERROR:
1057 return SignatureValidationInfo::CertificateGenericError;
1058 case CERTIFICATE_NOT_VERIFIED:
1059 return SignatureValidationInfo::CertificateNotVerified;
1060 }
1061}
1062
1063static SignatureValidationInfo fromInternal(SignatureInfo *si, FormWidgetSignature *fws)
1064{
1065 // get certificate info
1066 const X509CertificateInfo *ci = si->getCertificateInfo();
1067 CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci);
1068
1069 SignatureValidationInfoPrivate *priv = new SignatureValidationInfoPrivate(CertificateInfo(certPriv));
1070 switch (si->getSignatureValStatus()) {
1071 case SIGNATURE_VALID:
1072 priv->signature_status = SignatureValidationInfo::SignatureValid;
1073 break;
1074 case SIGNATURE_INVALID:
1075 priv->signature_status = SignatureValidationInfo::SignatureInvalid;
1076 break;
1077 case SIGNATURE_DIGEST_MISMATCH:
1078 priv->signature_status = SignatureValidationInfo::SignatureDigestMismatch;
1079 break;
1080 case SIGNATURE_DECODING_ERROR:
1081 priv->signature_status = SignatureValidationInfo::SignatureDecodingError;
1082 break;
1083 default:
1084 case SIGNATURE_GENERIC_ERROR:
1085 priv->signature_status = SignatureValidationInfo::SignatureGenericError;
1086 break;
1087 case SIGNATURE_NOT_FOUND:
1088 priv->signature_status = SignatureValidationInfo::SignatureNotFound;
1089 break;
1090 case SIGNATURE_NOT_VERIFIED:
1091 priv->signature_status = SignatureValidationInfo::SignatureNotVerified;
1092 break;
1093 }
1094 priv->certificate_status = SignatureValidationInfo::CertificateVerificationInProgress;
1095 priv->signer_name = QString::fromStdString(s: si->getSignerName());
1096 priv->signer_subject_dn = QString::fromStdString(s: si->getSubjectDN());
1097 priv->hash_algorithm = si->getHashAlgorithm();
1098 priv->location = UnicodeParsedString(s1: si->getLocation().toStr());
1099 priv->reason = UnicodeParsedString(s1: si->getReason().toStr());
1100
1101 priv->signing_time = si->getSigningTime();
1102 const std::vector<Goffset> ranges = fws->getSignedRangeBounds();
1103 if (!ranges.empty()) {
1104 for (Goffset bound : ranges) {
1105 priv->range_bounds.append(t: bound);
1106 }
1107 }
1108 const std::optional<GooString> checkedSignature = fws->getCheckedSignature(checkedFileSize: &priv->docLength);
1109 if (priv->range_bounds.size() == 4 && checkedSignature) {
1110 priv->signature = QByteArray::fromHex(hexEncoded: checkedSignature->c_str());
1111 }
1112
1113 return SignatureValidationInfo(priv);
1114}
1115
1116SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const
1117{
1118 auto tempResult = validateAsync(opt: static_cast<ValidateOptions>(opt), validationTime);
1119 tempResult.first.d_ptr->certificate_status = validateResult();
1120 return tempResult.first;
1121}
1122
1123class AsyncObjectPrivate
1124{ /*Currently unused. Created for abi future proofing*/
1125};
1126
1127AsyncObject::AsyncObject() : QObject(nullptr), d {} { }
1128
1129AsyncObject::~AsyncObject() = default;
1130
1131std::pair<SignatureValidationInfo, std::shared_ptr<Poppler::AsyncObject>> FormFieldSignature::validateAsync(ValidateOptions opt, const QDateTime &validationTime) const
1132{
1133 auto object = std::make_shared<AsyncObject>();
1134 FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
1135 const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1;
1136 SignatureInfo *si = fws->validateSignatureAsync(doVerifyCert: opt & ValidateVerifyCertificate, forceRevalidation: opt & ValidateForceRevalidation, validationTime: validationTimeT, ocspRevocationCheck: !(opt & ValidateWithoutOCSPRevocationCheck), enableAIA: opt & ValidateUseAIACertFetch,
1137 doneCallback: [obj = std::weak_ptr<AsyncObject>(object)]() {
1138 if (auto l = obj.lock()) {
1139 // We need to roundtrip over the eventloop
1140 // to ensure callers have a chance of connecting to AsyncObject::done
1141 QMetaObject::invokeMethod(
1142 object: l.get(),
1143 function: [innerObj = std::weak_ptr<AsyncObject>(l)]() {
1144 if (auto innerLocked = innerObj.lock()) {
1145 emit innerLocked->done();
1146 }
1147 },
1148 type: Qt::QueuedConnection);
1149 }
1150 });
1151
1152 return { fromInternal(si, fws), object };
1153}
1154
1155SignatureValidationInfo::CertificateStatus FormFieldSignature::validateResult() const
1156{
1157 return fromInternal(status: static_cast<FormWidgetSignature *>(m_formData->fm)->validateSignatureResult());
1158}
1159
1160FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const
1161{
1162 FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
1163 if (fws->signatureType() != unsigned_signature_field) {
1164 return FieldAlreadySigned;
1165 }
1166
1167 Goffset file_size = 0;
1168 const std::optional<GooString> sig = fws->getCheckedSignature(checkedFileSize: &file_size);
1169 if (sig) {
1170 // the above unsigned_signature_field check
1171 // should already catch this, but double check
1172 return FieldAlreadySigned;
1173 }
1174 const auto reason = std::unique_ptr<GooString>(data.reason().isEmpty() ? nullptr : QStringToUnicodeGooString(s: data.reason()));
1175 const auto location = std::unique_ptr<GooString>(data.location().isEmpty() ? nullptr : QStringToUnicodeGooString(s: data.location()));
1176 const auto ownerPwd = std::optional<GooString>(data.documentOwnerPassword().constData());
1177 const auto userPwd = std::optional<GooString>(data.documentUserPassword().constData());
1178 const auto gSignatureText = std::unique_ptr<GooString>(QStringToUnicodeGooString(s: data.signatureText()));
1179 const auto gSignatureLeftText = std::unique_ptr<GooString>(QStringToUnicodeGooString(s: data.signatureLeftText()));
1180
1181 const bool success = fws->signDocumentWithAppearance(filename: outputFileName.toStdString(), certNickname: data.certNickname().toStdString(), password: data.password().toStdString(), reason: reason.get(), location: location.get(), ownerPassword: ownerPwd, userPassword: userPwd, signatureText: *gSignatureText, signatureTextLeft: *gSignatureLeftText,
1182 fontSize: data.fontSize(), leftFontSize: data.leftFontSize(), fontColor: convertQColor(color: data.fontColor()), borderWidth: data.borderWidth(), borderColor: convertQColor(color: data.borderColor()), backgroundColor: convertQColor(color: data.backgroundColor()));
1183
1184 return success ? SigningSuccess : GenericSigningError;
1185}
1186
1187bool hasNSSSupport()
1188{
1189#ifdef ENABLE_NSS3
1190 return true;
1191#else
1192 return false;
1193#endif
1194}
1195
1196QVector<CertificateInfo> getAvailableSigningCertificates()
1197{
1198 auto backend = CryptoSign::Factory::createActive();
1199 if (!backend) {
1200 return {};
1201 }
1202 QVector<CertificateInfo> vReturnCerts;
1203 std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = backend->getAvailableSigningCertificates();
1204
1205 for (auto &cert : vCerts) {
1206 CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci: cert.get());
1207 vReturnCerts.append(t: CertificateInfo(certPriv));
1208 }
1209
1210 return vReturnCerts;
1211}
1212
1213static std::optional<CryptoSignBackend> convertToFrontend(std::optional<CryptoSign::Backend::Type> type)
1214{
1215 if (!type) {
1216 return std::nullopt;
1217 }
1218 switch (type.value()) {
1219 case CryptoSign::Backend::Type::NSS3:
1220 return CryptoSignBackend::NSS;
1221 case CryptoSign::Backend::Type::GPGME:
1222 return CryptoSignBackend::GPG;
1223 }
1224 return std::nullopt;
1225}
1226
1227static std::optional<CryptoSign::Backend::Type> convertToBackend(std::optional<CryptoSignBackend> backend)
1228{
1229 if (!backend) {
1230 return std::nullopt;
1231 }
1232
1233 switch (backend.value()) {
1234 case CryptoSignBackend::NSS:
1235 return CryptoSign::Backend::Type::NSS3;
1236 case CryptoSignBackend::GPG:
1237 return CryptoSign::Backend::Type::GPGME;
1238 }
1239 return std::nullopt;
1240}
1241
1242QVector<CryptoSignBackend> availableCryptoSignBackends()
1243{
1244 QVector<CryptoSignBackend> backends;
1245 for (auto &backend : CryptoSign::Factory::getAvailable()) {
1246 auto converted = convertToFrontend(type: backend);
1247 if (converted) {
1248 backends.push_back(t: converted.value());
1249 }
1250 }
1251 return backends;
1252}
1253
1254std::optional<CryptoSignBackend> activeCryptoSignBackend()
1255{
1256 return convertToFrontend(type: CryptoSign::Factory::getActive());
1257}
1258
1259bool setActiveCryptoSignBackend(CryptoSignBackend backend)
1260{
1261 auto available = availableCryptoSignBackends();
1262 if (!available.contains(t: backend)) {
1263 return false;
1264 }
1265 auto converted = convertToBackend(backend);
1266 if (!converted) {
1267 return false;
1268 }
1269 CryptoSign::Factory::setPreferredBackend(converted.value());
1270 return activeCryptoSignBackend() == backend;
1271}
1272
1273static bool hasNSSBackendFeature(CryptoSignBackendFeature feature)
1274{
1275 switch (feature) {
1276 case CryptoSignBackendFeature::BackendAsksPassphrase:
1277 return false;
1278 }
1279 return false;
1280}
1281
1282static bool hasGPGBackendFeature(CryptoSignBackendFeature feature)
1283{
1284 switch (feature) {
1285 case CryptoSignBackendFeature::BackendAsksPassphrase:
1286 return true;
1287 }
1288 return false;
1289}
1290
1291bool hasCryptoSignBackendFeature(CryptoSignBackend backend, CryptoSignBackendFeature feature)
1292{
1293 switch (backend) {
1294 case CryptoSignBackend::NSS:
1295 return hasNSSBackendFeature(feature);
1296 case CryptoSignBackend::GPG:
1297 return hasGPGBackendFeature(feature);
1298 }
1299 return false;
1300}
1301
1302QString POPPLER_QT6_EXPORT getNSSDir()
1303{
1304#ifdef ENABLE_NSS3
1305 return QString::fromLocal8Bit(ba: NSSSignatureConfiguration::getNSSDir().c_str());
1306#else
1307 return QString();
1308#endif
1309}
1310
1311void setNSSDir(const QString &path)
1312{
1313#ifdef ENABLE_NSS3
1314 if (path.isEmpty()) {
1315 return;
1316 }
1317
1318 GooString *goo = QStringToGooString(s: path);
1319 NSSSignatureConfiguration::setNSSDir(*goo);
1320 delete goo;
1321#else
1322 (void)path;
1323#endif
1324}
1325
1326namespace {
1327std::function<QString(const QString &)> nssPasswordCall;
1328}
1329
1330void setNSSPasswordCallback(const std::function<char *(const char *)> &f)
1331{
1332#ifdef ENABLE_NSS3
1333 NSSSignatureConfiguration::setNSSPasswordCallback(f);
1334#else
1335 qWarning() << "setNSSPasswordCallback called but this poppler is built without NSS support";
1336 (void)f;
1337#endif
1338}
1339}
1340

source code of poppler/qt6/src/poppler-form.cc