blob: 849ad045ff9b70d7df98af9077dea165e38d9971 [file] [log] [blame]
//========================================================================
//
// FileSpec.cc
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2008-2009 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
// Copyright (C) 2012, 2017-2021 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2012 Hib Eris <hib@hiberis.nl>
// 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
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
// Copyright (C) 2019 Christian Persch <chpe@src.gnome.org>
// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
//========================================================================
//
// Most of the code from Link.cc and PSOutputDev.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================
#include <config.h>
#include "FileSpec.h"
#include "XRef.h"
#include "goo/gfile.h"
EmbFile::EmbFile(Object &&efStream)
{
m_size = -1;
m_createDate = nullptr;
m_modDate = nullptr;
m_checksum = nullptr;
m_mimetype = nullptr;
m_objStr = std::move(efStream);
if (m_objStr.isStream()) {
// dataDict corresponds to Table 3.41 in the PDF1.6 spec.
Dict *dataDict = m_objStr.streamGetDict();
// subtype is normally the mimetype
Object subtypeName = dataDict->lookup("Subtype");
if (subtypeName.isName()) {
m_mimetype = new GooString(subtypeName.getName());
}
// paramDict corresponds to Table 3.42 in the PDF1.6 spec
Object paramDict = dataDict->lookup("Params");
if (paramDict.isDict()) {
Object paramObj = paramDict.dictLookup("ModDate");
if (paramObj.isString()) {
m_modDate = new GooString(paramObj.getString());
}
paramObj = paramDict.dictLookup("CreationDate");
if (paramObj.isString()) {
m_createDate = new GooString(paramObj.getString());
}
paramObj = paramDict.dictLookup("Size");
if (paramObj.isInt()) {
m_size = paramObj.getInt();
}
paramObj = paramDict.dictLookup("CheckSum");
if (paramObj.isString()) {
m_checksum = new GooString(paramObj.getString());
}
}
}
}
EmbFile::~EmbFile()
{
delete m_createDate;
delete m_modDate;
delete m_checksum;
delete m_mimetype;
}
bool EmbFile::save(const std::string &path)
{
FILE *f;
bool ret;
if (!(f = openFile(path.c_str(), "wb"))) {
return false;
}
ret = save2(f);
fclose(f);
return ret;
}
bool EmbFile::save2(FILE *f)
{
int c;
if (unlikely(!m_objStr.isStream())) {
return false;
}
m_objStr.streamReset();
while ((c = m_objStr.streamGetChar()) != EOF) {
fputc(c, f);
}
return true;
}
FileSpec::FileSpec(const Object *fileSpecA)
{
ok = true;
fileName = nullptr;
platformFileName = nullptr;
embFile = nullptr;
desc = nullptr;
fileSpec = fileSpecA->copy();
Object obj1 = getFileSpecName(fileSpecA);
if (!obj1.isString()) {
ok = false;
error(errSyntaxError, -1, "Invalid FileSpec");
return;
}
fileName = obj1.getString()->copy();
if (fileSpec.isDict()) {
obj1 = fileSpec.dictLookup("EF");
if (obj1.isDict()) {
fileStream = obj1.dictLookupNF("F").copy();
if (!fileStream.isRef()) {
ok = false;
fileStream.setToNull();
error(errSyntaxError, -1, "Invalid FileSpec: Embedded file stream is not an indirect reference");
return;
}
}
obj1 = fileSpec.dictLookup("Desc");
if (obj1.isString()) {
desc = obj1.getString()->copy();
}
}
}
FileSpec::~FileSpec()
{
delete fileName;
delete platformFileName;
delete embFile;
delete desc;
}
EmbFile *FileSpec::getEmbeddedFile()
{
if (!ok || !fileSpec.isDict()) {
return nullptr;
}
if (embFile) {
return embFile;
}
XRef *xref = fileSpec.getDict()->getXRef();
embFile = new EmbFile(fileStream.fetch(xref));
return embFile;
}
Object FileSpec::newFileSpecObject(XRef *xref, GooFile *file, const std::string &fileName)
{
Object paramsDict = Object(new Dict(xref));
paramsDict.dictSet("Size", Object(file->size()));
// No Subtype in the embedded file stream dictionary for now
Object streamDict = Object(new Dict(xref));
streamDict.dictSet("Length", Object(file->size()));
streamDict.dictSet("Params", std::move(paramsDict));
FileStream *fStream = new FileStream(file, 0, false, file->size(), std::move(streamDict));
fStream->setNeedsEncryptionOnSave(true);
Stream *stream = fStream;
const Ref streamRef = xref->addIndirectObject(Object(stream));
Dict *efDict = new Dict(xref);
efDict->set("F", Object(streamRef));
Dict *fsDict = new Dict(xref);
fsDict->set("Type", Object(objName, "Filespec"));
fsDict->set("UF", Object(new GooString(fileName)));
fsDict->set("EF", Object(efDict));
return Object(fsDict);
}
GooString *FileSpec::getFileNameForPlatform()
{
if (platformFileName) {
return platformFileName;
}
Object obj1 = getFileSpecNameForPlatform(&fileSpec);
if (obj1.isString()) {
platformFileName = obj1.getString()->copy();
}
return platformFileName;
}
Object getFileSpecName(const Object *fileSpec)
{
if (fileSpec->isString()) {
return fileSpec->copy();
}
if (fileSpec->isDict()) {
Object fileName = fileSpec->dictLookup("UF");
if (fileName.isString()) {
return fileName;
}
fileName = fileSpec->dictLookup("F");
if (fileName.isString()) {
return fileName;
}
fileName = fileSpec->dictLookup("DOS");
if (fileName.isString()) {
return fileName;
}
fileName = fileSpec->dictLookup("Mac");
if (fileName.isString()) {
return fileName;
}
fileName = fileSpec->dictLookup("Unix");
if (fileName.isString()) {
return fileName;
}
}
return Object();
}
Object getFileSpecNameForPlatform(const Object *fileSpec)
{
if (fileSpec->isString()) {
return fileSpec->copy();
}
Object fileName;
if (fileSpec->isDict()) {
fileName = fileSpec->dictLookup("UF");
if (!fileName.isString()) {
fileName = fileSpec->dictLookup("F");
if (!fileName.isString()) {
#ifdef _WIN32
const char *platform = "DOS";
#else
const char *platform = "Unix";
#endif
fileName = fileSpec->dictLookup(platform);
if (!fileName.isString()) {
error(errSyntaxError, -1, "Illegal file spec");
return Object();
}
}
}
} else {
error(errSyntaxError, -1, "Illegal file spec");
return Object();
}
// system-dependent path manipulation
#ifdef _WIN32
int i, j;
GooString *name = fileName.getString()->copy();
// "//...." --> "\...."
// "/x/...." --> "x:\...."
// "/server/share/...." --> "\\server\share\...."
// convert escaped slashes to slashes and unescaped slashes to backslashes
i = 0;
if (name->getChar(0) == '/') {
if (name->getLength() >= 2 && name->getChar(1) == '/') {
name->del(0);
i = 0;
} 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) == '/')) {
name->setChar(0, name->getChar(1));
name->setChar(1, ':');
i = 2;
} else {
for (j = 2; j < name->getLength(); ++j) {
if (name->getChar(j - 1) != '\\' && name->getChar(j) == '/') {
break;
}
}
if (j < name->getLength()) {
name->setChar(0, '\\');
name->insert(0, '\\');
i = 2;
}
}
}
for (; i < name->getLength(); ++i) {
if (name->getChar(i) == '/') {
name->setChar(i, '\\');
} else if (name->getChar(i) == '\\' && i + 1 < name->getLength() && name->getChar(i + 1) == '/') {
name->del(i);
}
}
fileName = Object(name);
#endif /* _WIN32 */
return fileName;
}