1 //========================================================================
2 //
3 // JSInfo.cc
4 //
5 // This file is licensed under the GPLv2 or later
6 //
7 // Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
8 // Copyright (C) 2017, 2020, 2021 Albert Astals Cid <aacid@kde.org>
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) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
11 // Copyright (C) 2020 Nelson Benítez León <nbenitezl@gmail.com>
12 //
13 // To see a description of the changes please see the Changelog file that
14 // came with your tarball or type make ChangeLog if you are building from git
15 //
16 //========================================================================
17 
18 #include "config.h"
19 #include <cstdio>
20 #include "Object.h"
21 #include "Dict.h"
22 #include "Annot.h"
23 #include "PDFDoc.h"
24 #include "JSInfo.h"
25 #include "Link.h"
26 #include "Form.h"
27 #include "UnicodeMap.h"
28 #include "UTF.h"
29 // #include "Win32Console.h"
30 
JSInfo(PDFDoc * docA,int firstPage)31 JSInfo::JSInfo(PDFDoc *docA, int firstPage)
32 {
33     doc = docA;
34     currentPage = firstPage + 1;
35 }
36 
~JSInfo()37 JSInfo::~JSInfo() { }
38 
printJS(const GooString * js)39 void JSInfo::printJS(const GooString *js)
40 {
41     Unicode *u = nullptr;
42     char buf[8];
43     int i, n, len;
44 
45     if (!js || !js->c_str())
46         return;
47 
48     len = TextStringToUCS4(js->toStr(), &u);
49     for (i = 0; i < len; i++) {
50         n = uniMap->mapUnicode(u[i], buf, sizeof(buf));
51         fwrite(buf, 1, n, file);
52     }
53     gfree(u);
54 }
55 
scanLinkAction(LinkAction * link,const char * action)56 void JSInfo::scanLinkAction(LinkAction *link, const char *action)
57 {
58     if (!link)
59         return;
60 
61     if (link->getKind() == actionJavaScript) {
62         hasJS = true;
63         if (print) {
64             LinkJavaScript *linkjs = static_cast<LinkJavaScript *>(link);
65             if (linkjs->isOk()) {
66                 const std::string &s = linkjs->getScript();
67                 fprintf(file, "%s:\n", action);
68                 GooString gooS = GooString(s);
69                 printJS(&gooS);
70                 fputs("\n\n", file);
71             }
72         }
73     }
74 
75     if (link->getKind() == actionRendition) {
76         LinkRendition *linkr = static_cast<LinkRendition *>(link);
77         if (!linkr->getScript().empty()) {
78             hasJS = true;
79             if (print) {
80                 fprintf(file, "%s (Rendition):\n", action);
81                 const GooString s(linkr->getScript());
82                 printJS(&s);
83                 fputs("\n\n", file);
84             }
85         }
86     }
87 }
88 
scanJS(int nPages)89 void JSInfo::scanJS(int nPages)
90 {
91     print = false;
92     file = nullptr;
93     onlyFirstJS = false;
94     scan(nPages);
95 }
96 
scanJS(int nPages,FILE * fout,const UnicodeMap * uMap)97 void JSInfo::scanJS(int nPages, FILE *fout, const UnicodeMap *uMap)
98 {
99     print = true;
100     file = fout;
101     uniMap = uMap;
102     onlyFirstJS = false;
103     scan(nPages);
104 }
105 
scanJS(int nPages,bool stopOnFirstJS)106 void JSInfo::scanJS(int nPages, bool stopOnFirstJS)
107 {
108     print = false;
109     file = nullptr;
110     onlyFirstJS = stopOnFirstJS;
111     scan(nPages);
112 }
113 
scan(int nPages)114 void JSInfo::scan(int nPages)
115 {
116     Page *page;
117     Annots *annots;
118     int lastPage;
119 
120     hasJS = false;
121 
122     // Names
123     int numNames = doc->getCatalog()->numJS();
124     if (numNames > 0) {
125         hasJS = true;
126         if (onlyFirstJS) {
127             return;
128         }
129         if (print) {
130             for (int i = 0; i < numNames; i++) {
131                 fprintf(file, "Name Dictionary \"%s\":\n", doc->getCatalog()->getJSName(i)->c_str());
132                 GooString *js = doc->getCatalog()->getJS(i);
133                 printJS(js);
134                 delete js;
135                 fputs("\n\n", file);
136             }
137         }
138     }
139 
140     // document actions
141     scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument).get(), "Before Close Document");
142     scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart).get(), "Before Save Document");
143     scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish).get(), "After Save Document");
144     scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart).get(), "Before Print Document");
145     scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish).get(), "After Print Document");
146 
147     if (onlyFirstJS && hasJS) {
148         return;
149     }
150     // form field actions
151     if (doc->getCatalog()->getFormType() == Catalog::AcroForm) {
152         Form *form = doc->getCatalog()->getForm();
153         for (int i = 0; i < form->getNumFields(); i++) {
154             FormField *field = form->getRootField(i);
155             for (int j = 0; j < field->getNumWidgets(); j++) {
156                 FormWidget *widget = field->getWidget(j);
157                 scanLinkAction(widget->getActivationAction(), "Field Activated");
158                 scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified).get(), "Field Modified");
159                 scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField).get(), "Format Field");
160                 scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField).get(), "Validate Field");
161                 scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField).get(), "Calculate Field");
162                 if (onlyFirstJS && hasJS) {
163                     return;
164                 }
165             }
166         }
167     }
168 
169     // scan pages
170 
171     if (currentPage > doc->getNumPages()) {
172         return;
173     }
174 
175     lastPage = currentPage + nPages;
176     if (lastPage > doc->getNumPages() + 1) {
177         lastPage = doc->getNumPages() + 1;
178     }
179 
180     for (int pg = currentPage; pg < lastPage; ++pg) {
181         page = doc->getPage(pg);
182         if (!page)
183             continue;
184 
185         // page actions (open, close)
186         scanLinkAction(page->getAdditionalAction(Page::actionOpenPage).get(), "Page Open");
187         scanLinkAction(page->getAdditionalAction(Page::actionClosePage).get(), "Page Close");
188 
189         if (onlyFirstJS && hasJS) {
190             return;
191         }
192         // annotation actions (links, screen, widget)
193         annots = page->getAnnots();
194         for (int i = 0; i < annots->getNumAnnots(); ++i) {
195             if (annots->getAnnot(i)->getType() == Annot::typeLink) {
196                 AnnotLink *annot = static_cast<AnnotLink *>(annots->getAnnot(i));
197                 scanLinkAction(annot->getAction(), "Link Annotation Activated");
198                 if (onlyFirstJS && hasJS) {
199                     return;
200                 }
201             } else if (annots->getAnnot(i)->getType() == Annot::typeScreen) {
202                 AnnotScreen *annot = static_cast<AnnotScreen *>(annots->getAnnot(i));
203                 scanLinkAction(annot->getAction(), "Screen Annotation Activated");
204                 scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(), "Screen Annotation Cursor Enter");
205                 scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(), "Screen Annotation Cursor Leave");
206                 scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(), "Screen Annotation Mouse Pressed");
207                 scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(), "Screen Annotation Mouse Released");
208                 scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(), "Screen Annotation Focus In");
209                 scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(), "Screen Annotation Focus Out");
210                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(), "Screen Annotation Page Open");
211                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(), "Screen Annotation Page Close");
212                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(), "Screen Annotation Page Visible");
213                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(), "Screen Annotation Page Invisible");
214 
215                 if (onlyFirstJS && hasJS) {
216                     return;
217                 }
218             } else if (annots->getAnnot(i)->getType() == Annot::typeWidget) {
219                 AnnotWidget *annot = static_cast<AnnotWidget *>(annots->getAnnot(i));
220                 scanLinkAction(annot->getAction(), "Widget Annotation Activated");
221                 scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(), "Widget Annotation Cursor Enter");
222                 scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(), "Widget Annotation Cursor Leave");
223                 scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(), "Widget Annotation Mouse Pressed");
224                 scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(), "Widget Annotation Mouse Released");
225                 scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(), "Widget Annotation Focus In");
226                 scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(), "Widget Annotation Focus Out");
227                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(), "Widget Annotation Page Open");
228                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(), "Widget Annotation Page Close");
229                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(), "Widget Annotation Page Visible");
230                 scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(), "Widget Annotation Page Invisible");
231                 if (onlyFirstJS && hasJS) {
232                     return;
233                 }
234             }
235         }
236     }
237 
238     currentPage = lastPage;
239 }
240 
containsJS()241 bool JSInfo::containsJS()
242 {
243     return hasJS;
244 }
245