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