1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4  */
5 
6 #include "StreamFunctions.h"
7 #include "nsDeflateConverter.h"
8 #include "nsStringStream.h"
9 #include "nsComponentManagerUtils.h"
10 #include "nsMemory.h"
11 #include "plstr.h"
12 #include "mozilla/UniquePtr.h"
13 
14 #define ZLIB_TYPE "deflate"
15 #define GZIP_TYPE "gzip"
16 #define X_GZIP_TYPE "x-gzip"
17 
18 using namespace mozilla;
19 
20 /**
21  * nsDeflateConverter is a stream converter applies the deflate compression
22  * method to the data.
23  */
NS_IMPL_ISUPPORTS(nsDeflateConverter,nsIStreamConverter,nsIStreamListener,nsIRequestObserver)24 NS_IMPL_ISUPPORTS(nsDeflateConverter, nsIStreamConverter, nsIStreamListener,
25                   nsIRequestObserver)
26 
27 nsresult nsDeflateConverter::Init() {
28   int zerr;
29 
30   mOffset = 0;
31 
32   mZstream.zalloc = Z_NULL;
33   mZstream.zfree = Z_NULL;
34   mZstream.opaque = Z_NULL;
35 
36   int32_t window = MAX_WBITS;
37   switch (mWrapMode) {
38     case WRAP_NONE:
39       window = -window;
40       break;
41     case WRAP_GZIP:
42       window += 16;
43       break;
44     default:
45       break;
46   }
47 
48   zerr = deflateInit2(&mZstream, mLevel, Z_DEFLATED, window, 8,
49                       Z_DEFAULT_STRATEGY);
50   if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
51 
52   mZstream.next_out = mWriteBuffer;
53   mZstream.avail_out = sizeof(mWriteBuffer);
54 
55   // mark the input buffer as empty.
56   mZstream.avail_in = 0;
57   mZstream.next_in = Z_NULL;
58 
59   return NS_OK;
60 }
61 
Convert(nsIInputStream * aFromStream,const char * aFromType,const char * aToType,nsISupports * aCtxt,nsIInputStream ** _retval)62 NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream* aFromStream,
63                                           const char* aFromType,
64                                           const char* aToType,
65                                           nsISupports* aCtxt,
66                                           nsIInputStream** _retval) {
67   return NS_ERROR_NOT_IMPLEMENTED;
68 }
69 
AsyncConvertData(const char * aFromType,const char * aToType,nsIStreamListener * aListener,nsISupports * aCtxt)70 NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char* aFromType,
71                                                    const char* aToType,
72                                                    nsIStreamListener* aListener,
73                                                    nsISupports* aCtxt) {
74   if (mListener) return NS_ERROR_ALREADY_INITIALIZED;
75 
76   NS_ENSURE_ARG_POINTER(aListener);
77 
78   if (!PL_strncasecmp(aToType, ZLIB_TYPE, sizeof(ZLIB_TYPE) - 1))
79     mWrapMode = WRAP_ZLIB;
80   else if (!PL_strcasecmp(aToType, GZIP_TYPE) ||
81            !PL_strcasecmp(aToType, X_GZIP_TYPE))
82     mWrapMode = WRAP_GZIP;
83   else
84     mWrapMode = WRAP_NONE;
85 
86   nsresult rv = Init();
87   NS_ENSURE_SUCCESS(rv, rv);
88 
89   mListener = aListener;
90   mContext = aCtxt;
91   return rv;
92 }
93 
94 NS_IMETHODIMP
GetConvertedType(const nsACString & aFromType,nsIChannel * aChannel,nsACString & aToType)95 nsDeflateConverter::GetConvertedType(const nsACString& aFromType,
96                                      nsIChannel* aChannel,
97                                      nsACString& aToType) {
98   return NS_ERROR_NOT_IMPLEMENTED;
99 }
100 
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)101 NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest* aRequest,
102                                                   nsIInputStream* aInputStream,
103                                                   uint64_t aOffset,
104                                                   uint32_t aCount) {
105   if (!mListener) return NS_ERROR_NOT_INITIALIZED;
106 
107   auto buffer = MakeUnique<char[]>(aCount);
108   NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
109 
110   nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount);
111   NS_ENSURE_SUCCESS(rv, rv);
112 
113   // make sure we aren't reading too much
114   mZstream.avail_in = aCount;
115   mZstream.next_in = (unsigned char*)buffer.get();
116 
117   int zerr = Z_OK;
118   // deflate loop
119   while (mZstream.avail_in > 0 && zerr == Z_OK) {
120     zerr = deflate(&mZstream, Z_NO_FLUSH);
121 
122     while (mZstream.avail_out == 0) {
123       // buffer is full, push the data out to the listener
124       rv = PushAvailableData(aRequest, nullptr);
125       NS_ENSURE_SUCCESS(rv, rv);
126       zerr = deflate(&mZstream, Z_NO_FLUSH);
127     }
128   }
129 
130   return NS_OK;
131 }
132 
OnStartRequest(nsIRequest * aRequest)133 NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest* aRequest) {
134   if (!mListener) return NS_ERROR_NOT_INITIALIZED;
135 
136   return mListener->OnStartRequest(aRequest);
137 }
138 
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)139 NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest* aRequest,
140                                                 nsresult aStatusCode) {
141   if (!mListener) return NS_ERROR_NOT_INITIALIZED;
142 
143   nsresult rv;
144 
145   int zerr;
146   do {
147     zerr = deflate(&mZstream, Z_FINISH);
148     rv = PushAvailableData(aRequest, nullptr);
149     NS_ENSURE_SUCCESS(rv, rv);
150   } while (zerr == Z_OK);
151 
152   deflateEnd(&mZstream);
153 
154   return mListener->OnStopRequest(aRequest, aStatusCode);
155 }
156 
PushAvailableData(nsIRequest * aRequest,nsISupports * aContext)157 nsresult nsDeflateConverter::PushAvailableData(nsIRequest* aRequest,
158                                                nsISupports* aContext) {
159   uint32_t bytesToWrite = sizeof(mWriteBuffer) - mZstream.avail_out;
160   // We don't need to do anything if there isn't any data
161   if (bytesToWrite == 0) return NS_OK;
162 
163   MOZ_ASSERT(bytesToWrite <= INT32_MAX);
164   nsCOMPtr<nsIInputStream> stream;
165   nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
166                                       Span((char*)mWriteBuffer, bytesToWrite),
167                                       NS_ASSIGNMENT_DEPEND);
168   NS_ENSURE_SUCCESS(rv, rv);
169 
170   rv = mListener->OnDataAvailable(aRequest, stream, mOffset, bytesToWrite);
171 
172   // now set the state for 'deflate'
173   mZstream.next_out = mWriteBuffer;
174   mZstream.avail_out = sizeof(mWriteBuffer);
175 
176   mOffset += bytesToWrite;
177   return rv;
178 }
179