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