Author: akhaldi Date: Sat Apr 26 17:34:51 2014 New Revision: 62990
URL: http://svn.reactos.org/svn/reactos?rev=62990&view=rev Log: [WINHTTP] * Sync with Wine 1.7.17. CORE-8080
Modified: trunk/reactos/dll/win32/winhttp/cookie.c trunk/reactos/dll/win32/winhttp/main.c trunk/reactos/dll/win32/winhttp/net.c trunk/reactos/dll/win32/winhttp/request.c trunk/reactos/dll/win32/winhttp/session.c trunk/reactos/dll/win32/winhttp/winhttp_private.h trunk/reactos/dll/win32/winhttp/winhttp_tlb.idl trunk/reactos/media/doc/README.WINE
Modified: trunk/reactos/dll/win32/winhttp/cookie.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/cookie.c?... ============================================================================== --- trunk/reactos/dll/win32/winhttp/cookie.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/cookie.c [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -122,20 +122,21 @@ const WCHAR *p; int len;
+ if (!(p = strchrW( string, '=' ))) + { + WARN("no '=' in %s\n", debugstr_w(string)); + return NULL; + } + if (p == string) + { + WARN("empty cookie name in %s\n", debugstr_w(string)); + return NULL; + } + if (!(cookie = heap_alloc_zero( sizeof(cookie_t) ))) return NULL;
list_init( &cookie->entry );
- if (!(p = strchrW( string, '=' ))) - { - WARN("no '=' in %s\n", debugstr_w(string)); - return NULL; - } - if (p == string) - { - WARN("empty cookie name in %s\n", debugstr_w(string)); - return NULL; - } len = p - string; if (!(cookie->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) {
Modified: trunk/reactos/dll/win32/winhttp/main.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/main.c?re... ============================================================================== --- trunk/reactos/dll/win32/winhttp/main.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/main.c [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -42,7 +42,7 @@ return TRUE; }
-typedef HRESULT (*fnCreateInstance)( IUnknown *outer, void **obj ); +typedef HRESULT (*fnCreateInstance)( void **obj );
struct winhttp_cf { @@ -99,14 +99,11 @@ if (outer) return CLASS_E_NOAGGREGATION;
- hr = cf->pfnCreateInstance( outer, (void **)&unknown ); + hr = cf->pfnCreateInstance( (void **)&unknown ); if (FAILED(hr)) return hr;
hr = IUnknown_QueryInterface( unknown, riid, obj ); - if (FAILED(hr)) - return hr; - IUnknown_Release( unknown ); return hr; }
Modified: trunk/reactos/dll/win32/winhttp/net.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/net.c?rev... ============================================================================== --- trunk/reactos/dll/win32/winhttp/net.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/net.c [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -705,24 +705,23 @@ return TRUE; }
-BOOL netconn_query_data_available( netconn_t *conn, DWORD *available ) -{ +ULONG netconn_query_data_available( netconn_t *conn ) +{ + if(!netconn_connected(conn)) + return 0; + + if(conn->secure) { + return conn->peek_len; + }else { #ifdef FIONREAD - int ret; - ULONG unread; -#endif - *available = 0; - if (!netconn_connected( conn )) return FALSE; - - if (conn->secure) - { - *available = conn->peek_len; - return TRUE; - } -#ifdef FIONREAD - if (!(ret = ioctlsocket( conn->socket, FIONREAD, &unread ))) *available = unread; -#endif - return TRUE; + ULONG unread; + + if(!ioctlsocket(conn->socket, FIONREAD, &unread)) + return unread; +#endif + } + + return 0; }
DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )
Modified: trunk/reactos/dll/win32/winhttp/request.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/request.c... ============================================================================== --- trunk/reactos/dll/win32/winhttp/request.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/request.c [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -25,6 +25,7 @@ # include <arpa/inet.h> #endif
+#include <assert.h> #include <winuser.h> #include <httprequest.h>
@@ -852,7 +853,7 @@
/* * Set (header) termination string for request - * Make sure there's exactly two new lines at the end of the request + * Make sure there are exactly two new lines at the end of the request */ p = &requestString[strlenW(requestString)-1]; while ( (*p == '\n') || (*p == '\r') ) @@ -995,6 +996,8 @@ done: request->read_pos = request->read_size = 0; request->read_chunked = FALSE; + request->read_chunked_size = ~0u; + request->read_chunked_eof = FALSE; heap_free( addressW ); return TRUE; } @@ -1121,7 +1124,7 @@ request->optional_len = optional_len; len += optional_len; } - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(DWORD) ); + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
end: if (async) @@ -1374,7 +1377,7 @@ char c0, c1, c2, c3; const WCHAR *p = base64;
- while (len >= 4) + while (len > 4) { if ((c0 = decode_char( p[0] )) > 63) return 0; if ((c1 = decode_char( p[1] )) > 63) return 0; @@ -1411,6 +1414,21 @@ buf[i + 1] = (c1 << 4) | (c2 >> 2); } i += 2; + } + else + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0; + if ((c3 = decode_char( p[3] )) > 63) return 0; + + if (buf) + { + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); + buf[i + 2] = (c2 << 6) | c3; + } + i += 3; } return i; } @@ -1600,7 +1618,11 @@ { int len = strlenW( ++p ); in.cbBuffer = decode_base64( p, len, NULL ); - if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) return FALSE; + if (!(in.pvBuffer = heap_alloc( in.cbBuffer ))) { + destroy_authinfo( authinfo ); + *auth_ptr = NULL; + return FALSE; + } decode_base64( p, len, in.pvBuffer ); } out.BufferType = SECBUFFER_TOKEN; @@ -1608,6 +1630,8 @@ if (!(out.pvBuffer = heap_alloc( authinfo->max_token ))) { heap_free( in.pvBuffer ); + destroy_authinfo( authinfo ); + *auth_ptr = NULL; return FALSE; } out_desc.ulVersion = 0; @@ -1783,15 +1807,20 @@ { request->content_length = ~0u; request->read_chunked = TRUE; + request->read_chunked_size = ~0u; + request->read_chunked_eof = FALSE; } request->content_read = 0; return request->content_length; }
/* read some more data into the read buffer */ -static BOOL read_more_data( request_t *request, int maxlen ) +static BOOL read_more_data( request_t *request, int maxlen, BOOL notify ) { int len; + BOOL ret; + + if (request->read_chunked_eof) return FALSE;
if (request->read_size && request->read_pos) { @@ -1800,10 +1829,16 @@ request->read_pos = 0; } if (maxlen == -1) maxlen = sizeof(request->read_buf); - if (!netconn_recv( &request->netconn, request->read_buf + request->read_size, - maxlen - request->read_size, 0, &len )) return FALSE; + + if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 ); + + ret = netconn_recv( &request->netconn, request->read_buf + request->read_size, + maxlen - request->read_size, 0, &len ); + + if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) ); + request->read_size += len; - return TRUE; + return ret; }
/* remove some amount of data from the read buffer */ @@ -1833,7 +1868,7 @@ remove_data( request, bytes_read ); if (eol) break;
- if (!read_more_data( request, -1 )) return FALSE; + if (!read_more_data( request, -1, TRUE )) return FALSE; if (!request->read_size) { *len = 0; @@ -1852,7 +1887,7 @@ }
/* discard data contents until we reach end of line */ -static BOOL discard_eol( request_t *request ) +static BOOL discard_eol( request_t *request, BOOL notify ) { do { @@ -1863,24 +1898,23 @@ break; } request->read_pos = request->read_size = 0; /* discard everything */ - if (!read_more_data( request, -1 )) return FALSE; + if (!read_more_data( request, -1, notify )) return FALSE; } while (request->read_size); return TRUE; }
/* read the size of the next chunk */ -static BOOL start_next_chunk( request_t *request ) +static BOOL start_next_chunk( request_t *request, BOOL notify ) { DWORD chunk_size = 0;
- if (!request->content_length) return TRUE; - if (request->content_length == request->content_read) - { - /* read terminator for the previous chunk */ - if (!discard_eol( request )) return FALSE; - request->content_length = ~0u; - request->content_read = 0; - } + assert(!request->read_chunked_size || request->read_chunked_size == ~0u); + + if (request->read_chunked_eof) return FALSE; + + /* read terminator for the previous chunk */ + if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE; + for (;;) { while (request->read_size) @@ -1892,17 +1926,22 @@ else if (ch == ';' || ch == '\r' || ch == '\n') { TRACE("reading %u byte chunk\n", chunk_size); - request->content_length = chunk_size; - request->content_read = 0; - if (!discard_eol( request )) return FALSE; - return TRUE; + + if (request->content_length == ~0u) request->content_length = chunk_size; + else request->content_length += chunk_size; + + request->read_chunked_size = chunk_size; + if (!chunk_size) request->read_chunked_eof = TRUE; + + return discard_eol( request, notify ); } remove_data( request, 1 ); } - if (!read_more_data( request, -1 )) return FALSE; + if (!read_more_data( request, -1, notify )) return FALSE; if (!request->read_size) { request->content_length = request->content_read = 0; + request->read_chunked_size = 0; return TRUE; } } @@ -1911,32 +1950,34 @@ /* return the size of data available to be read immediately */ static DWORD get_available_data( request_t *request ) { - if (request->read_chunked && - (request->content_length == ~0u || request->content_length == request->content_read)) - return 0; - return min( request->read_size, request->content_length - request->content_read ); + if (request->read_chunked) return min( request->read_chunked_size, request->read_size ); + return request->read_size; }
/* check if we have reached the end of the data to read */ static BOOL end_of_read_data( request_t *request ) { - if (request->read_chunked) return (request->content_length == 0); + if (request->read_chunked) return request->read_chunked_eof; if (request->content_length == ~0u) return FALSE; return (request->content_length == request->content_read); }
-static BOOL refill_buffer( request_t *request ) +static BOOL refill_buffer( request_t *request, BOOL notify ) { int len = sizeof(request->read_buf);
- if (request->read_chunked && - (request->content_length == ~0u || request->content_length == request->content_read)) - { - if (!start_next_chunk( request )) return FALSE; - } - if (request->content_length != ~0u) len = min( len, request->content_length - request->content_read ); + if (request->read_chunked) + { + if (request->read_chunked_eof) return FALSE; + if (request->read_chunked_size == ~0u || !request->read_chunked_size) + { + if (!start_next_chunk( request, notify )) return FALSE; + } + } + if (!request->read_chunked && request->content_length != ~0u) + len = min( len, request->content_length - request->content_read ); if (len <= request->read_size) return TRUE; - if (!read_more_data( request, len )) return FALSE; + if (!read_more_data( request, len, notify )) return FALSE; if (!request->read_size) request->content_length = request->content_read = 0; return TRUE; } @@ -1955,8 +1996,6 @@ WCHAR status_codeW[4]; /* sizeof("nnn") */
if (!netconn_connected( &request->netconn )) return FALSE; - - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
received_len = 0; do @@ -2013,7 +2052,7 @@ header_t *header;
buflen = MAX_REPLY_LEN; - if (!read_line( request, buffer, &buflen )) goto end; + if (!read_line( request, buffer, &buflen )) return TRUE; received_len += buflen; if (!*buffer) break;
@@ -2038,9 +2077,6 @@ }
TRACE("raw headers: %s\n", debugstr_w(raw_headers)); - -end: - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &received_len, sizeof(DWORD) ); return TRUE; }
@@ -2064,46 +2100,35 @@
static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) { - BOOL ret = TRUE; - int len, bytes_read = 0; - - if (request->read_chunked && - (request->content_length == ~0u || request->content_length == request->content_read)) - { - if (!start_next_chunk( request )) goto done; - } - if (request->content_length != ~0u) size = min( size, request->content_length - request->content_read ); - - if (request->read_size) - { - bytes_read = min( request->read_size, size ); - memcpy( buffer, request->read_buf + request->read_pos, bytes_read ); - remove_data( request, bytes_read ); - } - if (size > bytes_read && (!bytes_read || !async)) - { - if ((ret = netconn_recv( &request->netconn, (char *)buffer + bytes_read, size - bytes_read, - async ? 0 : MSG_WAITALL, &len ))) - bytes_read += len; - } + int count, bytes_read = 0; + + if (end_of_read_data( request )) goto done; + + while (size) + { + if (!(count = get_available_data( request ))) + { + if (!refill_buffer( request, async )) goto done; + if (!(count = get_available_data( request ))) goto done; + } + count = min( count, size ); + memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count ); + remove_data( request, count ); + if (request->read_chunked) request->read_chunked_size -= count; + size -= count; + bytes_read += count; + request->content_read += count; + if (end_of_read_data( request )) goto done; + } + if (request->read_chunked && !request->read_chunked_size) refill_buffer( request, async );
done: - request->content_read += bytes_read; TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length ); - if (async) - { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read ); - else - { - WINHTTP_ASYNC_RESULT result; - result.dwResult = API_READ_DATA; - result.dwError = get_last_error(); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); - } - } + + if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read ); if (read) *read = bytes_read; - if (!bytes_read && request->content_read == request->content_length) finished_reading( request ); - return ret; + if (end_of_read_data( request )) finished_reading( request ); + return TRUE; }
/* read any content returned by the server so that the connection can be reused */ @@ -2112,11 +2137,6 @@ DWORD bytes_read; char buffer[2048];
- if (request->content_length == ~0u) - { - finished_reading( request ); - return; - } for (;;) { if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return; @@ -2218,6 +2238,7 @@ if (!(ret = netconn_init( &request->netconn ))) goto end; request->read_pos = request->read_size = 0; request->read_chunked = FALSE; + request->read_chunked_eof = FALSE; } if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end; if (!(ret = open_connection( request ))) goto end; @@ -2300,6 +2321,8 @@ break; }
+ if (ret) refill_buffer( request, FALSE ); + if (async) { if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 ); @@ -2362,45 +2385,22 @@
static BOOL query_data_available( request_t *request, DWORD *available, BOOL async ) { - BOOL ret = TRUE; - DWORD count; - - if (!(count = get_available_data( request ))) - { - if (end_of_read_data( request )) - { - if (available) *available = 0; - return TRUE; - } - } - refill_buffer( request ); - count = get_available_data( request ); - - if (count == sizeof(request->read_buf)) /* check if we have even more pending in the socket */ - { - DWORD extra; - if ((ret = netconn_query_data_available( &request->netconn, &extra ))) - { - count = min( count + extra, request->content_length - request->content_read ); - } - } - if (async) - { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) ); - else - { - WINHTTP_ASYNC_RESULT result; - result.dwResult = API_QUERY_DATA_AVAILABLE; - result.dwError = get_last_error(); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); - } - } - if (ret) - { - TRACE("%u bytes available\n", count); - if (available) *available = count; - } - return ret; + DWORD count = get_available_data( request ); + + if (!request->read_chunked) + count += netconn_query_data_available( &request->netconn ); + if (!count) + { + refill_buffer( request, async ); + count = get_available_data( request ); + if (!request->read_chunked) + count += netconn_query_data_available( &request->netconn ); + } + + if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) ); + TRACE("%u bytes available\n", count); + if (available) *available = count; + return TRUE; }
static void task_query_data_available( task_header_t *task ) @@ -2508,7 +2508,7 @@
if (async) { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) ); + if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) ); else { WINHTTP_ASYNC_RESULT result; @@ -3810,11 +3810,11 @@ winhttp_request_SetAutoLogonPolicy };
-HRESULT WinHttpRequest_create( IUnknown *unknown, void **obj ) +HRESULT WinHttpRequest_create( void **obj ) { struct winhttp_request *request;
- TRACE("%p, %p\n", unknown, obj); + TRACE("%p\n", obj);
if (!(request = heap_alloc( sizeof(*request) ))) return E_OUTOFMEMORY; request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
Modified: trunk/reactos/dll/win32/winhttp/session.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/session.c... ============================================================================== --- trunk/reactos/dll/win32/winhttp/session.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/session.c [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -1354,7 +1354,11 @@ FIXME("getaddrinfo not found at build time\n"); #endif } - if (!ret) set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); + if (!ret) + { + set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); + *url = NULL; + } return ret; }
@@ -1597,11 +1601,11 @@ heap_free( hdr ); if (!ret) { - heap_free( config->lpszAutoConfigUrl ); + GlobalFree( config->lpszAutoConfigUrl ); config->lpszAutoConfigUrl = NULL; - heap_free( config->lpszProxy ); + GlobalFree( config->lpszProxy ); config->lpszProxy = NULL; - heap_free( config->lpszProxyBypass ); + GlobalFree( config->lpszProxyBypass ); config->lpszProxyBypass = NULL; } return ret;
Modified: trunk/reactos/dll/win32/winhttp/winhttp_private.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/winhttp_p... ============================================================================== --- trunk/reactos/dll/win32/winhttp/winhttp_private.h [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/winhttp_private.h [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -202,9 +202,11 @@ int send_timeout; int recv_timeout; LPWSTR status_text; - DWORD content_length; /* total number of bytes to be read (per chunk) */ + DWORD content_length; /* total number of bytes to be read */ DWORD content_read; /* bytes read so far */ BOOL read_chunked; /* are we reading in chunked mode? */ + BOOL read_chunked_eof; /* end of stream in chunked mode */ + BOOL read_chunked_size; /* chunk size remaining */ DWORD read_pos; /* current read position in read_buf */ DWORD read_size; /* valid data size in read_buf */ char read_buf[4096]; /* buffer for already read but not returned data */ @@ -279,7 +281,7 @@ BOOL netconn_create( netconn_t *, int, int, int ) DECLSPEC_HIDDEN; BOOL netconn_init( netconn_t * ) DECLSPEC_HIDDEN; void netconn_unload( void ) DECLSPEC_HIDDEN; -BOOL netconn_query_data_available( netconn_t *, DWORD * ) DECLSPEC_HIDDEN; +ULONG netconn_query_data_available( netconn_t * ) DECLSPEC_HIDDEN; BOOL netconn_recv( netconn_t *, void *, size_t, int, int * ) DECLSPEC_HIDDEN; BOOL netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr *, socklen_t *, int ) DECLSPEC_HIDDEN; BOOL netconn_secure_connect( netconn_t *, WCHAR * ) DECLSPEC_HIDDEN; @@ -295,7 +297,7 @@ BOOL set_server_for_hostname( connect_t *, LPCWSTR, INTERNET_PORT ) DECLSPEC_HIDDEN; void destroy_authinfo( struct authinfo * ) DECLSPEC_HIDDEN;
-extern HRESULT WinHttpRequest_create( IUnknown *, void ** ) DECLSPEC_HIDDEN; +extern HRESULT WinHttpRequest_create( void ** ) DECLSPEC_HIDDEN;
static inline const char *debugstr_variant( const VARIANT *v ) {
Modified: trunk/reactos/dll/win32/winhttp/winhttp_tlb.idl URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/winhttp/winhttp_t... ============================================================================== --- trunk/reactos/dll/win32/winhttp/winhttp_tlb.idl [iso-8859-1] (original) +++ trunk/reactos/dll/win32/winhttp/winhttp_tlb.idl [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -18,4 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#pragma makedep regtypelib + #include "httprequest.idl"
Modified: trunk/reactos/media/doc/README.WINE URL: http://svn.reactos.org/svn/reactos/trunk/reactos/media/doc/README.WINE?rev=6... ============================================================================== --- trunk/reactos/media/doc/README.WINE [iso-8859-1] (original) +++ trunk/reactos/media/doc/README.WINE [iso-8859-1] Sat Apr 26 17:34:51 2014 @@ -207,7 +207,7 @@ reactos/dll/win32/windowscodecsext # Synced to Wine-1.7.1 reactos/dll/win32/winemp3.acm # Synced to Wine-1.7.1 reactos/dll/win32/wing32 # Out of sync -reactos/dll/win32/winhttp # Synced to Wine-1.7.1 +reactos/dll/win32/winhttp # Synced to Wine-1.7.17 reactos/dll/win32/wininet # Synced to Wine-1.7.1 reactos/dll/win32/winmm # Forked at Wine-20050628 reactos/dll/win32/winmm/midimap # Forked at Wine-20050628