Sync DrDump crash handler with TortoiseSVN codebase
[TortoiseGit.git] / ext / CrashServer / CrashHandler / DumpUploaderServiceLib / gsoapWinInet.cpp
blobbddd0c99ace0a3ecf5092392b0d7a1c7411ccfad
1 /*
2 See the header file for details. This file is distributed under the MIT licence.
3 */
5 #include "gsoapWinInet.h"
7 /* ensure that the wininet library is linked */
8 #pragma comment( lib, "wininet.lib" )
9 /* disable deprecation warnings */
10 #pragma warning(disable : 4996)
12 #define UNUSED_ARG(x) (x)
13 #define INVALID_BUFFER_LENGTH ((DWORD)-1)
15 /* plugin id */
16 static const char wininet_id[] = "wininet-2.0";
18 /* plugin private data */
19 struct wininet_data
21 HINTERNET hInternet; /* internet session handle */
22 HINTERNET hConnection; /* current connection handle */
23 BOOL bDisconnect; /* connection is disconnected */
24 BOOL bKeepAlive; /* keep connection alive */
25 DWORD dwRequestFlags; /* extra request flags from user */
26 char * pBuffer; /* send buffer */
27 size_t uiBufferLenMax; /* total length of the message */
28 size_t uiBufferLen; /* length of data in buffer */
29 BOOL bIsChunkSize; /* expecting a chunk size buffer */
30 wininet_rse_callback pRseCallback; /* wininet_resolve_send_error callback. Allows clients to resolve ssl errors programatically */
31 wininet_progress_callback pProgressCallback; /* callback to report a progress */
32 LPVOID pProgressCallbackContext; /* context for pProgressCallback */
33 char * pRespHeaders; /* buffer for response headers */
34 size_t uiRespHeadersLen; /* length of data in response headers buffer */
35 size_t uiRespHeadersReaded;/* length of data readed from response headers buffer */
36 char * pszErrorMessage; /* wininet/system error message */
39 #if 1
40 #define Trace(pszFunc, pBuffer, uiBufferLen) \
41 DBGLOG(TEST, SOAP_MESSAGE(fdebug, "wininet %p: %s 0x%p %d\n", soap, pszFunc, pBuffer, uiBufferLen));
42 #else
43 void Trace(const char* pszFunc, const void* pBuffer, DWORD uiBufferLen)
46 static bool newOne = true;
47 FILE* f = fopen("C:\\log.log", newOne ? "wt" : "at");
48 newOne = false;
49 if (f)
51 fprintf(f, "\n============================================\n%s:\n", pszFunc);
52 if (pBuffer)
53 fwrite(pBuffer, uiBufferLen, 1, f);
54 fclose(f);
58 #endif
60 /* forward declarations */
61 static BOOL
62 wininet_init(
63 struct soap * soap,
64 struct wininet_data * a_pData,
65 DWORD a_dwRequestFlags );
66 static int
67 wininet_copy(
68 struct soap * soap,
69 struct soap_plugin * a_pDst,
70 struct soap_plugin * a_pSrc );
71 static void
72 wininet_delete(
73 struct soap * soap,
74 struct soap_plugin * a_pPluginData );
75 static SOAP_SOCKET
76 wininet_connect(
77 struct soap * soap,
78 const char * a_pszEndpoint,
79 const char * a_pszHost,
80 int a_nPort );
81 static int
82 wininet_post_header(
83 struct soap * soap,
84 const char * a_pszKey,
85 const char * a_pszValue );
86 static int
87 wininet_fsend(
88 struct soap * soap,
89 const char * a_pBuffer,
90 size_t a_uiBufferLen );
91 static size_t
92 wininet_frecv(
93 struct soap * soap,
94 char * a_pBuffer,
95 size_t a_uiBufferLen );
96 static int
97 wininet_disconnect(
98 struct soap * soap );
99 void CALLBACK
100 wininet_callback(
101 HINTERNET hInternet,
102 DWORD_PTR dwContext,
103 DWORD dwInternetStatus,
104 LPVOID lpvStatusInformation,
105 DWORD dwStatusInformationLength );
106 static BOOL
107 wininet_have_connection(
108 struct soap * soap,
109 struct wininet_data * a_pData );
110 static int
111 wininet_fpoll(
112 struct soap * soap );
113 static DWORD
114 wininet_set_timeout(
115 struct soap * soap,
116 struct wininet_data * a_pData,
117 const char * a_pszTimeout,
118 DWORD a_dwOption,
119 int a_nTimeout );
120 static BOOL
121 wininet_resolve_send_error(
122 HINTERNET a_hHttpRequest,
123 DWORD a_dwErrorCode );
124 static const char *
125 wininet_error_message(
126 struct soap * a_pData,
127 DWORD a_dwErrorMsgId );
128 static void
129 wininet_free_error_message(
130 struct wininet_data * a_pData );
132 /* plugin registration */
134 wininet_plugin(
135 struct soap * soap,
136 struct soap_plugin * a_pPluginData,
137 void * a_dwRequestFlags )
139 if ( !soap )
140 return SOAP_FATAL_ERROR;
142 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
143 "wininet %p: plugin registration\n", soap ));
145 a_pPluginData->id = wininet_id;
146 a_pPluginData->fcopy = wininet_copy;
147 a_pPluginData->fdelete = wininet_delete;
148 a_pPluginData->data = (void*) malloc( sizeof(struct wininet_data) );
149 if ( !a_pPluginData->data )
151 return SOAP_EOM;
153 if ( !wininet_init( soap,
154 (struct wininet_data *) a_pPluginData->data,
155 (DWORD) (size_t) a_dwRequestFlags ) )
157 free( a_pPluginData->data );
158 return SOAP_EOM;
161 #ifdef SOAP_DEBUG
162 if ( (soap->omode & SOAP_IO) == SOAP_IO_STORE )
164 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
165 "wininet %p: use of SOAP_IO_STORE is not recommended\n", soap ));
167 #endif
169 return SOAP_OK;
172 /* initialize private data */
173 static BOOL
174 wininet_init(
175 struct soap * soap,
176 struct wininet_data * a_pData,
177 DWORD a_dwRequestFlags )
179 if ( !soap )
180 return SOAP_FATAL_ERROR;
182 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
183 "wininet %p: init private data\n", soap ));
185 memset( a_pData, 0, sizeof(struct wininet_data) );
186 a_pData->dwRequestFlags = a_dwRequestFlags;
188 Trace("InternetOpenA", 0, 0);
190 /* start our internet session */
191 a_pData->hInternet = InternetOpenA(
192 "gSOAP", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
193 if ( !a_pData->hInternet )
195 soap->error = SOAP_EOF;
196 soap->errnum = GetLastError();
197 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
198 "wininet %p: init, error %d (%s) in InternetOpen\n",
199 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
200 wininet_free_error_message( a_pData );
201 return FALSE;
204 /* set the timeouts, if any of these fail the error isn't fatal */
205 wininet_set_timeout( soap, a_pData, "connect",
206 INTERNET_OPTION_CONNECT_TIMEOUT, soap->connect_timeout );
207 wininet_set_timeout( soap, a_pData, "receive",
208 INTERNET_OPTION_RECEIVE_TIMEOUT, soap->recv_timeout );
209 wininet_set_timeout( soap, a_pData, "send",
210 INTERNET_OPTION_SEND_TIMEOUT, soap->send_timeout );
212 /* set up the callback function so we get notifications */
213 InternetSetStatusCallback( a_pData->hInternet, wininet_callback );
215 /* set all of our callbacks */
216 soap->fopen = wininet_connect;
217 soap->fposthdr = wininet_post_header;
218 soap->fsend = wininet_fsend;
219 soap->frecv = wininet_frecv;
220 soap->fclose = wininet_disconnect;
221 soap->fpoll = wininet_fpoll;
223 return TRUE;
226 void
227 wininet_set_rse_callback(
228 struct soap * soap,
229 wininet_rse_callback a_pRsecallback)
231 struct wininet_data * pData = (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
233 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
234 "wininet %p: resolve_send_error callback = '%p'\n", soap, a_pRsecallback ));
236 pData->pRseCallback = a_pRsecallback;
239 void
240 wininet_set_progress_callback(
241 struct soap * soap,
242 wininet_progress_callback a_pProgressCallback,
243 LPVOID a_pContext)
245 struct wininet_data * pData = (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
247 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
248 "wininet %p: progress callback = '%p', context = '%p'\n", soap, a_pProgressCallback, a_pContext ));
250 pData->pProgressCallback = a_pProgressCallback;
251 pData->pProgressCallbackContext = a_pContext;
254 /* copy the private data structure */
255 static int
256 wininet_copy(
257 struct soap * soap,
258 struct soap_plugin * a_pDst,
259 struct soap_plugin * a_pSrc )
261 UNUSED_ARG( soap );
262 UNUSED_ARG( a_pDst );
263 UNUSED_ARG( a_pSrc );
265 _ASSERTE( !"wininet doesn't support copy" );
266 return SOAP_FATAL_ERROR;
269 /* deallocate of our private structure */
270 static void
271 wininet_delete(
272 struct soap * soap,
273 struct soap_plugin * a_pPluginData )
275 struct wininet_data * pData =
276 (struct wininet_data *) a_pPluginData->data;
278 UNUSED_ARG( soap );
280 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
281 "wininet %p: delete private data\n", soap ));
283 /* force a disconnect of any existing connection */
284 pData->bDisconnect = TRUE;
285 wininet_have_connection( soap, pData );
287 /* close down the internet */
288 if ( pData->hInternet )
290 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
291 "wininet %p: closing internet handle\n", soap));
292 InternetCloseHandle( pData->hInternet );
293 pData->hInternet = NULL;
296 /* free our data */
297 wininet_free_error_message( pData );
298 free( a_pPluginData->data );
301 /* gsoap documentation:
302 Called from a client proxy to open a connection to a Web Service located
303 at endpoint. Input parameters host and port are micro-parsed from endpoint.
304 Should return a valid file descriptor, or SOAP_INVALID_SOCKET and
305 soap->error set to an error code. Built-in gSOAP function: tcp_connect
307 static SOAP_SOCKET
308 wininet_connect(
309 struct soap * soap,
310 const char * a_pszEndpoint,
311 const char * a_pszHost,
312 int a_nPort )
314 URL_COMPONENTSA urlComponents;
315 char szUrlPath[MAX_PATH] = {0};
316 char szHost[MAX_PATH] = {0};
317 DWORD dwFlags;
318 HINTERNET hConnection = NULL;
319 HINTERNET hHttpRequest = NULL;
320 const char * pszVerb;
321 INTERNET_PORT nPort;
322 struct wininet_data * pData =
323 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
325 soap->error = SOAP_OK;
327 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
328 "wininet %p: connect, endpoint = '%s'\n", soap, a_pszEndpoint ));
330 /* we should be initialized but not connected */
331 _ASSERTE( pData->hInternet );
332 if ( !pData->hInternet )
333 return SOAP_FATAL_ERROR;
334 _ASSERTE( !pData->bKeepAlive && !pData->hConnection || pData->bKeepAlive && pData->hConnection );
335 if ( !(!pData->bKeepAlive && !pData->hConnection || pData->bKeepAlive && pData->hConnection) )
336 return SOAP_FATAL_ERROR;
337 _ASSERTE( soap->socket == SOAP_INVALID_SOCKET );
338 if ( soap->socket != SOAP_INVALID_SOCKET )
339 return SOAP_FATAL_ERROR;
341 /* parse out the url path */
342 memset( &urlComponents, 0, sizeof(urlComponents) );
343 urlComponents.dwStructSize = sizeof(urlComponents);
344 urlComponents.lpszHostName = szHost;
345 urlComponents.dwHostNameLength = MAX_PATH;
346 urlComponents.lpszUrlPath = szUrlPath;
347 urlComponents.dwUrlPathLength = MAX_PATH;
348 if ( !InternetCrackUrlA( a_pszEndpoint, 0, 0, &urlComponents ) )
350 soap->error = SOAP_TCP_ERROR;
351 soap->errnum = GetLastError();
352 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
353 "wininet %p: connect, error %d (%s) in InternetCrackUrl\n",
354 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
355 return SOAP_INVALID_SOCKET;
358 nPort = urlComponents.nPort;
360 if ( !pData->hConnection )
362 Trace("InternetConnectA", szHost, (DWORD)strlen(szHost));
364 /* connect to the target url, if we haven't connected yet
365 or if it was dropped */
366 hConnection = InternetConnectA( pData->hInternet,
367 szHost, nPort, "", "", INTERNET_SERVICE_HTTP,
368 0, (DWORD_PTR) soap );
369 if ( !hConnection )
371 soap->error = SOAP_TCP_ERROR;
372 soap->errnum = GetLastError();
373 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
374 "wininet %p: connect, error %d (%s) in InternetConnect\n",
375 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
376 return SOAP_INVALID_SOCKET;
379 else
381 hConnection = pData->hConnection;
382 pData->hConnection = NULL;
386 Note that although we specify HTTP/1.1 for the connection here, the
387 actual connection may be HTTP/1.0 depending on the settings in the
388 control panel. See the "Internet Options", "HTTP 1.1 settings".
390 dwFlags = pData->dwRequestFlags | INTERNET_FLAG_NO_CACHE_WRITE;
391 if ( soap->omode & SOAP_IO_KEEPALIVE )
393 dwFlags |= INTERNET_FLAG_KEEP_CONNECTION;
394 pData->bKeepAlive = TRUE;
396 if ( urlComponents.nScheme == INTERNET_SCHEME_HTTPS )
398 dwFlags |= INTERNET_FLAG_SECURE;
401 /* proxy requires full endpoint URL */
402 if ( soap->proxy_host )
404 strncpy(szUrlPath, a_pszEndpoint, MAX_PATH);
407 /* status determines the HTTP verb */
408 switch ( soap->status )
410 case SOAP_GET:
411 pszVerb = "GET";
412 break;
413 case SOAP_PUT:
414 pszVerb = "PUT";
415 break;
416 case SOAP_DEL:
417 pszVerb = "DELETE";
418 break;
419 case SOAP_CONNECT:
420 pszVerb = "CONNECT";
421 _snprintf(szUrlPath, MAX_PATH, "%s:%d", a_pszHost, a_nPort);
422 break;
423 default:
424 pszVerb = "POST";
427 Trace("HttpOpenRequestA", szUrlPath, (DWORD)strlen(szUrlPath));
429 hHttpRequest = HttpOpenRequestA(
430 hConnection, pszVerb, szUrlPath, "HTTP/1.1", NULL, NULL,
431 dwFlags, (DWORD_PTR) soap );
432 if ( !hHttpRequest )
434 InternetCloseHandle( hConnection );
435 pData->bKeepAlive = FALSE;
436 soap->error = SOAP_HTTP_ERROR;
437 soap->errnum = GetLastError();
438 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
439 "wininet %p: connect, error %d (%s) in HttpOpenRequest\n",
440 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
441 wininet_free_error_message(pData);
442 return SOAP_INVALID_SOCKET;
445 /* save the connection handle in our data structure */
446 pData->hConnection = hConnection;
448 /* return the http request handle as our file descriptor. */
449 _ASSERTE( sizeof(soap->socket) >= sizeof(HINTERNET) );
450 return (SOAP_SOCKET) hHttpRequest;
453 /* gsoap documentation:
454 Called by http_post and http_response (through the callbacks). Emits HTTP
455 key: val header entries. Should return SOAP_OK, or a gSOAP error code.
456 Built-in gSOAP function: http_post_header.
458 static int
459 wininet_post_header(
460 struct soap * soap,
461 const char * a_pszKey,
462 const char * a_pszValue )
464 HINTERNET hHttpRequest = (HINTERNET) soap->socket;
465 char szHeader[4096];
466 int nLen;
467 BOOL bResult = FALSE;
468 struct wininet_data * pData =
469 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
471 soap->error = SOAP_OK;
473 /* ensure that our connection hasn't been disconnected */
474 if ( !wininet_have_connection( soap, pData ) )
476 return SOAP_EOF;
479 /* if this is the initial POST header then we initialize our send buffer */
480 if ( a_pszKey && !a_pszValue )
482 _ASSERTE( !pData->pBuffer );
483 pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
484 pData->uiBufferLen = 0;
486 /* if we are using chunk output then we start with a chunk size */
487 pData->bIsChunkSize = ( (soap->omode & SOAP_IO) == SOAP_IO_CHUNK );
489 else if ( a_pszValue )
491 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
492 "wininet %p: post_header, adding '%s: %s'\n",
493 soap, a_pszKey, a_pszValue ));
495 /* determine the maximum length of this message so that we can
496 correctly determine when we have completed the send */
497 if ( !strcmp( a_pszKey, "Content-Length" ) )
499 _ASSERTE( pData->uiBufferLenMax == INVALID_BUFFER_LENGTH );
500 pData->uiBufferLenMax = strtoul( a_pszValue, NULL, 10 );
503 nLen = _snprintf(
504 szHeader, 4096, "%s: %s\r\n", a_pszKey, a_pszValue );
505 if ( nLen < 0 )
507 return SOAP_EOM;
510 Trace("HttpAddRequestHeadersA", szHeader, nLen);
512 bResult = HttpAddRequestHeadersA( hHttpRequest, szHeader, nLen,
513 HTTP_ADDREQ_FLAG_ADD_IF_NEW );
514 #ifdef SOAP_DEBUG
516 we don't return an error if this fails because it isn't
517 (or shouldn't be) critical.
519 if ( !bResult )
521 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
522 "wininet %p: post_header, error %d (%s) in HttpAddRequestHeaders\n",
523 soap, GetLastError(), wininet_error_message(soap,GetLastError()) ));
525 #endif
527 return SOAP_OK;
530 /* gsoap documentation:
531 Called for all send operations to emit contents of s of length n.
532 Should return SOAP_OK, or a gSOAP error code. Built-in gSOAP
533 function: fsend
535 Notes:
536 I do a heap of buffering here because we need the entire message available
537 in a single buffer in order to iterate through the sending loop. I had
538 hoped that the SOAP_IO_STORE flag would have worked to do the same, however
539 this still breaks the messages up into blocks. Although there were a number
540 of ways this could've been implemented, this works and supports all of the
541 possible SOAP_IO flags, even though the entire message is still buffered
542 the same as if SOAP_IO_STORE was used.
544 static int
545 wininet_fsend(
546 struct soap * soap,
547 const char * a_pBuffer,
548 size_t a_uiBufferLen )
550 HINTERNET hHttpRequest = (HINTERNET) soap->socket;
551 BOOL bResult;
552 BOOL bRetryPost;
553 DWORD dwStatusCode;
554 DWORD dwStatusCodeLen;
555 int nResult = SOAP_OK;
556 struct wininet_data * pData =
557 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
559 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
560 "wininet %p: fsend, data len = %lu bytes\n", soap, a_uiBufferLen ));
562 /* allow the request to be sent with a NULL buffer */
563 if (a_uiBufferLen == 0)
565 pData->uiBufferLenMax = 0;
568 /* ensure that our connection hasn't been disconnected */
569 if ( !wininet_have_connection( soap, pData ) )
571 return SOAP_EOF;
574 /* initialize on our first time through. pData->pBuffer will always be
575 non-null if this is not the first call. */
576 if ( !pData->pBuffer )
579 If we are using chunked sending, then we don't know how big the
580 buffer will need to be. So we start with a 0 length buffer and
581 grow it later to ensure that it is always large enough.
583 uiBufferLenMax = length of the allocated memory
584 uiBufferLen = length of the data in the buffer
586 if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK )
588 /* we make the initial allocation large enough for this chunksize
589 buffer, plus the next chunk of actual data, and a few extra
590 bytes for the final "0" chunksize block. */
591 size_t uiChunkSize = strtoul( a_pBuffer, NULL, 16 );
592 pData->uiBufferLenMax = uiChunkSize + a_uiBufferLen + 16;
594 else if ( a_uiBufferLen == pData->uiBufferLenMax )
597 If the currently supplied buffer from gsoap holds the entire
598 message then we just use their buffer and avoid any memory
599 allocation. This will only be true when (1) we are not using
600 chunked send (so uiBufferLenMax has been previously set to
601 the Content-Length header length), and (2) gsoap is sending
602 the entire message at one time.
604 pData->pBuffer = (char *) a_pBuffer;
605 pData->uiBufferLen = a_uiBufferLen;
608 _ASSERTE( pData->uiBufferLenMax != INVALID_BUFFER_LENGTH );
612 If we can't use the gsoap buffer, then we need to allocate our own
613 buffer for the entire message. This is because authentication may
614 require the entire message to be sent multiple times. Since this send
615 is only a part of the message, we need to buffer until we have the
616 entire message.
618 if ( pData->pBuffer != a_pBuffer )
621 We already have a buffer pointer, this means that it isn't the
622 first time we have been called. We have allocated a buffer and
623 are current filling it.
625 If we don't have enough room in the our buffer to add this new
626 data, then we need to reallocate. This case will only occur with
627 chunked sends.
629 size_t uiNewBufferLen = pData->uiBufferLen + a_uiBufferLen;
630 if ( !pData->pBuffer || uiNewBufferLen > pData->uiBufferLenMax )
632 while ( uiNewBufferLen > pData->uiBufferLenMax )
634 pData->uiBufferLenMax = pData->uiBufferLenMax * 2;
636 char *pReallocatedBuffer = (char *) realloc( pData->pBuffer, pData->uiBufferLenMax );
637 if ( !pReallocatedBuffer )
639 return SOAP_EOM;
641 pData->pBuffer = pReallocatedBuffer;
643 memcpy( pData->pBuffer + pData->uiBufferLen,
644 a_pBuffer, a_uiBufferLen );
645 pData->uiBufferLen = uiNewBufferLen;
647 /* if we are doing chunked transfers, and this is a chunk size block,
648 and it is "0", then this is the last block in the transfer and we
649 can set the maximum size now to continue to the actual send. */
650 if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK
651 && pData->bIsChunkSize
652 && a_pBuffer[2] == '0' && !isalnum((unsigned char)a_pBuffer[3]) )
654 pData->uiBufferLenMax = pData->uiBufferLen;
658 /* if we haven't got the entire length of the message yet, then
659 we return to gsoap and let it continue */
660 if ( pData->uiBufferLen < pData->uiBufferLenMax )
662 /* toggle our chunk size marker if we are chunking */
663 pData->bIsChunkSize =
664 ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
665 && !pData->bIsChunkSize;
666 return SOAP_OK;
668 _ASSERTE( pData->uiBufferLen == pData->uiBufferLenMax );
670 /* we've now got the entire message, now we can enter our sending loop */
671 bRetryPost = TRUE;
672 while ( bRetryPost )
674 bRetryPost = FALSE;
676 soap->error = SOAP_OK;
678 /* to report a progress when sending a big buffer we need to send it by small
679 chunks since INTERNET_STATUS_REQUEST_SENT callback is sent for entire buffer */
680 const DWORD dwChunkSize = 64 * 1024;
681 if ( pData->pProgressCallback && (DWORD)pData->uiBufferLen > dwChunkSize )
683 INTERNET_BUFFERSA buffer;
684 memset(&buffer, 0, sizeof(buffer));
685 buffer.dwStructSize = sizeof(buffer);
686 buffer.lpvBuffer = pData->pBuffer;
687 buffer.dwBufferLength = min((DWORD)pData->uiBufferLen, dwChunkSize);
688 buffer.dwBufferTotal = (DWORD)pData->uiBufferLen;
689 Trace("HttpSendRequestExA", pData->pBuffer, buffer.dwBufferLength);
690 bResult = HttpSendRequestExA( hHttpRequest, &buffer, NULL, 0, 0 );
691 while ( bResult && buffer.dwBufferLength < buffer.dwBufferTotal )
693 DWORD bytesWritten = min(buffer.dwBufferTotal - buffer.dwBufferLength, dwChunkSize);
694 Trace("InternetWriteFile", pData->pBuffer + buffer.dwBufferLength, bytesWritten);
695 bResult = InternetWriteFile( hHttpRequest, pData->pBuffer + buffer.dwBufferLength, bytesWritten, &bytesWritten );
696 buffer.dwBufferLength += bytesWritten;
698 if ( bResult )
700 bResult = HttpEndRequestA( hHttpRequest, NULL, 0, 0 );
703 else
705 Trace("HttpSendRequestA", pData->pBuffer, (DWORD)pData->uiBufferLen);
707 bResult = HttpSendRequestA(
708 hHttpRequest, NULL, 0, pData->pBuffer, (DWORD)pData->uiBufferLen );
710 if ( !bResult )
712 soap->error = SOAP_EOF;
713 soap->errnum = GetLastError();
714 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
715 "wininet %p: fsend, error %d (%s) in HttpSendRequest\n",
716 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
718 /* see if we can handle this error, see the MSDN documentation
719 for InternetErrorDlg for details */
720 switch ( soap->errnum )
722 case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
723 case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR:
724 case ERROR_INTERNET_INCORRECT_PASSWORD:
725 case ERROR_INTERNET_INVALID_CA:
726 case ERROR_INTERNET_POST_IS_NON_SECURE:
727 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
728 case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
729 case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
731 wininet_rseReturn errorResolved = rseDisplayDlg;
732 if (pData->pRseCallback)
734 errorResolved = pData->pRseCallback(hHttpRequest, soap->errnum);
736 if (errorResolved == rseDisplayDlg)
738 errorResolved = (wininet_rseReturn)
739 wininet_resolve_send_error( hHttpRequest, soap->errnum );
740 if ( errorResolved == rseTrue )
742 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
743 "wininet %p: fsend, error %d has been resolved\n",
744 soap, soap->errnum ));
745 bRetryPost = TRUE;
748 we would have been disconnected by the error. Since we
749 are going to try again, we will automatically be
750 reconnected. Therefore we want to disregard any
751 previous disconnection messages.
753 pData->bDisconnect = FALSE;
754 continue;
758 break;
761 /* if the error wasn't handled then we exit */
762 switch ( soap->errnum )
764 case ERROR_INTERNET_NAME_NOT_RESOLVED:
765 case ERROR_INTERNET_CANNOT_CONNECT:
766 nResult = SOAP_TCP_ERROR;
767 break;
768 default:
769 nResult = SOAP_HTTP_ERROR;
770 break;
772 soap_set_sender_error(soap, "HttpSendRequest failed", wininet_error_message(soap,soap->errnum), nResult);
773 break;
776 /* get the status code from the response to determine if we need
777 to authorize */
778 dwStatusCodeLen = sizeof(dwStatusCode);
779 Trace("HttpQueryInfo - wininet_fsend", 0, 0);
780 bResult = HttpQueryInfo(
781 hHttpRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
782 &dwStatusCode, &dwStatusCodeLen, NULL);
784 char buf[1024];dwStatusCodeLen = sizeof(buf);
785 bResult = HttpQueryInfo(
786 hHttpRequest, HTTP_QUERY_STATUS_TEXT,
787 buf, &dwStatusCodeLen, NULL);
789 if ( !bResult )
791 soap->error = SOAP_EOF;
792 soap->errnum = GetLastError();
793 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
794 "wininet %p: fsend, error %d (%s) in HttpQueryInfo\n",
795 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
796 nResult = SOAP_HTTP_ERROR;
797 break;
800 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
801 "wininet %p: fsend, HTTP status code = %lu\n",
802 soap, dwStatusCode));
805 if we need authentication, then request the user for the
806 appropriate data. Their reply is saved into the request so
807 that we can use it later.
809 switch ( dwStatusCode )
811 case HTTP_STATUS_DENIED:
812 case HTTP_STATUS_PROXY_AUTH_REQ:
814 wininet_rseReturn errorResolved = rseDisplayDlg;
815 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
816 "wininet %p: fsend, user authenication required\n",
817 soap ));
818 if (pData->pRseCallback)
820 errorResolved = pData->pRseCallback(hHttpRequest, dwStatusCode);
822 if (errorResolved == rseDisplayDlg)
824 errorResolved = (wininet_rseReturn)
825 wininet_resolve_send_error( hHttpRequest, ERROR_INTERNET_INCORRECT_PASSWORD );
827 if ( errorResolved == rseTrue )
829 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
830 "wininet %p: fsend, authentication has been provided\n",
831 soap ));
834 we may have been disconnected by the error. Since we
835 are going to try again, we will automatically be
836 reconnected. Therefore we want to disregard any previous
837 disconnection messages.
839 pData->bDisconnect = FALSE;
840 bRetryPost = TRUE;
841 continue;
844 break;
848 /* if we have an allocated buffer then we can deallocate it now */
849 if ( pData->pBuffer != a_pBuffer )
851 free( pData->pBuffer );
854 pData->pBuffer = 0;
855 pData->uiBufferLen = 0;
856 pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
858 pData->pRespHeaders = 0;
860 return nResult;
863 /* gsoap documentation:
864 Called for all receive operations to fill buffer s of maximum length n.
865 Should return the number of bytes read or 0 in case of an error, e.g. EOF.
866 Built-in gSOAP function: frecv
868 static size_t
869 wininet_frecv(
870 struct soap * soap,
871 char * a_pBuffer,
872 size_t a_uiBufferLen )
874 HINTERNET hHttpRequest = (HINTERNET) soap->socket;
875 DWORD dwBytesRead = 0;
876 size_t uiTotalBytesRead = 0;
877 BOOL bResult;
879 struct wininet_data * pData =
880 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
882 if (!pData->pRespHeaders)
884 DWORD size = 0;
886 retry:
887 // This call will fail on the first pass, because no buffer is allocated.
888 if (!HttpQueryInfo(hHttpRequest, HTTP_QUERY_RAW_HEADERS_CRLF,
889 (LPVOID)pData->pRespHeaders, &size, NULL))
891 // Check for an insufficient buffer.
892 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
894 // Allocate the necessary buffer.
895 pData->pRespHeaders = (char*) malloc(size);
897 // Retry the call.
898 goto retry;
900 else
902 pData->pRespHeaders = (char*) malloc(0);
903 pData->uiRespHeadersLen = 0;
904 pData->uiRespHeadersReaded = 0;
905 Trace("HttpQueryInfo - no headers", 0, 0);
908 else
910 pData->uiRespHeadersLen = size;
911 pData->uiRespHeadersReaded = 0;
912 Trace("HttpQueryInfo - reading headers", pData->pRespHeaders, static_cast<DWORD>(pData->uiRespHeadersLen));
916 if (pData->pRespHeaders && pData->uiRespHeadersReaded < pData->uiRespHeadersLen)
918 size_t notReceivedPart = pData->uiRespHeadersLen - pData->uiRespHeadersReaded;
919 size_t recvSize = min(a_uiBufferLen, notReceivedPart);
921 memcpy(a_pBuffer, pData->pRespHeaders + pData->uiRespHeadersReaded, recvSize);
922 pData->uiRespHeadersReaded += recvSize;
923 if (a_uiBufferLen == recvSize)
924 return a_uiBufferLen;
925 uiTotalBytesRead = recvSize;
928 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
929 "wininet %p: frecv, available buffer len = %lu\n",
930 soap, a_uiBufferLen ));
933 NOTE: we do not check here that our connection hasn't been
934 disconnected because in HTTP/1.0 connections, it will always have been
935 disconnected by now. This is because the response is checked by the
936 wininet_fsend function to ensure that we didn't need any special
937 authentication. At that time the connection would have been
938 disconnected. This is okay however as we can still read the response
939 from the request handle.
944 /* read from the connection up to our maximum amount of data */
945 _ASSERTE( a_uiBufferLen <= ULONG_MAX );
946 bResult = InternetReadFile(
947 hHttpRequest,
948 &a_pBuffer[uiTotalBytesRead],
949 static_cast<DWORD>(a_uiBufferLen - uiTotalBytesRead),
950 &dwBytesRead );
951 Trace("InternetReadFile", &a_pBuffer[uiTotalBytesRead], dwBytesRead);
952 if ( bResult )
954 uiTotalBytesRead += dwBytesRead;
955 if ( dwBytesRead == 0 )
957 if (pData->pRespHeaders)
959 free(pData->pRespHeaders);
960 pData->pRespHeaders = 0;
962 pData->bDisconnect = TRUE;
965 else
967 soap->errnum = GetLastError();
968 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
969 "wininet %p: frecv, error %d (%s) in InternetReadFile\n",
970 soap, soap->errnum, wininet_error_message(soap,soap->errnum) ));
971 soap_set_sender_error(soap, "InternetReadFile failed", wininet_error_message(soap,soap->errnum), SOAP_HTTP_ERROR);
974 while ( bResult && dwBytesRead && uiTotalBytesRead < a_uiBufferLen );
976 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
977 "wininet %p: recv, received %lu bytes\n", soap, uiTotalBytesRead ));
979 return uiTotalBytesRead;
982 /* gsoap documentation:
983 Called by client proxy multiple times, to close a socket connection before
984 a new socket connection is established and at the end of communications
985 when the SOAP_IO_KEEPALIVE flag is not set and soap.keep_alive = 0
986 (indicating that the other party supports keep alive). Should return
987 SOAP_OK, or a gSOAP error code. Built-in gSOAP function: tcp_disconnect
989 static int
990 wininet_disconnect(
991 struct soap * soap )
993 struct wininet_data * pData =
994 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
996 DBGLOG(TEST, SOAP_MESSAGE(fdebug, "wininet %p: disconnect\n", soap ));
998 /* force a disconnect by setting the disconnect flag to TRUE */
999 pData->bDisconnect = TRUE;
1000 pData->bKeepAlive = FALSE;
1001 wininet_have_connection( soap, pData );
1003 return soap->error = SOAP_OK;
1006 /* this is mostly for debug tracing */
1007 void CALLBACK
1008 wininet_callback(
1009 HINTERNET hInternet,
1010 DWORD_PTR dwContext,
1011 DWORD dwInternetStatus,
1012 LPVOID lpvStatusInformation,
1013 DWORD dwStatusInformationLength )
1015 struct soap * soap = (struct soap *) dwContext;
1017 UNUSED_ARG( hInternet );
1018 UNUSED_ARG( lpvStatusInformation );
1019 UNUSED_ARG( dwStatusInformationLength );
1021 switch ( dwInternetStatus )
1023 case INTERNET_STATUS_RESOLVING_NAME:
1024 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1025 "wininet %p: INTERNET_STATUS_RESOLVING_NAME\n", soap ));
1026 break;
1027 case INTERNET_STATUS_NAME_RESOLVED:
1028 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1029 "wininet %p: INTERNET_STATUS_NAME_RESOLVED\n", soap ));
1030 break;
1031 case INTERNET_STATUS_CONNECTING_TO_SERVER:
1032 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1033 "wininet %p: INTERNET_STATUS_CONNECTING_TO_SERVER\n", soap));
1034 break;
1035 case INTERNET_STATUS_CONNECTED_TO_SERVER:
1036 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1037 "wininet %p: INTERNET_STATUS_CONNECTED_TO_SERVER\n", soap));
1038 break;
1039 case INTERNET_STATUS_SENDING_REQUEST:
1040 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1041 "wininet %p: INTERNET_STATUS_SENDING_REQUEST\n", soap));
1042 break;
1043 case INTERNET_STATUS_REQUEST_SENT:
1045 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1046 "wininet %p: INTERNET_STATUS_REQUEST_SENT, bytes sent = %lu\n",
1047 soap, *(DWORD *)lpvStatusInformation ));
1048 struct wininet_data * pData =
1049 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
1050 if ( pData->pProgressCallback )
1052 pData->pProgressCallback( TRUE, *(DWORD *)lpvStatusInformation, pData->pProgressCallbackContext );
1055 break;
1056 case INTERNET_STATUS_RECEIVING_RESPONSE:
1057 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1058 "wininet %p: INTERNET_STATUS_RECEIVING_RESPONSE\n", soap));
1059 break;
1060 case INTERNET_STATUS_RESPONSE_RECEIVED:
1062 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1063 "wininet %p: INTERNET_STATUS_RESPONSE_RECEIVED, bytes received = %lu\n",
1064 soap, *(DWORD *)lpvStatusInformation ));
1065 struct wininet_data * pData =
1066 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
1067 if ( pData->pProgressCallback )
1069 pData->pProgressCallback( FALSE, *(DWORD *)lpvStatusInformation, pData->pProgressCallbackContext );
1072 break;
1073 case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
1074 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1075 "wininet %p: INTERNET_STATUS_CTL_RESPONSE_RECEIVED\n", soap));
1076 break;
1077 case INTERNET_STATUS_PREFETCH:
1078 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1079 "wininet %p: INTERNET_STATUS_PREFETCH\n", soap));
1080 break;
1081 case INTERNET_STATUS_CLOSING_CONNECTION:
1082 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1083 "wininet %p: INTERNET_STATUS_CLOSING_CONNECTION\n", soap));
1084 break;
1085 case INTERNET_STATUS_CONNECTION_CLOSED:
1086 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1087 "wininet %p: INTERNET_STATUS_CONNECTION_CLOSED\n", soap));
1089 /* the connection has been closed, so we close the handle here */
1090 struct wininet_data * pData =
1091 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
1092 if ( pData->hConnection )
1095 we only mark this for disconnection otherwise we get
1096 errors when reading the data from the handle. In every
1097 function that we use the connection we will check first to
1098 see if it has been disconnected.
1100 pData->bDisconnect = TRUE;
1103 break;
1104 case INTERNET_STATUS_HANDLE_CREATED:
1105 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1106 "wininet %p: INTERNET_STATUS_HANDLE_CREATED\n", soap));
1107 break;
1108 case INTERNET_STATUS_HANDLE_CLOSING:
1109 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1110 "wininet %p: INTERNET_STATUS_HANDLE_CLOSING\n", soap));
1111 break;
1112 #ifdef INTERNET_STATUS_DETECTING_PROXY
1113 case INTERNET_STATUS_DETECTING_PROXY:
1114 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1115 "wininet %p: INTERNET_STATUS_DETECTING_PROXY\n", soap));
1116 break;
1117 #endif
1118 case INTERNET_STATUS_REQUEST_COMPLETE:
1119 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1120 "wininet %p: INTERNET_STATUS_REQUEST_COMPLETE\n", soap));
1121 break;
1122 case INTERNET_STATUS_REDIRECT:
1123 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1124 "wininet %p: INTERNET_STATUS_REDIRECT, new url = %s\n",
1125 soap, (char*) lpvStatusInformation ));
1126 break;
1127 case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
1128 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1129 "wininet %p: INTERNET_STATUS_INTERMEDIATE_RESPONSE\n", soap));
1130 break;
1131 #ifdef INTERNET_STATUS_USER_INPUT_REQUIRED
1132 case INTERNET_STATUS_USER_INPUT_REQUIRED:
1133 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1134 "wininet %p: INTERNET_STATUS_USER_INPUT_REQUIRED\n", soap));
1135 break;
1136 #endif
1137 case INTERNET_STATUS_STATE_CHANGE:
1138 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1139 "wininet %p: INTERNET_STATUS_STATE_CHANGE\n", soap));
1140 break;
1141 #ifdef INTERNET_STATUS_COOKIE_SENT
1142 case INTERNET_STATUS_COOKIE_SENT:
1143 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1144 "wininet %p: INTERNET_STATUS_COOKIE_SENT\n", soap));
1145 break;
1146 #endif
1147 #ifdef INTERNET_STATUS_COOKIE_RECEIVED
1148 case INTERNET_STATUS_COOKIE_RECEIVED:
1149 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1150 "wininet %p: INTERNET_STATUS_COOKIE_RECEIVED\n", soap));
1151 break;
1152 #endif
1153 #ifdef INTERNET_STATUS_PRIVACY_IMPACTED
1154 case INTERNET_STATUS_PRIVACY_IMPACTED:
1155 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1156 "wininet %p: INTERNET_STATUS_PRIVACY_IMPACTED\n", soap));
1157 break;
1158 #endif
1159 #ifdef INTERNET_STATUS_P3P_HEADER
1160 case INTERNET_STATUS_P3P_HEADER:
1161 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1162 "wininet %p: INTERNET_STATUS_P3P_HEADER\n", soap));
1163 break;
1164 #endif
1165 #ifdef INTERNET_STATUS_P3P_POLICYREF
1166 case INTERNET_STATUS_P3P_POLICYREF:
1167 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1168 "wininet %p: INTERNET_STATUS_P3P_POLICYREF\n", soap));
1169 break;
1170 #endif
1171 #ifdef INTERNET_STATUS_COOKIE_HISTORY
1172 case INTERNET_STATUS_COOKIE_HISTORY:
1173 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1174 "wininet %p: INTERNET_STATUS_COOKIE_HISTORY\n", soap));
1175 break;
1176 #endif
1181 check to ensure that our connection hasn't been disconnected
1182 and disconnect remaining handles if necessary.
1184 static BOOL
1185 wininet_have_connection(
1186 struct soap * soap,
1187 struct wininet_data * a_pData )
1189 /* close the http request if we don't have a connection */
1190 BOOL bCloseRequest = a_pData->bDisconnect || !a_pData->hConnection;
1191 if ( bCloseRequest && soap->socket != SOAP_INVALID_SOCKET )
1193 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1194 "wininet %p: closing request\n", soap));
1196 Trace("InternetCloseHandle (closing request)", 0, 0);
1197 InternetCloseHandle( (HINTERNET) soap->socket );
1198 soap->socket = SOAP_INVALID_SOCKET;
1200 #if 0
1201 if ( a_pData->bKeepAlive )
1203 DWORD dwFlags;
1204 HINTERNET hHttpRequest = NULL;
1205 dwFlags = a_pData->dwRequestFlags | INTERNET_FLAG_NO_CACHE_WRITE;
1206 if ( soap->omode & SOAP_IO_KEEPALIVE )
1208 dwFlags |= INTERNET_FLAG_KEEP_CONNECTION;
1209 a_pData->bKeepAlive = TRUE;
1211 /*if ( urlComponents.nScheme == INTERNET_SCHEME_HTTPS )
1213 dwFlags |= INTERNET_FLAG_SECURE;
1216 hHttpRequest = HttpOpenRequestA(
1217 a_pData->hConnection, "POST", "/MyWebService/Service.asmx"/*szUrlPath*/, "HTTP/1.1", NULL, NULL,
1218 dwFlags, (DWORD_PTR) soap );
1219 if ( hHttpRequest )
1220 soap->socket = (SOAP_SOCKET) hHttpRequest;
1221 else
1222 a_pData->bKeepAlive = FALSE;
1224 #endif
1227 /* close the connection if we don't have a request */
1228 if ( soap->socket == SOAP_INVALID_SOCKET && a_pData->hConnection && !a_pData->bKeepAlive )
1230 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1231 "wininet %p: closing connection\n", soap));
1233 Trace("InternetCloseHandle (closing connection)", 0, 0);
1234 InternetCloseHandle( a_pData->hConnection );
1235 a_pData->hConnection = NULL;
1237 a_pData->bDisconnect = FALSE;
1239 /* clean up the send details if we don't have a request */
1240 if ( soap->socket == SOAP_INVALID_SOCKET )
1242 if ( a_pData->pBuffer )
1244 free( a_pData->pBuffer );
1245 a_pData->pBuffer = 0;
1247 a_pData->uiBufferLen = 0;
1248 a_pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
1251 return (soap->socket != SOAP_INVALID_SOCKET);
1254 static int
1255 wininet_fpoll(
1256 struct soap * soap)
1258 struct wininet_data * pData =
1259 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
1260 return wininet_have_connection(soap, pData) ? SOAP_OK : SOAP_EOF;
1263 static DWORD
1264 wininet_set_timeout(
1265 struct soap * soap,
1266 struct wininet_data * a_pData,
1267 const char * a_pszTimeout,
1268 DWORD a_dwOption,
1269 int a_nTimeout )
1271 UNUSED_ARG( soap );
1272 UNUSED_ARG( a_pszTimeout );
1274 if ( a_nTimeout > 0 )
1276 DWORD dwTimeout = a_nTimeout * 1000;
1277 if ( !InternetSetOption( a_pData->hInternet,
1278 a_dwOption, &dwTimeout, sizeof(DWORD) ) )
1280 DWORD dwErrorCode = GetLastError();
1281 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1282 "wininet %p: failed to set %s timeout, error %d (%s)\n",
1283 soap, a_pszTimeout, dwErrorCode,
1284 wininet_error_message(soap,dwErrorCode) ));
1285 return dwErrorCode;
1288 return 0;
1291 #if 0
1292 static BOOL
1293 wininet_flag_set_option(
1294 HINTERNET a_hHttpRequest,
1295 DWORD a_dwOption,
1296 DWORD a_dwFlag )
1298 DWORD dwBuffer;
1299 DWORD dwBufferLength = sizeof(DWORD);
1301 BOOL bSuccess = InternetQueryOption(
1302 a_hHttpRequest,
1303 a_dwOption,
1304 &dwBuffer,
1305 &dwBufferLength );
1306 if (bSuccess) {
1307 dwBuffer |= a_dwFlag;
1308 bSuccess = InternetSetOption(
1309 a_hHttpRequest,
1310 a_dwOption,
1311 &dwBuffer,
1312 dwBufferLength);
1314 #ifdef SOAP_DEBUG
1315 if ( !bSuccess ) {
1316 DWORD dwErrorCode = GetLastError();
1317 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1318 "wininet %p: failed to set option: %X, error %d (%s)\n",
1319 a_hHttpRequest, a_dwOption, dwErrorCode,
1320 wininet_error_message(soap,dwErrorCode) ));
1322 #endif
1323 return bSuccess;
1325 #endif
1327 static BOOL
1328 wininet_resolve_send_error(
1329 HINTERNET a_hHttpRequest,
1330 DWORD a_dwErrorCode )
1332 DWORD dwResult = InternetErrorDlg(
1333 GetDesktopWindow(),
1334 a_hHttpRequest,
1335 a_dwErrorCode,
1336 FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
1337 FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
1338 FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
1339 NULL );
1340 BOOL bRetVal = (a_dwErrorCode == ERROR_INTERNET_INCORRECT_PASSWORD) ?
1341 (dwResult == ERROR_INTERNET_FORCE_RETRY) :
1342 (dwResult == ERROR_SUCCESS);
1343 /* If appropriate for your application, it is possible to ignore
1344 errors in future once they have been handled or ignored once.
1345 For example, to ignore invalid SSL certificate dates on this
1346 connection once the client has indicated to ignore them this
1347 time, uncomment this code.
1350 if (bRetVal)
1352 switch (a_dwErrorCode)
1354 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
1355 wininet_flag_set_option(a_hHttpRequest,
1356 INTERNET_OPTION_SECURITY_FLAGS,
1357 SECURITY_FLAG_IGNORE_CERT_CN_INVALID );
1358 break;
1362 return bRetVal;
1366 static const char *
1367 wininet_error_message(
1368 struct soap * soap,
1369 DWORD a_dwErrorMsgId )
1371 HINSTANCE hModule;
1372 DWORD dwResult;
1373 DWORD dwFormatFlags;
1374 struct wininet_data * pData =
1375 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
1377 /* free any existing error message */
1378 wininet_free_error_message( pData );
1380 dwFormatFlags =
1381 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1382 FORMAT_MESSAGE_IGNORE_INSERTS |
1383 FORMAT_MESSAGE_FROM_SYSTEM;
1385 /* load wininet.dll for the error messages */
1386 hModule = LoadLibraryExA( "wininet.dll", NULL,
1387 LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES );
1388 if ( hModule )
1390 dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
1393 /* format the messages */
1394 wchar_t* pszErrorMessage = NULL;
1395 dwResult = FormatMessageW(
1396 dwFormatFlags,
1397 hModule,
1398 a_dwErrorMsgId,
1399 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1400 (LPWSTR) &pszErrorMessage,
1402 NULL );
1404 /* remove the CR LF from the error message and convert to proper char set */
1405 if ( dwResult > 2 )
1407 dwResult -= 2;
1408 pszErrorMessage[dwResult] = 0;
1410 wchar_t* pszExtendedErrorMessage = (wchar_t*)_alloca(dwResult + 100);
1411 swprintf(pszExtendedErrorMessage, L"WinInet %d, %ls", a_dwErrorMsgId, pszErrorMessage);
1413 UINT newCodePage = CP_UTF8; // TODO: May be it should use SOAP_C_UTFSTRING or SOAP_C_MBSTRING?
1414 int len = WideCharToMultiByte(newCodePage, 0, pszExtendedErrorMessage, -1, NULL, 0, NULL, NULL);
1415 if (len)
1417 pData->pszErrorMessage = new char[len];
1418 WideCharToMultiByte(newCodePage, 0, pszExtendedErrorMessage, -1, pData->pszErrorMessage, len, NULL, NULL);
1420 else
1422 pData->pszErrorMessage = NULL;
1424 LocalFree( pszErrorMessage );
1426 else
1428 pData->pszErrorMessage = NULL;
1431 /* free the library if we loaded it */
1432 if ( hModule )
1434 FreeLibrary( hModule );
1437 if ( pData->pszErrorMessage )
1439 return pData->pszErrorMessage;
1441 else
1443 const static char szUnknown[] = "(unknown)";
1444 return szUnknown;
1448 static void
1449 wininet_free_error_message(
1450 struct wininet_data * a_pData )
1452 if ( a_pData->pszErrorMessage )
1454 delete[] a_pData->pszErrorMessage;
1455 a_pData->pszErrorMessage = 0;