1//========================================================================
2//
3// FileSpec.cc
4//
5// All changes made under the Poppler project to this file are licensed
6// under GPL version 2 or later
7//
8// Copyright (C) 2008-2009 Carlos Garcia Campos <carlosgc@gnome.org>
9// Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
10// Copyright (C) 2012, 2017-2021 Albert Astals Cid <aacid@kde.org>
11// Copyright (C) 2012 Hib Eris <hib@hiberis.nl>
12// 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
13// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
14// Copyright (C) 2019 Christian Persch <chpe@src.gnome.org>
15// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
16//
17// To see a description of the changes please see the Changelog file that
18// came with your tarball or type make ChangeLog if you are building from git
19//
20//========================================================================
21
22//========================================================================
23//
24// Most of the code from Link.cc and PSOutputDev.cc
25//
26// Copyright 1996-2003 Glyph & Cog, LLC
27//
28//========================================================================
29
30#include <config.h>
31
32#include "FileSpec.h"
33#include "XRef.h"
34#include "goo/gfile.h"
35
36EmbFile::EmbFile(Object &&efStream)
37{
38 m_size = -1;
39 m_createDate = nullptr;
40 m_modDate = nullptr;
41 m_checksum = nullptr;
42 m_mimetype = nullptr;
43
44 m_objStr = std::move(efStream);
45
46 if (m_objStr.isStream()) {
47 // dataDict corresponds to Table 3.41 in the PDF1.6 spec.
48 Dict *dataDict = m_objStr.streamGetDict();
49
50 // subtype is normally the mimetype
51 Object subtypeName = dataDict->lookup(key: "Subtype");
52 if (subtypeName.isName()) {
53 m_mimetype = new GooString(subtypeName.getName());
54 }
55
56 // paramDict corresponds to Table 3.42 in the PDF1.6 spec
57 Object paramDict = dataDict->lookup(key: "Params");
58 if (paramDict.isDict()) {
59 Object paramObj = paramDict.dictLookup(key: "ModDate");
60 if (paramObj.isString()) {
61 m_modDate = new GooString(paramObj.getString());
62 }
63
64 paramObj = paramDict.dictLookup(key: "CreationDate");
65 if (paramObj.isString()) {
66 m_createDate = new GooString(paramObj.getString());
67 }
68
69 paramObj = paramDict.dictLookup(key: "Size");
70 if (paramObj.isInt()) {
71 m_size = paramObj.getInt();
72 }
73
74 paramObj = paramDict.dictLookup(key: "CheckSum");
75 if (paramObj.isString()) {
76 m_checksum = new GooString(paramObj.getString());
77 }
78 }
79 }
80}
81
82EmbFile::~EmbFile()
83{
84 delete m_createDate;
85 delete m_modDate;
86 delete m_checksum;
87 delete m_mimetype;
88}
89
90bool EmbFile::save(const std::string &path)
91{
92 FILE *f;
93 bool ret;
94
95 if (!(f = openFile(path: path.c_str(), mode: "wb"))) {
96 return false;
97 }
98 ret = save2(f);
99 fclose(stream: f);
100 return ret;
101}
102
103bool EmbFile::save2(FILE *f)
104{
105 int c;
106
107 if (unlikely(!m_objStr.isStream())) {
108 return false;
109 }
110
111 m_objStr.streamReset();
112 while ((c = m_objStr.streamGetChar()) != EOF) {
113 fputc(c: c, stream: f);
114 }
115 return true;
116}
117
118FileSpec::FileSpec(const Object *fileSpecA)
119{
120 ok = true;
121 fileName = nullptr;
122 platformFileName = nullptr;
123 embFile = nullptr;
124 desc = nullptr;
125 fileSpec = fileSpecA->copy();
126
127 Object obj1 = getFileSpecName(fileSpec: fileSpecA);
128 if (!obj1.isString()) {
129 ok = false;
130 error(category: errSyntaxError, pos: -1, msg: "Invalid FileSpec");
131 return;
132 }
133
134 fileName = obj1.getString()->copy();
135
136 if (fileSpec.isDict()) {
137 obj1 = fileSpec.dictLookup(key: "EF");
138 if (obj1.isDict()) {
139 fileStream = obj1.dictLookupNF(key: "F").copy();
140 if (!fileStream.isRef()) {
141 ok = false;
142 fileStream.setToNull();
143 error(category: errSyntaxError, pos: -1, msg: "Invalid FileSpec: Embedded file stream is not an indirect reference");
144 return;
145 }
146 }
147
148 obj1 = fileSpec.dictLookup(key: "Desc");
149 if (obj1.isString()) {
150 desc = obj1.getString()->copy();
151 }
152 }
153}
154
155FileSpec::~FileSpec()
156{
157 delete fileName;
158 delete platformFileName;
159 delete embFile;
160 delete desc;
161}
162
163EmbFile *FileSpec::getEmbeddedFile()
164{
165 if (!ok || !fileSpec.isDict()) {
166 return nullptr;
167 }
168
169 if (embFile) {
170 return embFile;
171 }
172
173 XRef *xref = fileSpec.getDict()->getXRef();
174 embFile = new EmbFile(fileStream.fetch(xref));
175
176 return embFile;
177}
178
179Object FileSpec::newFileSpecObject(XRef *xref, GooFile *file, const std::string &fileName)
180{
181 Object paramsDict = Object(new Dict(xref));
182 paramsDict.dictSet(key: "Size", val: Object(file->size()));
183
184 // No Subtype in the embedded file stream dictionary for now
185 Object streamDict = Object(new Dict(xref));
186 streamDict.dictSet(key: "Length", val: Object(file->size()));
187 streamDict.dictSet(key: "Params", val: std::move(paramsDict));
188
189 FileStream *fStream = new FileStream(file, 0, false, file->size(), std::move(streamDict));
190 fStream->setNeedsEncryptionOnSave(true);
191 Stream *stream = fStream;
192 const Ref streamRef = xref->addIndirectObject(o: Object(stream));
193
194 Dict *efDict = new Dict(xref);
195 efDict->set(key: "F", val: Object(streamRef));
196
197 Dict *fsDict = new Dict(xref);
198 fsDict->set(key: "Type", val: Object(objName, "Filespec"));
199 fsDict->set(key: "UF", val: Object(new GooString(fileName)));
200 fsDict->set(key: "EF", val: Object(efDict));
201
202 return Object(fsDict);
203}
204
205GooString *FileSpec::getFileNameForPlatform()
206{
207 if (platformFileName) {
208 return platformFileName;
209 }
210
211 Object obj1 = getFileSpecNameForPlatform(fileSpec: &fileSpec);
212 if (obj1.isString()) {
213 platformFileName = obj1.getString()->copy();
214 }
215
216 return platformFileName;
217}
218
219Object getFileSpecName(const Object *fileSpec)
220{
221 if (fileSpec->isString()) {
222 return fileSpec->copy();
223 }
224
225 if (fileSpec->isDict()) {
226 Object fileName = fileSpec->dictLookup(key: "UF");
227 if (fileName.isString()) {
228 return fileName;
229 }
230 fileName = fileSpec->dictLookup(key: "F");
231 if (fileName.isString()) {
232 return fileName;
233 }
234 fileName = fileSpec->dictLookup(key: "DOS");
235 if (fileName.isString()) {
236 return fileName;
237 }
238 fileName = fileSpec->dictLookup(key: "Mac");
239 if (fileName.isString()) {
240 return fileName;
241 }
242 fileName = fileSpec->dictLookup(key: "Unix");
243 if (fileName.isString()) {
244 return fileName;
245 }
246 }
247 return Object();
248}
249
250Object getFileSpecNameForPlatform(const Object *fileSpec)
251{
252 if (fileSpec->isString()) {
253 return fileSpec->copy();
254 }
255
256 Object fileName;
257 if (fileSpec->isDict()) {
258 fileName = fileSpec->dictLookup(key: "UF");
259 if (!fileName.isString()) {
260 fileName = fileSpec->dictLookup(key: "F");
261 if (!fileName.isString()) {
262#ifdef _WIN32
263 const char *platform = "DOS";
264#else
265 const char *platform = "Unix";
266#endif
267 fileName = fileSpec->dictLookup(key: platform);
268 if (!fileName.isString()) {
269 error(category: errSyntaxError, pos: -1, msg: "Illegal file spec");
270 return Object();
271 }
272 }
273 }
274 } else {
275 error(category: errSyntaxError, pos: -1, msg: "Illegal file spec");
276 return Object();
277 }
278
279 // system-dependent path manipulation
280#ifdef _WIN32
281 int i, j;
282 GooString *name = fileName.getString()->copy();
283 // "//...." --> "\...."
284 // "/x/...." --> "x:\...."
285 // "/server/share/...." --> "\\server\share\...."
286 // convert escaped slashes to slashes and unescaped slashes to backslashes
287 i = 0;
288 if (name->getChar(0) == '/') {
289 if (name->getLength() >= 2 && name->getChar(1) == '/') {
290 name->del(0);
291 i = 0;
292 } else if (name->getLength() >= 2 && ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') || (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) && (name->getLength() == 2 || name->getChar(2) == '/')) {
293 name->setChar(0, name->getChar(1));
294 name->setChar(1, ':');
295 i = 2;
296 } else {
297 for (j = 2; j < name->getLength(); ++j) {
298 if (name->getChar(j - 1) != '\\' && name->getChar(j) == '/') {
299 break;
300 }
301 }
302 if (j < name->getLength()) {
303 name->setChar(0, '\\');
304 name->insert(0, '\\');
305 i = 2;
306 }
307 }
308 }
309 for (; i < name->getLength(); ++i) {
310 if (name->getChar(i) == '/') {
311 name->setChar(i, '\\');
312 } else if (name->getChar(i) == '\\' && i + 1 < name->getLength() && name->getChar(i + 1) == '/') {
313 name->del(i);
314 }
315 }
316 fileName = Object(name);
317#endif /* _WIN32 */
318
319 return fileName;
320}
321

source code of poppler/poppler/FileSpec.cc