/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
/* libmwaw: tools
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/
#include <string.h>
#include <iostream>
#include <memory>
#include <algorithm>
#include "file_internal.h"
#include "input.h"
#include "xattr.h"
#include <sys/stat.h>
#if WITH_EXTENDED_FS
# include <sys/types.h>
# include <sys/xattr.h>
#endif
namespace libmwaw_tools
{
InputStream *XAttr::getStream(const char *attr) const
{
if (m_fName.empty() || !attr)
return 0;
#if WITH_EXTENDED_FS==1
# define MWAW_EXTENDED_FS , 0, XATTR_SHOWCOMPRESSION
#else
# define MWAW_EXTENDED_FS
#endif
#if WITH_EXTENDED_FS
ssize_t sz=getxattr(m_fName.c_str(), attr, 0, 0 MWAW_EXTENDED_FS);
if (sz>0) {
std::unique_ptr<char[]> buffer{new char[size_t(sz)]};
if (buffer==0) {
MWAW_DEBUG_MSG(("XAttr::getStream: can not alloc buffer\n"));
return 0;
}
if (getxattr(m_fName.c_str(), attr, buffer.get(), size_t(sz) MWAW_EXTENDED_FS) != sz) {
MWAW_DEBUG_MSG(("XAttr::getStream: getxattr can read attribute\n"));
return 0;
}
InputStream *res=new StringStream(reinterpret_cast<unsigned char *>(buffer.get()),static_cast<unsigned long>(sz));
return res;
}
#endif
std::unique_ptr<InputStream> auxi{getAuxillarInput()};
if (auxi) {
InputStream *res = unMacMIME(auxi.get(), attr);
return res;
}
return getUsingFinderDat(attr);
}
InputStream *XAttr::getAuxillarInput() const
{
if (m_fName.empty())
return 0;
/** look for file ._NAME or __MACOSX/._NAME
Fixme: Must be probably changed to works on Windows */
size_t sPos=m_fName.rfind('/');
std::string folder(""), file("");
if (sPos==std::string::npos)
file = m_fName;
else {
folder=m_fName.substr(0,sPos+1);
file=m_fName.substr(sPos+1);
}
std::string name=folder+"._"+file;
struct stat status;
if (stat(name.c_str(), &status)==0 && S_ISREG(status.st_mode))
;
else {
name=folder+"__MACOSX/._"+file;
if (stat(name.c_str(), &status) || !S_ISREG(status.st_mode))
return 0;
}
std::unique_ptr<FileStream> res{new FileStream(name.c_str())};
if (res->ok()) {
MWAW_DEBUG_MSG(("XAttr::getAuxillarInput: find an auxilliary file: %s\n",name.c_str()));
return res.release();
}
return 0;
}
/* os9 way to save resource on a Fat,... disk */
InputStream *XAttr::getUsingFinderDat(char const *what) const
{
if (m_fName.empty())
return 0;
bool lookForResourceFork=true;
if (strcmp("com.apple.FinderInfo",what)==0)
lookForResourceFork=false;
else if (strcmp("com.apple.ResourceFork",what))
return 0;
/** look for file FINDER.DAT */
size_t sPos=m_fName.rfind('/');
std::string folder(""), file("");
if (sPos==std::string::npos)
file = m_fName;
else {
folder=m_fName.substr(0,sPos+1);
file=m_fName.substr(sPos+1);
}
std::string name=folder+"FINDER.DAT";
struct stat status;
if (stat(name.c_str(), &status)!=0 || !S_ISREG(status.st_mode))
return 0;
std::unique_ptr<FileStream> input{new FileStream(name.c_str())};
if (!input || !input->ok()) {
return 0;
}
input->seek(0, InputStream::SK_SET);
try {
int num=0;
while (!input->atEOS()) {
if (++num==23) { // must realign read on 2048
if (input->seek(24, InputStream::SK_CUR))
break;
num=0;
}
long pos=input->tell();
if (input->seek(pos+92, InputStream::SK_SET))
break;
input->seek(pos, InputStream::SK_SET);
auto nSz=static_cast<int>(input->readU8());
if (nSz<=0 || nSz>31) {
MWAW_DEBUG_MSG(("XAttr::readFinderDat: file size seems bad %d\n", nSz));
input->seek(pos+92, InputStream::SK_SET);
continue;
}
std::string fName("");
for (int i=0; i<nSz; ++i)
fName+=char(input->readU8());
if (fName!=file) {
input->seek(pos+92, InputStream::SK_SET);
continue;
}
// fInfo or fInfo
if (!lookForResourceFork) {
input->seek(pos+32, InputStream::SK_SET);
unsigned long numBytesRead = 0;
const unsigned char *data = input->read(16, numBytesRead);
if (numBytesRead != 16 || !data) {
MWAW_DEBUG_MSG(("XAttr::readFinderDat: can not read fileinfo\n"));
break;
}
return new StringStream(data,static_cast<unsigned long>(16));
}
input->seek(pos+80, InputStream::SK_SET);
std::string rsrcName("");
for (int i=0; i<8; ++i) {
auto c=char(input->readU8());
if (!c) break;
rsrcName += c;
}
for (int i=0; i <3; ++i) { // extension
auto c=char(input->readU8());
if (!c) break;
if (i==0) rsrcName += '.';
rsrcName += c;
}
name=folder+"RESOURCE.FRK/"+rsrcName;
if (stat(name.c_str(), &status)!=0 || !S_ISREG(status.st_mode))
break;
input.reset(new FileStream(name.c_str()));
if (!input || !input->ok())
break;
return input.release();
}
}
catch (...) {
}
return 0;
}
/* freely inspired from http://tools.ietf.org/html/rfc1740#appendix-A
*/
InputStream *XAttr::unMacMIME(InputStream *inp, char const *what) const
{
if (!inp || !what) return 0;
int lookForId=0;
if (strcmp("com.apple.FinderInfo",what)==0)
lookForId=9;
else if (strcmp("com.apple.ResourceFork",what)==0)
lookForId=2;
else if (strcmp("com.apple.DataFork",what)==0)
lookForId=1;
if (lookForId==0) return 0;
try {
inp->seek(0, InputStream::SK_SET);
auto magicNumber = long(inp->readU32());
if (magicNumber != 0x00051600 && magicNumber != 0x00051607)
return 0;
auto version = long(inp->readU32());
if (version != 0x20000) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: unknown version: %lx\n", static_cast<long unsigned int>(version)));
return 0;
}
inp->seek(16, InputStream::SK_CUR); // filename
auto numEntries = long(inp->readU16());
if (inp->atEOS() || numEntries <= 0) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: can not read number of entries\n"));
return 0;
}
for (int i = 0; i < numEntries; i++) {
long pos = inp->tell();
auto wh = static_cast<int>(inp->readU32());
if (wh <= 0 || wh >= 16 || inp->atEOS()) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: find unknown id: %d\n", wh));
return 0;
}
MWAW_DEBUG_MSG(("XAttr::unMacMIME: find entry %d\n", wh));
if (wh != lookForId) {
inp->seek(8, InputStream::SK_CUR);
continue;
}
auto entryPos = long(inp->readU32());
auto entrySize = static_cast<unsigned long>(inp->readU32());
if (entrySize == 0) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: entry %d is empty\n", wh));
return 0;
}
if (entryPos <= pos || entrySize == 0) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: find bad entry pos\n"));
return 0;
}
/* try to read the data */
inp->seek(entryPos, InputStream::SK_SET);
if (inp->tell() != entryPos) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: can not seek entry pos %lx\n", static_cast<long unsigned int>(entryPos)));
return 0;
}
unsigned long numBytesRead = 0;
const unsigned char *data = inp->read(entrySize, numBytesRead);
if (numBytesRead != entrySize || !data) {
MWAW_DEBUG_MSG(("XAttr::unMacMIME: can not read %lX byte\n", static_cast<long unsigned int>(entryPos)));
return 0;
}
return new StringStream(data,static_cast<unsigned long>(entrySize));
}
}
catch (...) {
return 0;
}
return 0;
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: