LCOV - code coverage report
Current view: directory - modules/libjar - nsJARInputStream.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 153 116 75.8 %
Date: 2012-06-02 Functions: 13 11 84.6 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* nsJARInputStream.cpp
       3                 :  * 
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Netscape Communicator source code. 
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Netscape Communications Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 1999
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Mitch Stoltz <mstoltz@netscape.com>
      26                 :  *   Taras Glek <tglek@mozilla.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "nsJARInputStream.h"
      43                 : #include "zipstruct.h"         // defines ZIP compression codes
      44                 : #include "nsZipArchive.h"
      45                 : 
      46                 : #include "nsNetUtil.h"
      47                 : #include "nsEscape.h"
      48                 : #include "nsIFile.h"
      49                 : #include "nsDebug.h"
      50                 : #if defined(XP_WIN)
      51                 : #include <windows.h>
      52                 : #endif
      53                 : 
      54                 : /*---------------------------------------------
      55                 :  *  nsISupports implementation
      56                 :  *--------------------------------------------*/
      57                 : 
      58           24419 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream)
      59                 : 
      60                 : /*----------------------------------------------------------
      61                 :  * nsJARInputStream implementation
      62                 :  *--------------------------------------------------------*/
      63                 : 
      64                 : nsresult
      65            1288 : nsJARInputStream::InitFile(nsJAR *aJar, nsZipItem *item)
      66                 : {
      67            1288 :     nsresult rv = NS_OK;
      68            1288 :     NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
      69            1288 :     NS_ABORT_IF_FALSE(item, "Argument may not be null");
      70                 : 
      71                 :     // Mark it as closed, in case something fails in initialisation
      72            1288 :     mMode = MODE_CLOSED;
      73                 :     //-- prepare for the compression type
      74            1288 :     switch (item->Compression()) {
      75                 :        case STORED: 
      76             454 :            mMode = MODE_COPY;
      77             454 :            break;
      78                 : 
      79                 :        case DEFLATED:
      80             834 :            rv = gZlibInit(&mZs);
      81             834 :            NS_ENSURE_SUCCESS(rv, rv);
      82                 :     
      83             834 :            mMode = MODE_INFLATE;
      84             834 :            mInCrc = item->CRC32();
      85             834 :            mOutCrc = crc32(0L, Z_NULL, 0);
      86             834 :            break;
      87                 : 
      88                 :        default:
      89               0 :            return NS_ERROR_NOT_IMPLEMENTED;
      90                 :     }
      91                 :    
      92                 :     // Must keep handle to filepointer and mmap structure as long as we need access to the mmapped data
      93            1288 :     mFd = aJar->mZip->GetFD();
      94            1288 :     mZs.next_in = (Bytef *)aJar->mZip->GetData(item);
      95            1288 :     if (!mZs.next_in)
      96               1 :         return NS_ERROR_FILE_CORRUPTED;
      97            1287 :     mZs.avail_in = item->Size();
      98            1287 :     mOutSize = item->RealSize();
      99            1287 :     mZs.total_out = 0;
     100            1287 :     return NS_OK;
     101                 : }
     102                 : 
     103                 : nsresult
     104               1 : nsJARInputStream::InitDirectory(nsJAR* aJar,
     105                 :                                 const nsACString& aJarDirSpec,
     106                 :                                 const char* aDir)
     107                 : {
     108               1 :     NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
     109               1 :     NS_ABORT_IF_FALSE(aDir, "Argument may not be null");
     110                 : 
     111                 :     // Mark it as closed, in case something fails in initialisation
     112               1 :     mMode = MODE_CLOSED;
     113                 :     
     114                 :     // Keep the zipReader for getting the actual zipItems
     115               1 :     mJar = aJar;
     116                 :     nsZipFind *find;
     117                 :     nsresult rv;
     118                 :     // We can get aDir's contents as strings via FindEntries
     119                 :     // with the following pattern (see nsIZipReader.findEntries docs)
     120                 :     // assuming dirName is properly escaped:
     121                 :     //
     122                 :     //   dirName + "?*~" + dirName + "?*/?*"
     123               2 :     nsDependentCString dirName(aDir);
     124               1 :     mNameLen = dirName.Length();
     125                 : 
     126                 :     // iterate through dirName and copy it to escDirName, escaping chars
     127                 :     // which are special at the "top" level of the regexp so FindEntries
     128                 :     // works correctly
     129               2 :     nsCAutoString escDirName;
     130               1 :     const char* curr = dirName.BeginReading();
     131               1 :     const char* end  = dirName.EndReading();
     132              22 :     while (curr != end) {
     133              20 :         switch (*curr) {
     134                 :             case '*':
     135                 :             case '?':
     136                 :             case '$':
     137                 :             case '[':
     138                 :             case ']':
     139                 :             case '^':
     140                 :             case '~':
     141                 :             case '(':
     142                 :             case ')':
     143                 :             case '\\':
     144               0 :                 escDirName.Append('\\');
     145                 :                 // fall through
     146                 :             default:
     147              20 :                 escDirName.Append(*curr);
     148                 :         }
     149              20 :         ++curr;
     150                 :     }
     151               2 :     nsCAutoString pattern = escDirName + NS_LITERAL_CSTRING("?*~") +
     152               4 :                             escDirName + NS_LITERAL_CSTRING("?*/?*");
     153               1 :     rv = mJar->mZip->FindInit(pattern.get(), &find);
     154               1 :     if (NS_FAILED(rv)) return rv;
     155                 : 
     156                 :     const char *name;
     157                 :     PRUint16 nameLen;
     158               3 :     while ((rv = find->FindNext( &name, &nameLen )) == NS_OK) {
     159                 :         // Must copy, to make it zero-terminated
     160               1 :         mArray.AppendElement(nsCString(name,nameLen));
     161                 :     }
     162               1 :     delete find;
     163                 : 
     164               1 :     if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && NS_FAILED(rv)) {
     165               0 :         return NS_ERROR_FAILURE;    // no error translation
     166                 :     }
     167                 : 
     168                 :     // Sort it
     169               1 :     mArray.Sort();
     170                 : 
     171               1 :     mBuffer.AssignLiteral("300: ");
     172               1 :     mBuffer.Append(aJarDirSpec);
     173               1 :     mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n");
     174                 : 
     175                 :     // Open for reading
     176               1 :     mMode = MODE_DIRECTORY;
     177               1 :     mZs.total_out = 0;
     178               1 :     mArrPos = 0;
     179               1 :     return NS_OK;
     180                 : }
     181                 : 
     182                 : NS_IMETHODIMP 
     183            2439 : nsJARInputStream::Available(PRUint32 *_retval)
     184                 : {
     185                 :     // A lot of callers don't check the error code.
     186                 :     // They just use the _retval value.
     187            2439 :     *_retval = 0;
     188                 : 
     189            2439 :     switch (mMode) {
     190                 :       case MODE_NOTINITED:
     191               0 :         break;
     192                 : 
     193                 :       case MODE_CLOSED:
     194               0 :         return NS_BASE_STREAM_CLOSED;
     195                 : 
     196                 :       case MODE_DIRECTORY:
     197               1 :         *_retval = mBuffer.Length();
     198               1 :         break;
     199                 : 
     200                 :       case MODE_INFLATE:
     201                 :       case MODE_COPY:
     202            2438 :         *_retval = mOutSize - mZs.total_out;
     203            2438 :         break;
     204                 :     }
     205                 : 
     206            2439 :     return NS_OK;
     207                 : }
     208                 : 
     209                 : NS_IMETHODIMP
     210            1246 : nsJARInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
     211                 : {
     212            1246 :     NS_ENSURE_ARG_POINTER(aBuffer);
     213            1246 :     NS_ENSURE_ARG_POINTER(aBytesRead);
     214                 : 
     215            1246 :     *aBytesRead = 0;
     216                 : 
     217            1246 :     nsresult rv = NS_OK;
     218                 : MOZ_WIN_MEM_TRY_BEGIN
     219            1246 :     switch (mMode) {
     220                 :       case MODE_NOTINITED:
     221               0 :         return NS_OK;
     222                 : 
     223                 :       case MODE_CLOSED:
     224               0 :         return NS_BASE_STREAM_CLOSED;
     225                 : 
     226                 :       case MODE_DIRECTORY:
     227               1 :         return ReadDirectory(aBuffer, aCount, aBytesRead);
     228                 : 
     229                 :       case MODE_INFLATE:
     230             834 :         if (mFd) {
     231             834 :           rv = ContinueInflate(aBuffer, aCount, aBytesRead);
     232                 :         }
     233                 :         // be aggressive about releasing the file!
     234                 :         // note that sometimes, we will release  mFd before we've finished
     235                 :         // deflating - this is because zlib buffers the input
     236             834 :         if (mZs.avail_in == 0) {
     237             833 :             mFd = nsnull;
     238                 :         }
     239             834 :         break;
     240                 : 
     241                 :       case MODE_COPY:
     242             411 :         if (mFd) {
     243             411 :           PRUint32 count = NS_MIN(aCount, mOutSize - PRUint32(mZs.total_out));
     244             411 :           if (count) {
     245             411 :               memcpy(aBuffer, mZs.next_in + mZs.total_out, count);
     246             411 :               mZs.total_out += count;
     247                 :           }
     248             411 :           *aBytesRead = count;
     249                 :         }
     250                 :         // be aggressive about releasing the file!
     251                 :         // note that sometimes, we will release mFd before we've finished copying.
     252             411 :         if (mZs.total_out >= mOutSize) {
     253             411 :             mFd = nsnull;
     254                 :         }
     255             411 :         break;
     256                 :     }
     257                 : MOZ_WIN_MEM_TRY_CATCH(rv = NS_ERROR_FAILURE)
     258            1245 :     return rv;
     259                 : }
     260                 : 
     261                 : NS_IMETHODIMP
     262               0 : nsJARInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
     263                 : {
     264                 :     // don't have a buffer to read from, so this better not be called!
     265               0 :     NS_NOTREACHED("Consumers should be using Read()!");
     266               0 :     return NS_ERROR_NOT_IMPLEMENTED;
     267                 : }
     268                 : 
     269                 : NS_IMETHODIMP
     270               0 : nsJARInputStream::IsNonBlocking(bool *aNonBlocking)
     271                 : {
     272               0 :     *aNonBlocking = false;
     273               0 :     return NS_OK;
     274                 : }
     275                 : 
     276                 : NS_IMETHODIMP
     277            3487 : nsJARInputStream::Close()
     278                 : {
     279            3487 :     mMode = MODE_CLOSED;
     280            3487 :     mFd = nsnull;
     281            3487 :     return NS_OK;
     282                 : }
     283                 : 
     284                 : nsresult 
     285             834 : nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount,
     286                 :                                   PRUint32* aBytesRead)
     287                 : {
     288                 :     // No need to check the args, ::Read did that, but assert them at least
     289             834 :     NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
     290             834 :     NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
     291                 : 
     292                 :     // Keep old total_out count
     293             834 :     const PRUint32 oldTotalOut = mZs.total_out;
     294                 :     
     295                 :     // make sure we aren't reading too much
     296             834 :     mZs.avail_out = NS_MIN(aCount, (mOutSize-oldTotalOut));
     297             834 :     mZs.next_out = (unsigned char*)aBuffer;
     298                 : 
     299                 :     // now inflate
     300             834 :     int zerr = inflate(&mZs, Z_SYNC_FLUSH);
     301             834 :     if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
     302               0 :         return NS_ERROR_FILE_CORRUPTED;
     303                 : 
     304             834 :     *aBytesRead = (mZs.total_out - oldTotalOut);
     305                 : 
     306                 :     // Calculate the CRC on the output
     307             834 :     mOutCrc = crc32(mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
     308                 : 
     309                 :     // be aggressive about ending the inflation
     310                 :     // for some reason we don't always get Z_STREAM_END
     311             834 :     if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) {
     312             833 :         inflateEnd(&mZs);
     313                 : 
     314                 :         // stop returning valid data as soon as we know we have a bad CRC
     315             833 :         if (mOutCrc != mInCrc) {
     316                 :             // asserting because while this rarely happens, you definitely
     317                 :             // want to catch it in debug builds!
     318               0 :             NS_NOTREACHED(0);
     319               0 :             return NS_ERROR_FILE_CORRUPTED;
     320                 :         }
     321                 :     }
     322                 : 
     323             834 :     return NS_OK;
     324                 : }
     325                 : 
     326                 : nsresult
     327               1 : nsJARInputStream::ReadDirectory(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
     328                 : {
     329                 :     // No need to check the args, ::Read did that, but assert them at least
     330               1 :     NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
     331               1 :     NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
     332                 : 
     333                 :     // If the buffer contains data, copy what's there up to the desired amount
     334               1 :     PRUint32 numRead = CopyDataToBuffer(aBuffer, aCount);
     335                 : 
     336               1 :     if (aCount > 0) {
     337                 :         // empty the buffer and start writing directory entry lines to it
     338               0 :         mBuffer.Truncate();
     339               0 :         mCurPos = 0;
     340               0 :         const PRUint32 arrayLen = mArray.Length();
     341                 : 
     342               0 :         for ( ;aCount > mBuffer.Length(); mArrPos++) {
     343                 :             // have we consumed all the directory contents?
     344               0 :             if (arrayLen <= mArrPos)
     345               0 :                 break;
     346                 : 
     347               0 :             const char * entryName = mArray[mArrPos].get();
     348               0 :             PRUint32 entryNameLen = mArray[mArrPos].Length();
     349               0 :             nsZipItem* ze = mJar->mZip->GetItem(entryName);
     350               0 :             NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
     351                 : 
     352                 :             // Last Modified Time
     353                 :             PRExplodedTime tm;
     354               0 :             PR_ExplodeTime(ze->LastModTime(), PR_GMTParameters, &tm);
     355                 :             char itemLastModTime[65];
     356                 :             PR_FormatTimeUSEnglish(itemLastModTime,
     357                 :                                    sizeof(itemLastModTime),
     358                 :                                    " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ",
     359               0 :                                    &tm);
     360                 : 
     361                 :             // write a 201: line to the buffer for this item
     362                 :             // 200: filename content-length last-modified file-type
     363               0 :             mBuffer.AppendLiteral("201: ");
     364                 : 
     365                 :             // Names must be escaped and relative, so use the pre-calculated length
     366                 :             // of the directory name as the offset into the string
     367                 :             // NS_EscapeURL adds the escaped URL to the give string buffer
     368                 :             NS_EscapeURL(entryName + mNameLen,
     369                 :                          entryNameLen - mNameLen, 
     370                 :                          esc_Minimal | esc_AlwaysCopy,
     371               0 :                          mBuffer);
     372                 : 
     373               0 :             mBuffer.Append(' ');
     374               0 :             mBuffer.AppendInt(ze->RealSize(), 10);
     375               0 :             mBuffer.Append(itemLastModTime); // starts/ends with ' '
     376               0 :             if (ze->IsDirectory()) 
     377               0 :                 mBuffer.AppendLiteral("DIRECTORY\n");
     378                 :             else
     379               0 :                 mBuffer.AppendLiteral("FILE\n");
     380                 :         }
     381                 : 
     382                 :         // Copy up to the desired amount of data to buffer
     383               0 :         numRead += CopyDataToBuffer(aBuffer, aCount);
     384                 :     }
     385                 : 
     386               1 :     *aBytesRead = numRead;
     387               1 :     return NS_OK;
     388                 : }
     389                 : 
     390                 : PRUint32
     391               1 : nsJARInputStream::CopyDataToBuffer(char* &aBuffer, PRUint32 &aCount)
     392                 : {
     393               1 :     const PRUint32 writeLength = NS_MIN(aCount, mBuffer.Length() - mCurPos);
     394                 : 
     395               1 :     if (writeLength > 0) {
     396               1 :         memcpy(aBuffer, mBuffer.get() + mCurPos, writeLength);
     397               1 :         mCurPos += writeLength;
     398               1 :         aCount  -= writeLength;
     399               1 :         aBuffer += writeLength;
     400                 :     }
     401                 : 
     402                 :     // return number of bytes copied to the buffer so the
     403                 :     // Read method can return the number of bytes copied
     404               1 :     return writeLength;
     405                 : }

Generated by: LCOV version 1.7