From 11ca05f6aea51f20de2baf0c1e73a2e243b0c972 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Fri, 29 May 2009 23:35:13 +0200 Subject: [PATCH] wininet: Added support for decompressing gzip encoded content. --- configure | 84 ++++++++++++++++- configure.ac | 10 +- dlls/wininet/Makefile.in | 2 +- dlls/wininet/http.c | 197 ++++++++++++++++++++++++++++++++++++--- dlls/wininet/internet.h | 5 + include/config.h.in | 6 ++ 6 files changed, 289 insertions(+), 15 deletions(-) diff --git a/configure b/configure index 413c5907635..52a81307660 100755 --- a/configure +++ b/configure @@ -669,6 +669,7 @@ ALSALIBS ESDLIBS ESDINCL ESDCONFIG +ZLIB FREETYPEINCL FREETYPELIBS ft_devel @@ -5776,6 +5777,7 @@ done + for ac_header in \ @@ -5899,7 +5901,8 @@ for ac_header in \ unistd.h \ utime.h \ valgrind/memcheck.h \ - valgrind/valgrind.h + valgrind/valgrind.h \ + zlib.h do as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` @@ -15051,6 +15054,85 @@ done LIBS="$ac_wine_check_funcs_save_LIBS" +if test "$ac_cv_header_zlib_h" = "yes" +then + { $as_echo "$as_me:$LINENO: checking for inflate in -lz" >&5 +$as_echo_n "checking for inflate in -lz... " >&6; } +if test "${ac_cv_lib_z_inflate+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char inflate (); +int +main () +{ +return inflate (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_z_inflate=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_z_inflate=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_z_inflate" >&5 +$as_echo "$ac_cv_lib_z_inflate" >&6; } +if test "x$ac_cv_lib_z_inflate" = x""yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ZLIB 1 +_ACEOF + + ZLIB="-lz" + +fi + +fi + if test "x$with_esd" != xno then save_CFLAGS="$CFLAGS" diff --git a/configure.ac b/configure.ac index 879e358bb29..beccbbd6513 100644 --- a/configure.ac +++ b/configure.ac @@ -377,7 +377,8 @@ AC_CHECK_HEADERS(\ unistd.h \ utime.h \ valgrind/memcheck.h \ - valgrind/valgrind.h + valgrind/valgrind.h \ + zlib.h ) AC_HEADER_STAT() @@ -1172,6 +1173,13 @@ WINE_CHECK_LIB_FUNCS(\ pthread_get_stacksize_np, [$LIBPTHREAD]) +dnl **** Check for zlib **** +if test "$ac_cv_header_zlib_h" = "yes" +then + AC_CHECK_LIB(z,inflate,[AC_DEFINE(HAVE_ZLIB,1,[Define to 1 if you have the `z' library (-lz).]) + AC_SUBST(ZLIB,"-lz")]) +fi + dnl **** Check for EsounD **** if test "x$with_esd" != xno then diff --git a/dlls/wininet/Makefile.in b/dlls/wininet/Makefile.in index 116c9fdb4e8..c7cc1e0a8c1 100644 --- a/dlls/wininet/Makefile.in +++ b/dlls/wininet/Makefile.in @@ -7,7 +7,7 @@ MODULE = wininet.dll IMPORTLIB = wininet IMPORTS = mpr shlwapi shell32 user32 advapi32 kernel32 ntdll DELAYIMPORTS = secur32 crypt32 -EXTRALIBS = @SOCKETLIBS@ +EXTRALIBS = @SOCKETLIBS@ @ZLIB@ C_SRCS = \ cookie.c \ diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index 8db3e97a737..b11ce21c416 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -48,6 +48,9 @@ #endif #include #include +#ifdef HAVE_ZLIB +# include +#endif #include "windef.h" #include "winbase.h" @@ -162,6 +165,15 @@ struct HttpAuthInfo BOOL finished; /* finished authenticating */ }; +#ifdef HAVE_ZLIB + +struct gzip_stream_t { + z_stream zstream; + BYTE buf[4096]; +}; + +#endif + static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr); static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear); static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier); @@ -188,6 +200,57 @@ static LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head) return &req->pCustHeaders[HeaderIndex]; } +#ifdef HAVE_ZLIB + +static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size) +{ + return HeapAlloc(GetProcessHeap(), 0, items*size); +} + +static void wininet_zfree(voidpf opaque, voidpf address) +{ + HeapFree(GetProcessHeap(), 0, address); +} + +static void init_gzip_stream(WININETHTTPREQW *req) +{ + gzip_stream_t *gzip_stream; + int zres; + + gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t)); + gzip_stream->zstream.zalloc = wininet_zalloc; + gzip_stream->zstream.zfree = wininet_zfree; + gzip_stream->zstream.opaque = NULL; + gzip_stream->zstream.next_in = NULL; + gzip_stream->zstream.avail_in = 0; + + zres = inflateInit2(&gzip_stream->zstream, 0x1f); + if(zres != Z_OK) { + ERR("inflateInit failed: %d\n", zres); + HeapFree(GetProcessHeap(), 0, gzip_stream); + return; + } + + req->gzip_stream = gzip_stream; + req->dwContentLength = ~0u; + + if(req->read_size) { + memcpy(gzip_stream->buf, req->read_buf + req->read_pos, req->read_size); + gzip_stream->zstream.next_in = gzip_stream->buf; + gzip_stream->zstream.avail_in = req->read_size; + req->read_size = 0; + } +} + +#else + +static void init_gzip_stream(WININETHTTPREQW *req) +{ + ERR("gzip stream not supported, missing zlib.\n"); +} + +#endif + /* set the request content length based on the headers */ static DWORD set_content_length( LPWININETHTTPREQW lpwhr ) { @@ -208,6 +271,16 @@ static DWORD set_content_length( LPWININETHTTPREQW lpwhr ) lpwhr->read_chunked = TRUE; } + if(lpwhr->decoding) { + int encoding_idx; + + static const WCHAR gzipW[] = {'g','z','i','p',0}; + + encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE); + if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW)) + init_gzip_stream(lpwhr); + } + return lpwhr->dwContentLength; } @@ -1476,6 +1549,14 @@ static void HTTPREQ_CloseConnection(WININETHANDLEHEADER *hdr) TRACE("%p\n",lpwhr); +#ifdef HAVE_ZLIB + if(lpwhr->gzip_stream) { + inflateEnd(&lpwhr->gzip_stream->zstream); + HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream); + lpwhr->gzip_stream = NULL; + } +#endif + if (!NETCON_connected(&lpwhr->netConnection)) return; @@ -1659,26 +1740,95 @@ static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buf HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword); if (!(req->lpHttpSession->lpszPassword = WININET_strdupW(buffer))) return ERROR_OUTOFMEMORY; return ERROR_SUCCESS; + case INTERNET_OPTION_HTTP_DECODING: + if(size != sizeof(BOOL)) + return ERROR_INVALID_PARAMETER; + req->decoding = *(BOOL*)buffer; + return ERROR_SUCCESS; } return ERROR_INTERNET_INVALID_OPTION; } +static inline BOOL is_gzip_buf_empty(gzip_stream_t *gzip_stream) +{ +#ifdef HAVE_ZLIB + return gzip_stream->zstream.avail_in == 0; +#else + return TRUE; +#endif +} + +static DWORD read_gzip_data(WININETHTTPREQW *req, BYTE *buf, int size, int flags, int *read_ret) +{ + DWORD ret = ERROR_SUCCESS; + int read = 0; + +#ifdef HAVE_ZLIB + int res, len, zres; + z_stream *zstream; + + zstream = &req->gzip_stream->zstream; + + while(read < size) { + if(is_gzip_buf_empty(req->gzip_stream)) { + res = NETCON_recv( &req->netConnection, req->gzip_stream->buf, + sizeof(req->gzip_stream->buf), flags, &len); + if(!res) { + if(!read) + ret = INTERNET_GetLastError(); + break; + } + + zstream->next_in = req->gzip_stream->buf; + zstream->avail_in = len; + } + + zstream->next_out = buf+read; + zstream->avail_out = size-read; + zres = inflate(zstream, Z_FULL_FLUSH); + read = size - zstream->avail_out; + if(zres == Z_STREAM_END) { + TRACE("end of data\n"); + req->dwContentLength = req->dwContentRead + req->read_size + read; + break; + }else if(zres != Z_OK) { + WARN("inflate failed %d\n", zres); + if(!read) + ret = ERROR_INTERNET_DECODING_FAILED; + break; + } + } +#endif + + *read_ret = read; + return ret; +} + /* read some more data into the read buffer (the read section must be held) */ static BOOL read_more_data( WININETHTTPREQW *req, int maxlen ) { int len; - if (req->read_size && req->read_pos) + if (req->read_pos) { /* move existing data to the start of the buffer */ - memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size ); + if(req->read_size) + memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size ); req->read_pos = 0; } if (maxlen == -1) maxlen = sizeof(req->read_buf); - if (!NETCON_recv( &req->netConnection, req->read_buf + req->read_size, - maxlen - req->read_size, 0, &len )) return FALSE; + + if (req->gzip_stream) { + if(read_gzip_data(req, req->read_buf + req->read_size, maxlen - req->read_size, 0, &len) != ERROR_SUCCESS) + return FALSE; + }else { + if(!NETCON_recv( &req->netConnection, req->read_buf + req->read_size, + maxlen - req->read_size, 0, &len )) + return FALSE; + } + req->read_size += len; return TRUE; } @@ -1847,6 +1997,7 @@ static void HTTP_ReceiveRequestData(WININETHTTPREQW *req, BOOL first_notif) static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync) { int len, bytes_read = 0; + DWORD ret = ERROR_SUCCESS; EnterCriticalSection( &req->read_section ); if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead)) @@ -1862,12 +2013,20 @@ static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD remove_data( req, bytes_read ); } - if (size > bytes_read && (!bytes_read || sync)) + if (size > bytes_read) { - if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read, - sync ? MSG_WAITALL : 0, &len)) - bytes_read += len; - /* always return success, even if the network layer returns an error */ + if (req->gzip_stream) { + if(is_gzip_buf_empty(req->gzip_stream) || !bytes_read || sync) { + ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size - bytes_read, sync ? MSG_WAITALL : 0, &len); + if(ret == ERROR_SUCCESS) + bytes_read += len; + } + }else if (!bytes_read || sync) { + if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read, + sync ? MSG_WAITALL : 0, &len)) + bytes_read += len; + /* always return success, even if the network layer returns an error */ + } } done: req->dwContentRead += bytes_read; @@ -1876,7 +2035,7 @@ done: TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength ); LeaveCriticalSection( &req->read_section ); - if(req->lpszCacheFile) { + if(ret == ERROR_SUCCESS && req->lpszCacheFile) { BOOL res; DWORD dwBytesWritten; @@ -1888,7 +2047,7 @@ done: if(!bytes_read && (req->dwContentRead == req->dwContentLength)) HTTP_FinishedReading(req); - return ERROR_SUCCESS; + return ret; } @@ -2101,7 +2260,7 @@ static DWORD HTTPREQ_QueryDataAvailable(WININETHANDLEHEADER *hdr, DWORD *availab } done: - if (*available == sizeof(req->read_buf)) /* check if we have even more pending in the socket */ + if (*available == sizeof(req->read_buf) && !req->gzip_stream) /* check if we have even more pending in the socket */ { DWORD extra; if (NETCON_query_data_available(&req->netConnection, &extra)) @@ -2396,6 +2555,16 @@ static BOOL HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel, index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only); break; + case HTTP_QUERY_CONTENT_LENGTH: + if(lpwhr->gzip_stream) { + INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND); + return FALSE; + } + + index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level], + requested_index,request_only); + break; + case HTTP_QUERY_RAW_HEADERS_CRLF: { LPWSTR headers; @@ -2504,6 +2673,10 @@ static BOOL HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel, return TRUE; } break; + case HTTP_QUERY_CONTENT_ENCODING: + index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level], + requested_index,request_only); + break; default: assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1)); diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h index 95632bc80d7..1d49eaa7c3f 100644 --- a/dlls/wininet/internet.h +++ b/dlls/wininet/internet.h @@ -183,6 +183,8 @@ typedef struct struct HttpAuthInfo; +typedef struct gzip_stream_t gzip_stream_t; + typedef struct { WININETHANDLEHEADER hdr; @@ -209,6 +211,9 @@ typedef struct DWORD read_pos; /* current read position in read_buf */ DWORD read_size; /* valid data size in read_buf */ BYTE read_buf[4096]; /* buffer for already read but not returned data */ + + BOOL decoding; + gzip_stream_t *gzip_stream; } WININETHTTPREQW, *LPWININETHTTPREQW; diff --git a/include/config.h.in b/include/config.h.in index 12ce108bee5..c22d1f7863f 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -1041,6 +1041,12 @@ /* Define if Xrender has the XRenderSetPictureTransform function */ #undef HAVE_XRENDERSETPICTURETRANSFORM +/* Define to 1 if you have the `z' library (-lz). */ +#undef HAVE_ZLIB + +/* Define to 1 if you have the header file. */ +#undef HAVE_ZLIB_H + /* Define to 1 if you have the `_pclose' function. */ #undef HAVE__PCLOSE