gecko/xpinstall/src/nsAppleSingleDecoder.cpp

714 lines
18 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. 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.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Samir Gehani <sgehani@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _NS_APPLESINGLEDECODER_H_
#include "nsAppleSingleDecoder.h"
#endif
#include "MoreFilesX.h"
#ifdef STANDALONE_ASD
#include <iostream>
using namespace std;
#undef MOZILLA_CLIENT
#endif
#ifdef MOZILLA_CLIENT
#include "nsISupportsUtils.h"
#endif
/*----------------------------------------------------------------------*
* Constructors/Destructor
*----------------------------------------------------------------------*/
nsAppleSingleDecoder::nsAppleSingleDecoder(const FSRef *inRef, FSRef *outRef)
: mInRef(NULL),
mOutRef(NULL),
mInRefNum(0),
mRenameReqd(false)
{
#ifdef MOZILLA_CLIENT
MOZ_COUNT_CTOR(nsAppleSingleDecoder);
#endif
if (inRef && outRef)
{
/* merely point to FSRefs, not own 'em */
mInRef = inRef;
mOutRef = outRef;
}
}
nsAppleSingleDecoder::nsAppleSingleDecoder()
: mInRef(NULL),
mOutRef(NULL),
mInRefNum(0),
mRenameReqd(false)
{
#ifdef MOZILLA_CLIENT
MOZ_COUNT_CTOR(nsAppleSingleDecoder);
#endif
}
nsAppleSingleDecoder::~nsAppleSingleDecoder()
{
/* not freeing FSRefs since we don't own 'em */
#ifdef MOZILLA_CLIENT
MOZ_COUNT_DTOR(nsAppleSingleDecoder);
#endif
}
#pragma mark -
/*----------------------------------------------------------------------*
* Public methods
*----------------------------------------------------------------------*/
OSErr
nsAppleSingleDecoder::Decode()
{
OSErr err = noErr;
ASHeader header;
UInt32 bytesRead = sizeof(header);
HFSUniStr255 nameInRef;
// param check
if (!mInRef || !mOutRef)
return paramErr;
// check for existence (and get leaf name for later renaming of decoded file)
err = FSGetCatalogInfo(mInRef, kFSCatInfoNone, NULL, &nameInRef, NULL, NULL);
if (err == fnfErr)
return err;
HFSUniStr255 dataForkName;
MAC_ERR_CHECK(FSGetDataForkName( &dataForkName ));
MAC_ERR_CHECK(FSOpenFork( mInRef, dataForkName.length, dataForkName.unicode,
fsRdPerm, &mInRefNum ));
MAC_ERR_CHECK(FSRead( mInRefNum, (long *)&bytesRead, &header ));
if ( (bytesRead != sizeof(header)) ||
(header.magicNum != APPLESINGLE_MAGIC) ||
(header.versionNum != APPLESINGLE_VERSION) ||
(header.numEntries == 0) ) // empty file?
return -1;
// create the outSpec which we'll rename correctly later
FSRef parentRef;
MAC_ERR_CHECK(FSGetParentRef( mInRef, &parentRef ));
MAC_ERR_CHECK(FSMakeUnique( &parentRef, mOutRef ));
MAC_ERR_CHECK(FSCreateFork( mOutRef, dataForkName.length,
dataForkName.unicode ));
/* Loop through the entries, processing each.
** Set the time/date stamps last, because otherwise they'll
** be destroyed when we write.
*/
{
Boolean hasDateEntry = false;
ASEntry dateEntry;
long offset;
ASEntry entry;
for ( int i=0; i < header.numEntries; i++ )
{
offset = sizeof( ASHeader ) + sizeof( ASEntry ) * i;
MAC_ERR_CHECK(SetFPos( mInRefNum, fsFromStart, offset ));
bytesRead = sizeof(entry);
MAC_ERR_CHECK(FSRead( mInRefNum, (long *) &bytesRead, &entry ));
if (bytesRead != sizeof(entry))
return -1;
if ( entry.entryID == AS_FILEDATES )
{
hasDateEntry = true;
dateEntry = entry;
}
else
MAC_ERR_CHECK(ProcessASEntry( entry ));
}
if ( hasDateEntry )
MAC_ERR_CHECK(ProcessASEntry( dateEntry ));
}
// close the inSpec
FSClose( mInRefNum );
// rename if need be
if (mRenameReqd)
{
// delete encoded version of target file
MAC_ERR_CHECK(FSDeleteObject( mInRef ));
MAC_ERR_CHECK(FSRenameUnicode( mOutRef, nameInRef.length,
nameInRef.unicode, kTextEncodingUnknown, mOutRef ));
mRenameReqd = false;
}
return err;
}
OSErr
nsAppleSingleDecoder::Decode(const FSRef *inRef, FSRef *outRef)
{
OSErr err = noErr;
// param check
if (inRef && outRef)
{
mInRef = inRef; // reinit
mOutRef = outRef;
mRenameReqd = false;
}
else
return paramErr;
err = Decode();
return err;
}
Boolean
DecodeDirIterateFilter(Boolean containerChanged, ItemCount currentLevel,
const FSCatalogInfo *catalogInfo, const FSRef *ref,
const FSSpec *spec, const HFSUniStr255 *name, void *yourDataPtr)
{
FSRef outRef;
nsAppleSingleDecoder *thisObj;
Boolean isDir;
// param check
if (!yourDataPtr || !ref)
return false;
// extract 'this' -- an nsAppleSingleDecoder instance
thisObj = (nsAppleSingleDecoder*) yourDataPtr;
isDir = nsAppleSingleDecoder::IsDirectory(ref);
// if current FSRef is file
if (!isDir)
{
// if file is in AppleSingle format
if (nsAppleSingleDecoder::IsAppleSingleFile(ref))
{
// decode file
thisObj->Decode(ref, &outRef);
}
}
// else current FSRef is folder
else
{
thisObj->DecodeFolder(ref);
}
return false; // always continue iteration
}
OSErr
nsAppleSingleDecoder::DecodeFolder(const FSRef *aFolder)
{
OSErr err;
Boolean isDir = false;
// check that FSSpec is folder
if (aFolder)
{
isDir = IsDirectory((const FSRef *) aFolder);
if (!isDir)
return dirNFErr;
}
// recursively enumerate contents of folder
// (maxLevels=0 means recurse all)
err = FSIterateContainer(aFolder, 0, kFSCatInfoNone, false,
false, DecodeDirIterateFilter, (void*)this);
// XXX do we really want to return err?
return err;
}
Boolean
nsAppleSingleDecoder::IsAppleSingleFile(const FSRef *inRef)
{
OSErr err;
SInt16 inRefNum;
UInt32 magic;
long bytesRead = sizeof(magic);
// param checks
if (!inRef)
return false;
// check for existence
err = FSGetCatalogInfo(inRef, kFSCatInfoNone, NULL, NULL, NULL, NULL);
if (err!=noErr)
return false;
// open and read the magic number len bytes
HFSUniStr255 dataForkName;
err = FSGetDataForkName( &dataForkName );
if (err!=noErr)
return false;
err = FSOpenFork( inRef, dataForkName.length, dataForkName.unicode,
fsRdPerm, &inRefNum );
if (err!=noErr)
return false;
err = FSRead( inRefNum, &bytesRead, &magic );
if (err!=noErr)
return false;
FSClose(inRefNum);
if (bytesRead != sizeof(magic))
return false;
// check if bytes read match magic number
return (magic == APPLESINGLE_MAGIC);
}
#pragma mark -
/*----------------------------------------------------------------------*
* Private methods
*----------------------------------------------------------------------*/
OSErr
nsAppleSingleDecoder::ProcessASEntry(ASEntry inEntry)
{
switch (inEntry.entryID)
{
case AS_DATA:
return ProcessDataFork( inEntry );
break;
case AS_RESOURCE:
return ProcessResourceFork( inEntry );
break;
case AS_REALNAME:
ProcessRealName( inEntry );
break;
case AS_FILEDATES:
return ProcessFileDates( inEntry );
break;
case AS_FINDERINFO:
return ProcessFinderInfo( inEntry );
break;
case AS_MACINFO:
case AS_COMMENT:
case AS_ICONBW:
case AS_ICONCOLOR:
case AS_PRODOSINFO:
case AS_MSDOSINFO:
case AS_AFPNAME:
case AS_AFPINFO:
case AS_AFPDIRID:
default:
return 0;
}
return 0;
}
OSErr
nsAppleSingleDecoder::ProcessDataFork(ASEntry inEntry)
{
OSErr err = noErr;
SInt16 refNum;
/* Setup the files */
HFSUniStr255 dataForkName;
err = FSGetDataForkName( &dataForkName );
if (err != noErr)
return err;
err = FSOpenFork( mOutRef, dataForkName.length, dataForkName.unicode,
fsWrPerm, &refNum );
if ( err == noErr )
err = EntryToMacFile( inEntry, refNum );
FSClose( refNum );
return err;
}
OSErr
nsAppleSingleDecoder::ProcessResourceFork(ASEntry inEntry)
{
OSErr err = noErr;
SInt16 refNum;
HFSUniStr255 rsrcForkName;
err = FSGetResourceForkName( &rsrcForkName );
if (err != noErr)
return err;
err = FSOpenFork( mOutRef, rsrcForkName.length, rsrcForkName.unicode,
fsWrPerm, &refNum );
if ( err == noErr )
err = EntryToMacFile( inEntry, refNum );
FSClose( refNum );
return err;
}
OSErr
nsAppleSingleDecoder::ProcessRealName(ASEntry inEntry)
{
OSErr err = noErr;
Str255 newName;
HFSUniStr255 newNameUni;
UInt32 bytesRead;
FSRef parentOfOutRef;
MAC_ERR_CHECK(SetFPos(mInRefNum, fsFromStart, inEntry.entryOffset));
bytesRead = inEntry.entryLength;
MAC_ERR_CHECK(FSRead(mInRefNum, (long *) &bytesRead, &newName[1]));
if (bytesRead != inEntry.entryLength)
return -1;
newName[0] = inEntry.entryLength;
MAC_ERR_CHECK(FSGetParentRef(mOutRef, &parentOfOutRef));
MAC_ERR_CHECK(HFSNameGetUnicodeName(newName, kTextEncodingUnknown,
&newNameUni));
err = FSRenameUnicode(mOutRef, newNameUni.length, newNameUni.unicode,
kTextEncodingUnknown, mOutRef);
if (err == dupFNErr)
{
HFSUniStr255 inRefName;
MAC_ERR_CHECK(FSGetCatalogInfo(mInRef, kFSCatInfoNone, NULL, &inRefName,
NULL, NULL));
// if we are trying to rename temp decode file to src name, rename later
if (nsAppleSingleDecoder::UCstrcmp(&newNameUni, &inRefName))
{
mRenameReqd = true;
return noErr;
}
// otherwise replace file in the way
FSRef inTheWayRef;
// delete file in the way
MAC_ERR_CHECK(FSMakeFSRefUnicode( &parentOfOutRef, newNameUni.length,
newNameUni.unicode, kTextEncodingUnknown, &inTheWayRef ));
MAC_ERR_CHECK(FSDeleteObject( &inTheWayRef ));
// rename decoded file to ``newName''
MAC_ERR_CHECK(FSRenameUnicode( mOutRef, newNameUni.length,
newNameUni.unicode, kTextEncodingUnknown, mOutRef ));
}
return err;
}
OSErr
nsAppleSingleDecoder::ProcessFileDates(ASEntry inEntry)
{
OSErr err = noErr;
ASFileDates dates;
UInt32 bytesRead;
FSCatalogInfo catInfo;
if ( inEntry.entryLength != sizeof(dates) )
return -1;
MAC_ERR_CHECK(SetFPos(mInRefNum, fsFromStart, inEntry.entryOffset));
bytesRead = inEntry.entryLength;
MAC_ERR_CHECK(FSRead(mInRefNum, (long *) &bytesRead, &dates));
if (bytesRead != inEntry.entryLength)
return -1;
#define YR_2000_SECONDS 3029529600
LocalDateTime local = (LocalDateTime) {0, 0, 0};
// set creation date
local.lowSeconds = dates.create + YR_2000_SECONDS;
ConvertLocalToUTCDateTime(&local, &catInfo.createDate);
// set content modification date
local.lowSeconds = dates.modify + YR_2000_SECONDS;
ConvertLocalToUTCDateTime(&local, &catInfo.contentModDate);
// set last access date
local.lowSeconds = dates.access + YR_2000_SECONDS;
ConvertLocalToUTCDateTime(&local, &catInfo.accessDate);
// set backup date
local.lowSeconds = dates.backup + YR_2000_SECONDS;
ConvertLocalToUTCDateTime(&local, &catInfo.backupDate);
// set attribute modification date
GetUTCDateTime(&catInfo.attributeModDate, kUTCDefaultOptions);
MAC_ERR_CHECK(FSSetCatalogInfo(mOutRef,
kFSCatInfoCreateDate |
kFSCatInfoContentMod |
kFSCatInfoAttrMod |
kFSCatInfoAccessDate |
kFSCatInfoBackupDate,
&catInfo));
return err;
}
OSErr
nsAppleSingleDecoder::ProcessFinderInfo(ASEntry inEntry)
{
OSErr err = noErr;
ASFinderInfo info;
UInt32 bytesRead;
FSCatalogInfo catInfo;
if (inEntry.entryLength != sizeof( ASFinderInfo ))
return -1;
MAC_ERR_CHECK(SetFPos(mInRefNum, fsFromStart, inEntry.entryOffset));
bytesRead = sizeof(info);
MAC_ERR_CHECK(FSRead(mInRefNum, (long *) &bytesRead, &info));
if (bytesRead != inEntry.entryLength)
return -1;
MAC_ERR_CHECK(FSGetCatalogInfo(mOutRef, kFSCatInfoGettableInfo, &catInfo,
NULL, NULL, NULL));
BlockMoveData((const void *) &info.ioFlFndrInfo,
(void *) &catInfo.finderInfo, sizeof(FInfo));
BlockMoveData((const void *) &info.ioFlXFndrInfo,
(void *) &catInfo.extFinderInfo, sizeof(FXInfo));
MAC_ERR_CHECK(FSSetCatalogInfo(mOutRef,
kFSCatInfoFinderInfo |
kFSCatInfoFinderXInfo,
&catInfo));
return err;
}
OSErr
nsAppleSingleDecoder::EntryToMacFile(ASEntry inEntry, UInt16 inTargetSpecRefNum)
{
#define BUFFER_SIZE 8192
OSErr err = noErr;
char buffer[BUFFER_SIZE];
UInt32 totalRead = 0, bytesRead, bytesToWrite;
MAC_ERR_CHECK(SetFPos( mInRefNum, fsFromStart, inEntry.entryOffset ));
while ( totalRead < inEntry.entryLength )
{
// Should we yield in here?
bytesRead = BUFFER_SIZE;
err = FSRead( mInRefNum, (long *) &bytesRead, buffer );
if (err!=noErr && err!=eofErr)
return err;
if ( bytesRead <= 0 )
return -1;
bytesToWrite = totalRead + bytesRead > inEntry.entryLength ?
inEntry.entryLength - totalRead :
bytesRead;
totalRead += bytesRead;
MAC_ERR_CHECK(FSWrite(inTargetSpecRefNum, (long *) &bytesToWrite,
buffer));
}
return err;
}
#pragma mark -
Boolean
nsAppleSingleDecoder::IsDirectory(const FSRef *inRef)
{
OSErr err;
Boolean isDir;
if (!inRef)
return false;
err = FSGetFinderInfo(inRef, NULL, NULL, &isDir);
if (err != noErr)
return false;
return isDir;
}
OSErr
nsAppleSingleDecoder::FSMakeUnique(const FSRef *inParentRef, FSRef *outRef)
{
/*
** This function has descended from Apple Sample Code, but we have
** made changes. Specifically, this function descends from the
** GenerateUniqueHFSUniStr() function in MoreFilesX.c, version 1.0.1.
*/
OSErr result = noErr;
long i, startSeed = 0x4a696d4c; /* a fine unlikely filename */
FSRefParam pb;
unsigned char hexStr[17] = "0123456789ABCDEF";
HFSUniStr255 uniqueName;
/* set up the parameter block */
pb.name = uniqueName.unicode;
pb.nameLength = 8; /* always 8 characters */
pb.textEncodingHint = kTextEncodingUnknown;
pb.newRef = outRef;
pb.ref = inParentRef;
/* loop until we get fnfErr with a filename in both directories */
result = noErr;
while ( fnfErr != result )
{
/* convert startSeed to 8 character Unicode string */
uniqueName.length = 8;
for ( i = 0; i < 8; ++i )
{
uniqueName.unicode[i] = hexStr[((startSeed >> ((7-i)*4)) & 0xf)];
}
result = PBMakeFSRefUnicodeSync(&pb);
if ( fnfErr == result )
{
OSErr err;
MAC_ERR_CHECK(FSCreateFileUnicode(inParentRef, uniqueName.length,
uniqueName.unicode, kFSCatInfoNone, NULL, outRef, NULL));
return noErr;
}
else if ( noErr != result)
{
/* exit if anything other than noErr or fnfErr */
return result;
}
/* increment seed for next pass through loop */
++(startSeed);
}
return result;
}
/*----------------------------------------------------------------------*
* Utilities
*----------------------------------------------------------------------*/
Boolean
nsAppleSingleDecoder::UCstrcmp(const HFSUniStr255 *str1,
const HFSUniStr255 *str2)
{
OSStatus status;
Boolean bEqual;
status = UCCompareTextDefault(kUCCollateStandardOptions, str1->unicode,
str1->length, str2->unicode, str2->length,
&bEqual, NULL);
if (status != noErr)
return false;
return bEqual;
}
#ifdef STANDALONE_ASD
int
main(int argc, char **argv)
{
OSErr err;
FSRef encoded, decoded;
nsAppleSingleDecoder *decoder = new nsAppleSingleDecoder;
Boolean isDir;
if (argc < 2)
{
cout << "usage: " << argv[0] << " <file/folder>" << endl
<< "\t\tAppleSingle encoded file path" << endl
<< "\t\tor" << endl
<< "\t\tfolder path containing AppleSingle encoded files." << endl;
exit(-1);
}
err = FSPathMakeRef((const UInt8 *)argv[1], &encoded, &isDir);
if (err != noErr)
return 1;
// handle AppleSingle encoded files
if (!isDir)
{
Boolean isEncoded = nsAppleSingleDecoder::IsAppleSingleFile(&encoded);
cout << "IsAppleSingleFile returned: " << (isEncoded ? "true" : "false")
<< endl;
if (isEncoded)
{
err = decoder->Decode(&encoded, &decoded);
cout << "Decode returned: " << err << endl;
}
}
// handle folders containing AppleSingle encoded files
else
{
err = decoder->DecodeFolder(&encoded);
cout << "DecodeFolder returned: " << err << endl;
}
// verify out file (n/a for folders)
if (!isDir)
{
err = FSGetCatalogInfo(&decoded, kFSCatInfoNone, NULL, NULL, NULL, NULL);
if (err == noErr)
{
cout << "Decoded file appears to exist" << endl;
char path[1024];
FSRefMakePath(&decoded, (UInt8 *)path, 1024);
cout << "Decoded file path: " << path << endl;
}
else
cout << "Decoded file doesn't appear to exist: " << err << endl;
}
return 0;
}
#endif /* STANDALONE_ASD */