Back to index

lightning-sunbird  0.9+nobinonly
nsAppleFileDecoder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jean-Francois Ducarroz <ducarroz@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038  
00039 #include "nsAppleFileDecoder.h"
00040 #include "prmem.h"
00041 #include "nsCRT.h"
00042 
00043 
00044 NS_IMPL_THREADSAFE_ISUPPORTS2(nsAppleFileDecoder, nsIAppleFileDecoder, nsIOutputStream)
00045 
00046 nsAppleFileDecoder::nsAppleFileDecoder()
00047 {
00048   m_state = parseHeaders;
00049   m_dataBufferLength = 0;
00050   m_dataBuffer = (unsigned char*) PR_MALLOC(MAX_BUFFERSIZE);
00051   m_entries = nsnull;
00052   m_rfRefNum = -1;
00053   m_totalDataForkWritten = 0;
00054   m_totalResourceForkWritten = 0;
00055   m_headerOk = PR_FALSE;
00056   
00057   m_comment[0] = 0;
00058   memset(&m_dates, 0, sizeof(m_dates));
00059   memset(&m_finderInfo, 0, sizeof(m_dates));
00060   memset(&m_finderExtraInfo, 0, sizeof(m_dates));
00061 }
00062 
00063 nsAppleFileDecoder::~nsAppleFileDecoder()
00064 {
00065   if (m_output)
00066     Close();
00067 
00068   PR_FREEIF(m_dataBuffer);
00069   if (m_entries)
00070     delete [] m_entries;
00071 }
00072 
00073 NS_IMETHODIMP nsAppleFileDecoder::Initialize(nsIOutputStream *outputStream, nsIFile *outputFile)
00074 {
00075   m_output = outputStream;
00076   
00077   nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(outputFile);
00078   PRBool saveFollowLinks;
00079   macFile->GetFollowLinks(&saveFollowLinks);
00080   macFile->SetFollowLinks(PR_TRUE);
00081   macFile->GetFSSpec(&m_fsFileSpec);
00082   macFile->SetFollowLinks(saveFollowLinks);
00083 
00084   m_offset = 0;
00085   m_dataForkOffset = 0;
00086 
00087   return NS_OK;
00088 }
00089 
00090 NS_IMETHODIMP nsAppleFileDecoder::Close(void)
00091 {
00092   nsresult rv;
00093   rv = m_output->Close();
00094 
00095   PRInt32 i;
00096 
00097   if (m_rfRefNum != -1)
00098     FSClose(m_rfRefNum);
00099     
00100   /* Check if the file is complete and if it's the case, write file attributes */
00101   if (m_headerOk)
00102   {
00103     PRBool dataOk = PR_TRUE; /* It's ok if the file doesn't have a datafork, therefore set it to true by default. */
00104     if (m_headers.magic == APPLESINGLE_MAGIC)
00105     {
00106       for (i = 0; i < m_headers.entriesCount; i ++)
00107         if (ENT_DFORK == m_entries[i].id)
00108         {
00109           dataOk = (PRBool)(m_totalDataForkWritten == m_entries[i].length);
00110           break;
00111         }
00112     }
00113 
00114     PRBool resourceOk = FALSE;
00115     for (i = 0; i < m_headers.entriesCount; i ++)
00116       if (ENT_RFORK == m_entries[i].id)
00117       {
00118         resourceOk = (PRBool)(m_totalResourceForkWritten == m_entries[i].length);
00119         break;
00120       }
00121       
00122     if (dataOk && resourceOk)
00123     {
00124       HFileInfo *fpb;
00125       CInfoPBRec cipbr;
00126       
00127       fpb = (HFileInfo *) &cipbr;
00128       fpb->ioVRefNum = m_fsFileSpec.vRefNum;
00129       fpb->ioDirID   = m_fsFileSpec.parID;
00130       fpb->ioNamePtr = m_fsFileSpec.name;
00131       fpb->ioFDirIndex = 0;
00132       PBGetCatInfoSync(&cipbr);
00133 
00134       /* set finder info */
00135       memcpy(&fpb->ioFlFndrInfo, &m_finderInfo, sizeof (FInfo));
00136       memcpy(&fpb->ioFlXFndrInfo, &m_finderExtraInfo, sizeof (FXInfo));
00137       fpb->ioFlFndrInfo.fdFlags &= 0xfc00; /* clear flags maintained by finder */
00138       
00139       /* set file dates */
00140       fpb->ioFlCrDat = m_dates.create - CONVERT_TIME;
00141       fpb->ioFlMdDat = m_dates.modify - CONVERT_TIME;
00142       fpb->ioFlBkDat = m_dates.backup - CONVERT_TIME;
00143     
00144       /* update file info */
00145       fpb->ioDirID = fpb->ioFlParID;
00146       PBSetCatInfoSync(&cipbr);
00147       
00148       /* set comment */
00149       IOParam vinfo;
00150       GetVolParmsInfoBuffer vp;
00151       DTPBRec dtp;
00152 
00153       memset((void *) &vinfo, 0, sizeof (vinfo));
00154       vinfo.ioVRefNum = fpb->ioVRefNum;
00155       vinfo.ioBuffer  = (Ptr) &vp;
00156       vinfo.ioReqCount = sizeof (vp);
00157       if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr && ((vp.vMAttrib >> bHasDesktopMgr) & 1)) 
00158       {
00159         memset((void *) &dtp, 0, sizeof (dtp));
00160         dtp.ioVRefNum = fpb->ioVRefNum;
00161         if (PBDTGetPath(&dtp) == noErr) 
00162         {
00163           dtp.ioDTBuffer = (Ptr) &m_comment[1];
00164           dtp.ioNamePtr  = fpb->ioNamePtr;
00165           dtp.ioDirID    = fpb->ioDirID;
00166           dtp.ioDTReqCount = m_comment[0];
00167           if (PBDTSetCommentSync(&dtp) == noErr) 
00168             PBDTFlushSync(&dtp);
00169         }
00170       }
00171     }
00172   }
00173   
00174   /* setting m_headerOk to false will prevent us to reprocess the header in case the Close function is called several time*/
00175   m_headerOk = PR_FALSE;
00176 
00177   return rv;
00178 }
00179 
00180 NS_IMETHODIMP nsAppleFileDecoder::Flush(void)
00181 {
00182   return m_output->Flush();
00183 } 
00184 
00185 NS_IMETHODIMP nsAppleFileDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count, PRUint32 *_retval)
00186 {
00187   return m_output->WriteFrom(inStr, count, _retval);
00188 }
00189 
00190 NS_IMETHODIMP nsAppleFileDecoder::WriteSegments(nsReadSegmentFun reader, void * closure, PRUint32 count, PRUint32 *_retval)
00191 {
00192   return m_output->WriteSegments(reader, closure, count, _retval);
00193 }
00194 
00195 NS_IMETHODIMP nsAppleFileDecoder::IsNonBlocking(PRBool *aNonBlocking)
00196 {
00197   return m_output->IsNonBlocking(aNonBlocking);
00198 }
00199 
00200 NS_IMETHODIMP nsAppleFileDecoder::Write(const char *buffer, PRUint32 bufferSize, PRUint32* writeCount)
00201 {
00202   /* WARNING: to simplify my life, I presume that I should get all appledouble headers in the first block,
00203               else I would have to implement a buffer */
00204 
00205   const char * buffPtr = buffer;
00206   PRUint32 dataCount;
00207   PRInt32 i;
00208   nsresult rv = NS_OK;
00209 
00210   *writeCount = 0;
00211   
00212   while (bufferSize > 0 && NS_SUCCEEDED(rv))
00213   {
00214     switch (m_state)
00215     {
00216       case parseHeaders :
00217         dataCount = sizeof(ap_header) - m_dataBufferLength;
00218         if (dataCount > bufferSize)
00219           dataCount = bufferSize;
00220         memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
00221         m_dataBufferLength += dataCount;
00222         
00223         if (m_dataBufferLength == sizeof(ap_header))
00224         {
00225           memcpy(&m_headers, m_dataBuffer, sizeof(ap_header));
00226 
00227           /* Check header to be sure we are dealing with the right kind of data, else just write it to the data fork. */
00228           if ((m_headers.magic == APPLEDOUBLE_MAGIC || m_headers.magic == APPLESINGLE_MAGIC) && 
00229               m_headers.version == VERSION && m_headers.entriesCount)
00230           {
00231             /* Just to be sure, the filler must contains only 0 */
00232             for (i = 0; i < 4 && m_headers.fill[i] == 0L; i ++)
00233               ;
00234             if (i == 4)
00235               m_state = parseEntries;
00236           }
00237           m_dataBufferLength = 0;
00238           
00239           if (m_state == parseHeaders)
00240           {
00241             dataCount = 0;
00242             m_state = parseWriteThrough;
00243           }
00244         }
00245         break;
00246       
00247       case parseEntries :
00248         {
00249         if (!m_entries)
00250         {
00251           m_entries = new ap_entry[m_headers.entriesCount];
00252           if (!m_entries)
00253             return NS_ERROR_OUT_OF_MEMORY;
00254         }
00255         PRUint32 entriesSize = sizeof(ap_entry) * m_headers.entriesCount;
00256         dataCount = entriesSize - m_dataBufferLength;
00257         if (dataCount > bufferSize)
00258           dataCount = bufferSize;
00259         memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
00260         m_dataBufferLength += dataCount;
00261 
00262         if (m_dataBufferLength == entriesSize)
00263         {
00264           for (i = 0; i < m_headers.entriesCount; i ++)
00265           {
00266             memcpy(&m_entries[i], &m_dataBuffer[i * sizeof(ap_entry)], sizeof(ap_entry));
00267             if (m_headers.magic == APPLEDOUBLE_MAGIC)
00268             {
00269               PRUint32 offset = m_entries[i].offset + m_entries[i].length;
00270               if (offset > m_dataForkOffset)
00271                 m_dataForkOffset = offset;
00272             }
00273           }
00274           m_headerOk = PR_TRUE;          
00275           m_state = parseLookupPart;
00276         }
00277         }
00278         break;
00279       
00280       case parseLookupPart :
00281         /* which part are we parsing? */
00282         m_currentPartID = -1;
00283         for (i = 0; i < m_headers.entriesCount; i ++)
00284           if (m_offset == m_entries[i].offset && m_entries[i].length)
00285           {
00286               m_currentPartID = m_entries[i].id;
00287               m_currentPartLength = m_entries[i].length;
00288               m_currentPartCount = 0;
00289 
00290               switch (m_currentPartID)
00291               {
00292                 case ENT_DFORK    : m_state = parseDataFork;           break;
00293                 case ENT_RFORK    : m_state = parseResourceFork;       break;
00294 
00295                 case ENT_COMMENT  :
00296                 case ENT_DATES    :
00297                 case ENT_FINFO    :
00298                   m_dataBufferLength = 0;
00299                   m_state = parsePart;
00300                   break;
00301                 
00302                 default           : m_state = parseSkipPart;           break;
00303               }
00304               break;
00305           }
00306           
00307         if (m_currentPartID == -1)
00308         {
00309           /* maybe is the datafork of an appledouble file? */
00310           if (m_offset == m_dataForkOffset)
00311           {
00312               m_currentPartID = ENT_DFORK;
00313               m_currentPartLength = -1;
00314               m_currentPartCount = 0;
00315               m_state = parseDataFork;
00316           }
00317           else
00318             dataCount = 1;
00319         }        
00320         break;
00321       
00322       case parsePart :
00323         dataCount = m_currentPartLength - m_dataBufferLength;
00324         if (dataCount > bufferSize)
00325           dataCount = bufferSize;
00326         memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
00327         m_dataBufferLength += dataCount;
00328         
00329         if (m_dataBufferLength == m_currentPartLength)
00330         {
00331           switch (m_currentPartID)
00332           {
00333             case ENT_COMMENT  :
00334               m_comment[0] = m_currentPartLength > 255 ? 255 : m_currentPartLength;
00335               memcpy(&m_comment[1], buffPtr, m_comment[0]);
00336               break;
00337             case ENT_DATES    :
00338               if (m_currentPartLength == sizeof(m_dates))
00339                 memcpy(&m_dates, buffPtr, m_currentPartLength);
00340               break;
00341             case ENT_FINFO    :
00342               if (m_currentPartLength == (sizeof(m_finderInfo) + sizeof(m_finderExtraInfo)))
00343               {
00344                 memcpy(&m_finderInfo, buffPtr, sizeof(m_finderInfo));
00345                 memcpy(&m_finderExtraInfo, buffPtr + sizeof(m_finderInfo), sizeof(m_finderExtraInfo));
00346               }
00347               break;
00348           }
00349           m_state = parseLookupPart;
00350         }
00351         break;
00352       
00353       case parseSkipPart :
00354         dataCount = m_currentPartLength - m_currentPartCount;
00355         if (dataCount > bufferSize)
00356           dataCount = bufferSize;
00357         else
00358           m_state = parseLookupPart;
00359         break;
00360       
00361       case parseDataFork :
00362         if (m_headers.magic == APPLEDOUBLE_MAGIC)
00363           dataCount = bufferSize;
00364         else
00365         {
00366           dataCount = m_currentPartLength - m_currentPartCount;
00367           if (dataCount > bufferSize)
00368             dataCount = bufferSize;
00369           else
00370             m_state = parseLookupPart;
00371         }
00372         
00373         if (m_output)
00374         {
00375           PRUint32 writeCount;
00376           rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
00377           if (dataCount != writeCount)
00378             rv = NS_ERROR_FAILURE;
00379           m_totalDataForkWritten += dataCount;
00380         }
00381         
00382         break;
00383       
00384       case parseResourceFork :
00385         {
00386         dataCount = m_currentPartLength - m_currentPartCount;
00387         if (dataCount > bufferSize)
00388           dataCount = bufferSize;
00389         else
00390           m_state = parseLookupPart;
00391         
00392         if (m_rfRefNum == -1)
00393         {
00394           if (noErr != FSpOpenRF(&m_fsFileSpec, fsWrPerm, &m_rfRefNum))
00395             return NS_ERROR_FAILURE;
00396         }
00397         
00398         long count = dataCount;
00399         if (noErr != FSWrite(m_rfRefNum, &count, buffPtr) || count != dataCount)
00400             return NS_ERROR_FAILURE;
00401         m_totalResourceForkWritten += dataCount;
00402         }
00403         break;
00404       
00405       case parseWriteThrough :
00406         dataCount = bufferSize;
00407         if (m_output)
00408         {
00409           PRUint32 writeCount;
00410           rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
00411           if (dataCount != writeCount)
00412             rv = NS_ERROR_FAILURE;
00413         }
00414         break;
00415     }
00416 
00417     if (dataCount)
00418     {
00419       *writeCount += dataCount;
00420       bufferSize -= dataCount;
00421       buffPtr += dataCount;
00422       m_currentPartCount += dataCount;
00423       m_offset += dataCount;
00424       dataCount = 0;
00425     }
00426   }
00427 
00428   return rv;
00429 }