michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDecodeAppleFile.h" michael@0: #include "prmem.h" michael@0: #include "nsCRT.h" michael@0: michael@0: michael@0: NS_IMPL_ADDREF(nsDecodeAppleFile) michael@0: NS_IMPL_RELEASE(nsDecodeAppleFile) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsDecodeAppleFile) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream) michael@0: NS_INTERFACE_MAP_ENTRY(nsIOutputStream) michael@0: NS_INTERFACE_MAP_END_THREADSAFE michael@0: michael@0: nsDecodeAppleFile::nsDecodeAppleFile() michael@0: { michael@0: m_state = parseHeaders; michael@0: m_dataBufferLength = 0; michael@0: m_dataBuffer = (unsigned char*) PR_MALLOC(MAX_BUFFERSIZE); michael@0: m_entries = nullptr; michael@0: m_rfRefNum = -1; michael@0: m_totalDataForkWritten = 0; michael@0: m_totalResourceForkWritten = 0; michael@0: m_headerOk = false; michael@0: michael@0: m_comment[0] = 0; michael@0: memset(&m_dates, 0, sizeof(m_dates)); michael@0: memset(&m_finderInfo, 0, sizeof(m_dates)); michael@0: memset(&m_finderExtraInfo, 0, sizeof(m_dates)); michael@0: } michael@0: michael@0: nsDecodeAppleFile::~nsDecodeAppleFile() michael@0: { michael@0: michael@0: PR_FREEIF(m_dataBuffer); michael@0: if (m_entries) michael@0: delete [] m_entries; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::Initialize(nsIOutputStream *output, nsIFile *file) michael@0: { michael@0: m_output = output; michael@0: michael@0: nsCOMPtr macFile = do_QueryInterface(file); michael@0: macFile->GetTargetFSSpec(&m_fsFileSpec); michael@0: michael@0: m_offset = 0; michael@0: m_dataForkOffset = 0; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::Close(void) michael@0: { michael@0: nsresult rv; michael@0: rv = m_output->Close(); michael@0: michael@0: int32_t i; michael@0: michael@0: if (m_rfRefNum != -1) michael@0: FSClose(m_rfRefNum); michael@0: michael@0: /* Check if the file is complete and if it's the case, write file attributes */ michael@0: if (m_headerOk) michael@0: { michael@0: bool dataOk = true; /* It's ok if the file doesn't have a datafork, therefore set it to true by default. */ michael@0: if (m_headers.magic == APPLESINGLE_MAGIC) michael@0: { michael@0: for (i = 0; i < m_headers.entriesCount; i ++) michael@0: if (ENT_DFORK == m_entries[i].id) michael@0: { michael@0: dataOk = (bool)(m_totalDataForkWritten == m_entries[i].length); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: bool resourceOk = FALSE; michael@0: for (i = 0; i < m_headers.entriesCount; i ++) michael@0: if (ENT_RFORK == m_entries[i].id) michael@0: { michael@0: resourceOk = (bool)(m_totalResourceForkWritten == m_entries[i].length); michael@0: break; michael@0: } michael@0: michael@0: if (dataOk && resourceOk) michael@0: { michael@0: HFileInfo *fpb; michael@0: CInfoPBRec cipbr; michael@0: michael@0: fpb = (HFileInfo *) &cipbr; michael@0: fpb->ioVRefNum = m_fsFileSpec.vRefNum; michael@0: fpb->ioDirID = m_fsFileSpec.parID; michael@0: fpb->ioNamePtr = m_fsFileSpec.name; michael@0: fpb->ioFDirIndex = 0; michael@0: PBGetCatInfoSync(&cipbr); michael@0: michael@0: /* set finder info */ michael@0: memcpy(&fpb->ioFlFndrInfo, &m_finderInfo, sizeof (FInfo)); michael@0: memcpy(&fpb->ioFlXFndrInfo, &m_finderExtraInfo, sizeof (FXInfo)); michael@0: fpb->ioFlFndrInfo.fdFlags &= 0xfc00; /* clear flags maintained by finder */ michael@0: michael@0: /* set file dates */ michael@0: fpb->ioFlCrDat = m_dates.create - CONVERT_TIME; michael@0: fpb->ioFlMdDat = m_dates.modify - CONVERT_TIME; michael@0: fpb->ioFlBkDat = m_dates.backup - CONVERT_TIME; michael@0: michael@0: /* update file info */ michael@0: fpb->ioDirID = fpb->ioFlParID; michael@0: PBSetCatInfoSync(&cipbr); michael@0: michael@0: /* set comment */ michael@0: IOParam vinfo; michael@0: GetVolParmsInfoBuffer vp; michael@0: DTPBRec dtp; michael@0: michael@0: memset((void *) &vinfo, 0, sizeof (vinfo)); michael@0: vinfo.ioVRefNum = fpb->ioVRefNum; michael@0: vinfo.ioBuffer = (Ptr) &vp; michael@0: vinfo.ioReqCount = sizeof (vp); michael@0: if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr && ((vp.vMAttrib >> bHasDesktopMgr) & 1)) michael@0: { michael@0: memset((void *) &dtp, 0, sizeof (dtp)); michael@0: dtp.ioVRefNum = fpb->ioVRefNum; michael@0: if (PBDTGetPath(&dtp) == noErr) michael@0: { michael@0: dtp.ioDTBuffer = (Ptr) &m_comment[1]; michael@0: dtp.ioNamePtr = fpb->ioNamePtr; michael@0: dtp.ioDirID = fpb->ioDirID; michael@0: dtp.ioDTReqCount = m_comment[0]; michael@0: if (PBDTSetCommentSync(&dtp) == noErr) michael@0: PBDTFlushSync(&dtp); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::Flush(void) michael@0: { michael@0: return m_output->Flush(); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) michael@0: { michael@0: return m_output->WriteFrom(inStr, count, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) michael@0: { michael@0: return m_output->WriteSegments(reader, closure, count, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::IsNonBlocking(bool *aNonBlocking) michael@0: { michael@0: return m_output->IsNonBlocking(aNonBlocking); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsDecodeAppleFile::Write(const char *buffer, uint32_t bufferSize, uint32_t* writeCount) michael@0: { michael@0: /* WARNING: to simplify my life, I presume that I should get all appledouble headers in the first block, michael@0: else I would have to implement a buffer */ michael@0: michael@0: const char * buffPtr = buffer; michael@0: uint32_t dataCount; michael@0: int32_t i; michael@0: nsresult rv = NS_OK; michael@0: michael@0: *writeCount = 0; michael@0: michael@0: while (bufferSize > 0 && NS_SUCCEEDED(rv)) michael@0: { michael@0: switch (m_state) michael@0: { michael@0: case parseHeaders : michael@0: dataCount = sizeof(ap_header) - m_dataBufferLength; michael@0: if (dataCount > bufferSize) michael@0: dataCount = bufferSize; michael@0: memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount); michael@0: m_dataBufferLength += dataCount; michael@0: michael@0: if (m_dataBufferLength == sizeof(ap_header)) michael@0: { michael@0: memcpy(&m_headers, m_dataBuffer, sizeof(ap_header)); michael@0: michael@0: /* Check header to be sure we are dealing with the right kind of data, else just write it to the data fork. */ michael@0: if ((m_headers.magic == APPLEDOUBLE_MAGIC || m_headers.magic == APPLESINGLE_MAGIC) && michael@0: m_headers.version == VERSION && m_headers.entriesCount) michael@0: { michael@0: /* Just to be sure, the filler must contains only 0 */ michael@0: for (i = 0; i < 4 && m_headers.fill[i] == 0L; i ++) michael@0: ; michael@0: if (i == 4) michael@0: m_state = parseEntries; michael@0: } michael@0: m_dataBufferLength = 0; michael@0: michael@0: if (m_state == parseHeaders) michael@0: { michael@0: dataCount = 0; michael@0: m_state = parseWriteThrough; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case parseEntries : michael@0: if (!m_entries) michael@0: { michael@0: m_entries = new ap_entry[m_headers.entriesCount]; michael@0: if (!m_entries) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: uint32_t entriesSize = sizeof(ap_entry) * m_headers.entriesCount; michael@0: dataCount = entriesSize - m_dataBufferLength; michael@0: if (dataCount > bufferSize) michael@0: dataCount = bufferSize; michael@0: memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount); michael@0: m_dataBufferLength += dataCount; michael@0: michael@0: if (m_dataBufferLength == entriesSize) michael@0: { michael@0: for (i = 0; i < m_headers.entriesCount; i ++) michael@0: { michael@0: memcpy(&m_entries[i], &m_dataBuffer[i * sizeof(ap_entry)], sizeof(ap_entry)); michael@0: if (m_headers.magic == APPLEDOUBLE_MAGIC) michael@0: { michael@0: uint32_t offset = m_entries[i].offset + m_entries[i].length; michael@0: if (offset > m_dataForkOffset) michael@0: m_dataForkOffset = offset; michael@0: } michael@0: } michael@0: m_headerOk = true; michael@0: m_state = parseLookupPart; michael@0: } michael@0: break; michael@0: michael@0: case parseLookupPart : michael@0: /* which part are we parsing? */ michael@0: m_currentPartID = -1; michael@0: for (i = 0; i < m_headers.entriesCount; i ++) michael@0: if (m_offset == m_entries[i].offset && m_entries[i].length) michael@0: { michael@0: m_currentPartID = m_entries[i].id; michael@0: m_currentPartLength = m_entries[i].length; michael@0: m_currentPartCount = 0; michael@0: michael@0: switch (m_currentPartID) michael@0: { michael@0: case ENT_DFORK : m_state = parseDataFork; break; michael@0: case ENT_RFORK : m_state = parseResourceFork; break; michael@0: michael@0: case ENT_COMMENT : michael@0: case ENT_DATES : michael@0: case ENT_FINFO : michael@0: m_dataBufferLength = 0; michael@0: m_state = parsePart; michael@0: break; michael@0: michael@0: default : m_state = parseSkipPart; break; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (m_currentPartID == -1) michael@0: { michael@0: /* maybe is the datafork of an appledouble file? */ michael@0: if (m_offset == m_dataForkOffset) michael@0: { michael@0: m_currentPartID = ENT_DFORK; michael@0: m_currentPartLength = -1; michael@0: m_currentPartCount = 0; michael@0: m_state = parseDataFork; michael@0: } michael@0: else michael@0: dataCount = 1; michael@0: } michael@0: break; michael@0: michael@0: case parsePart : michael@0: dataCount = m_currentPartLength - m_dataBufferLength; michael@0: if (dataCount > bufferSize) michael@0: dataCount = bufferSize; michael@0: memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount); michael@0: m_dataBufferLength += dataCount; michael@0: michael@0: if (m_dataBufferLength == m_currentPartLength) michael@0: { michael@0: switch (m_currentPartID) michael@0: { michael@0: case ENT_COMMENT : michael@0: m_comment[0] = m_currentPartLength > 255 ? 255 : m_currentPartLength; michael@0: memcpy(&m_comment[1], buffPtr, m_comment[0]); michael@0: break; michael@0: case ENT_DATES : michael@0: if (m_currentPartLength == sizeof(m_dates)) michael@0: memcpy(&m_dates, buffPtr, m_currentPartLength); michael@0: break; michael@0: case ENT_FINFO : michael@0: if (m_currentPartLength == (sizeof(m_finderInfo) + sizeof(m_finderExtraInfo))) michael@0: { michael@0: memcpy(&m_finderInfo, buffPtr, sizeof(m_finderInfo)); michael@0: memcpy(&m_finderExtraInfo, buffPtr + sizeof(m_finderInfo), sizeof(m_finderExtraInfo)); michael@0: } michael@0: break; michael@0: } michael@0: m_state = parseLookupPart; michael@0: } michael@0: break; michael@0: michael@0: case parseSkipPart : michael@0: dataCount = m_currentPartLength - m_currentPartCount; michael@0: if (dataCount > bufferSize) michael@0: dataCount = bufferSize; michael@0: else michael@0: m_state = parseLookupPart; michael@0: break; michael@0: michael@0: case parseDataFork : michael@0: if (m_headers.magic == APPLEDOUBLE_MAGIC) michael@0: dataCount = bufferSize; michael@0: else michael@0: { michael@0: dataCount = m_currentPartLength - m_currentPartCount; michael@0: if (dataCount > bufferSize) michael@0: dataCount = bufferSize; michael@0: else michael@0: m_state = parseLookupPart; michael@0: } michael@0: michael@0: if (m_output) michael@0: { michael@0: uint32_t writeCount; michael@0: rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount); michael@0: if (dataCount != writeCount) michael@0: rv = NS_ERROR_FAILURE; michael@0: m_totalDataForkWritten += dataCount; michael@0: } michael@0: michael@0: break; michael@0: michael@0: case parseResourceFork : michael@0: dataCount = m_currentPartLength - m_currentPartCount; michael@0: if (dataCount > bufferSize) michael@0: dataCount = bufferSize; michael@0: else michael@0: m_state = parseLookupPart; michael@0: michael@0: if (m_rfRefNum == -1) michael@0: { michael@0: if (noErr != FSpOpenRF(&m_fsFileSpec, fsWrPerm, &m_rfRefNum)) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: long count = dataCount; michael@0: if (noErr != FSWrite(m_rfRefNum, &count, buffPtr) || count != dataCount) michael@0: return NS_ERROR_FAILURE; michael@0: m_totalResourceForkWritten += dataCount; michael@0: break; michael@0: michael@0: case parseWriteThrough : michael@0: dataCount = bufferSize; michael@0: if (m_output) michael@0: { michael@0: uint32_t writeCount; michael@0: rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount); michael@0: if (dataCount != writeCount) michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (dataCount) michael@0: { michael@0: *writeCount += dataCount; michael@0: bufferSize -= dataCount; michael@0: buffPtr += dataCount; michael@0: m_currentPartCount += dataCount; michael@0: m_offset += dataCount; michael@0: dataCount = 0; michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: }