Optimize length check
[TortoiseGit.git] / ext / CrashServer / CrashHandler / DumpUploaderServiceLib / gsoapWinInet-2.8.0.cpp
blobb47d6def30449d809c565c3c144c1616895a6a8f
1 /*
2 See the header file for details. This file is distributed under the MIT licence.
3 */
5 /* system */
6 #include <windows.h>
7 #include <crtdbg.h>
8 #include <wininet.h>
10 /* gsoap */
11 #include <stdsoap2.h>
13 /* local */
14 #include "gsoapWinInet.h"
16 /* ensure that the wininet library is linked */
17 #pragma comment( lib, "wininet.lib" )
18 /* disable deprecation warnings */
19 #pragma warning(disable : 4996)
21 #define UNUSED_ARG(x) (x)
22 #define INVALID_BUFFER_LENGTH ((DWORD)-1)
24 /* plugin id */
25 static const char wininet_id[] = "wininet-2.0";
27 /* plugin private data */
28 struct wininet_data
30 HINTERNET hInternet; /* internet session handle */
31 HINTERNET hConnection; /* current connection handle */
32 BOOL bDisconnect; /* connection is disconnected */
33 DWORD dwRequestFlags; /* extra request flags from user */
34 char * pBuffer; /* send buffer */
35 size_t uiBufferLenMax; /* total length of the message */
36 size_t uiBufferLen; /* length of data in buffer */
37 BOOL bIsChunkSize; /* expecting a chunk size buffer */
38 wininet_rse_callback pRseCallback; /* wininet_resolve_send_error callback. Allows clients to resolve ssl errors programatically */
39 #ifdef SOAP_DEBUG
40 /* this is only used for DBGLOG output */
41 char * pszErrorMessage; /* wininet/system error message */
42 #endif
45 /* forward declarations */
46 static BOOL
47 wininet_init(
48 struct soap * soap,
49 struct wininet_data * a_pData,
50 DWORD a_dwRequestFlags );
51 static int
52 wininet_copy(
53 struct soap * soap,
54 struct soap_plugin * a_pDst,
55 struct soap_plugin * a_pSrc );
56 static void
57 wininet_delete(
58 struct soap * soap,
59 struct soap_plugin * a_pPluginData );
60 static SOAP_SOCKET
61 wininet_connect(
62 struct soap * soap,
63 const char * a_pszEndpoint,
64 const char * a_pszHost,
65 int a_nPort );
66 static int
67 wininet_post_header(
68 struct soap * soap,
69 const char * a_pszKey,
70 const char * a_pszValue );
71 static int
72 wininet_fsend(
73 struct soap * soap,
74 const char * a_pBuffer,
75 size_t a_uiBufferLen );
76 static size_t
77 wininet_frecv(
78 struct soap * soap,
79 char * a_pBuffer,
80 size_t a_uiBufferLen );
81 static int
82 wininet_disconnect(
83 struct soap * soap );
84 void CALLBACK
85 wininet_callback(
86 HINTERNET hInternet,
87 DWORD_PTR dwContext,
88 DWORD dwInternetStatus,
89 LPVOID lpvStatusInformation,
90 DWORD dwStatusInformationLength );
91 static BOOL
92 wininet_have_connection(
93 struct soap * soap,
94 struct wininet_data * a_pData );
95 static DWORD
96 wininet_set_timeout(
97 struct soap * soap,
98 struct wininet_data * a_pData,
99 const char * a_pszTimeout,
100 DWORD a_dwOption,
101 int a_nTimeout );
102 static BOOL
103 wininet_resolve_send_error(
104 HINTERNET a_hHttpRequest,
105 DWORD a_dwErrorCode );
107 #ifdef SOAP_DEBUG
108 /* this is only used for DBGLOG output */
109 static const char *
110 wininet_error_message(
111 struct soap * a_pData,
112 DWORD a_dwErrorMsgId );
113 static void
114 wininet_free_error_message(
115 struct wininet_data * a_pData );
116 #else
117 #define wininet_free_error_message(x)
118 #endif
120 /* plugin registration */
122 wininet_plugin(
123 struct soap * soap,
124 struct soap_plugin * a_pPluginData,
125 void * a_dwRequestFlags )
127 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
128 "wininet %p: plugin registration\n", soap ));
130 a_pPluginData->id = wininet_id;
131 a_pPluginData->fcopy = wininet_copy;
132 a_pPluginData->fdelete = wininet_delete;
133 a_pPluginData->data = (void*) malloc( sizeof(struct wininet_data) );
134 if ( !a_pPluginData->data )
136 return SOAP_EOM;
138 if ( !wininet_init( soap,
139 (struct wininet_data *) a_pPluginData->data,
140 (DWORD) (size_t) a_dwRequestFlags ) )
142 free( a_pPluginData->data );
143 return SOAP_EOM;
146 #ifdef SOAP_DEBUG
147 if ( (soap->omode & SOAP_IO) == SOAP_IO_STORE )
149 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
150 "wininet %p: use of SOAP_IO_STORE is not recommended\n", soap ));
152 #endif
154 return SOAP_OK;
157 /* initialize private data */
158 static BOOL
159 wininet_init(
160 struct soap * soap,
161 struct wininet_data * a_pData,
162 DWORD a_dwRequestFlags )
164 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
165 "wininet %p: init private data\n", soap ));
167 memset( a_pData, 0, sizeof(struct wininet_data) );
168 a_pData->dwRequestFlags = a_dwRequestFlags;
170 /* start our internet session */
171 a_pData->hInternet = InternetOpenA(
172 "gSOAP", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
173 if ( !a_pData->hInternet )
175 soap->error = GetLastError();
176 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
177 "wininet %p: init, error %d (%s) in InternetOpen\n",
178 soap, soap->error, wininet_error_message(soap,soap->error) ));
179 wininet_free_error_message( a_pData );
180 return FALSE;
183 /* set the timeouts, if any of these fail the error isn't fatal */
184 wininet_set_timeout( soap, a_pData, "connect",
185 INTERNET_OPTION_CONNECT_TIMEOUT, soap->connect_timeout );
186 wininet_set_timeout( soap, a_pData, "receive",
187 INTERNET_OPTION_RECEIVE_TIMEOUT, soap->recv_timeout );
188 wininet_set_timeout( soap, a_pData, "send",
189 INTERNET_OPTION_SEND_TIMEOUT, soap->send_timeout );
191 /* set up the callback function so we get notifications */
192 InternetSetStatusCallback( a_pData->hInternet, wininet_callback );
194 /* set all of our callbacks */
195 soap->fopen = wininet_connect;
196 soap->fposthdr = wininet_post_header;
197 soap->fsend = wininet_fsend;
198 soap->frecv = wininet_frecv;
199 soap->fclose = wininet_disconnect;
201 return TRUE;
204 void
205 wininet_set_rse_callback(
206 struct soap * soap,
207 wininet_rse_callback a_pRsecallback)
209 struct wininet_data * pData = (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
211 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
212 "wininet %p: resolve_send_error callback = '%p'\n", soap, a_pRsecallback ));
214 pData->pRseCallback = a_pRsecallback;
218 /* copy the private data structure */
219 static int
220 wininet_copy(
221 struct soap * soap,
222 struct soap_plugin * a_pDst,
223 struct soap_plugin * a_pSrc )
225 UNUSED_ARG( soap );
226 UNUSED_ARG( a_pDst );
227 UNUSED_ARG( a_pSrc );
229 _ASSERTE( !"wininet doesn't support copy" );
230 return SOAP_FATAL_ERROR;
233 /* deallocate of our private structure */
234 static void
235 wininet_delete(
236 struct soap * soap,
237 struct soap_plugin * a_pPluginData )
239 struct wininet_data * pData =
240 (struct wininet_data *) a_pPluginData->data;
242 UNUSED_ARG( soap );
244 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
245 "wininet %p: delete private data\n", soap ));
247 /* force a disconnect of any existing connection */
248 pData->bDisconnect = TRUE;
249 wininet_have_connection( soap, pData );
251 /* close down the internet */
252 if ( pData->hInternet )
254 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
255 "wininet %p: closing internet handle\n", soap));
256 InternetCloseHandle( pData->hInternet );
257 pData->hInternet = NULL;
260 /* free our data */
261 wininet_free_error_message( pData );
262 free( a_pPluginData->data );
265 /* gsoap documentation:
266 Called from a client proxy to open a connection to a Web Service located
267 at endpoint. Input parameters host and port are micro-parsed from endpoint.
268 Should return a valid file descriptor, or SOAP_INVALID_SOCKET and
269 soap->error set to an error code. Built-in gSOAP function: tcp_connect
271 static SOAP_SOCKET
272 wininet_connect(
273 struct soap * soap,
274 const char * a_pszEndpoint,
275 const char * a_pszHost,
276 int a_nPort )
278 URL_COMPONENTSA urlComponents;
279 char szUrlPath[MAX_PATH];
280 char szHost[MAX_PATH];
281 DWORD dwFlags;
282 HINTERNET hConnection = NULL;
283 HINTERNET hHttpRequest = NULL;
284 struct wininet_data * pData =
285 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
287 soap->error = SOAP_OK;
289 /* we parse the URL ourselves so we don't use these parameters */
290 UNUSED_ARG( a_pszHost );
291 UNUSED_ARG( a_nPort );
293 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
294 "wininet %p: connect, endpoint = '%s'\n", soap, a_pszEndpoint ));
296 /* we should be initialized but not connected */
297 _ASSERTE( pData->hInternet );
298 _ASSERTE( !pData->hConnection );
299 _ASSERTE( soap->socket == SOAP_INVALID_SOCKET );
301 /* parse out the url path */
302 memset( &urlComponents, 0, sizeof(urlComponents) );
303 urlComponents.dwStructSize = sizeof(urlComponents);
304 urlComponents.lpszHostName = szHost;
305 urlComponents.dwHostNameLength = MAX_PATH;
306 urlComponents.lpszUrlPath = szUrlPath;
307 urlComponents.dwUrlPathLength = MAX_PATH;
308 if ( !InternetCrackUrlA( a_pszEndpoint, 0, 0, &urlComponents ) )
310 InternetCloseHandle( hConnection );
311 soap->error = GetLastError();
312 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
313 "wininet %p: connect, error %d (%s) in InternetCrackUrl\n",
314 soap, soap->error, wininet_error_message(soap,soap->error) ));
315 return SOAP_INVALID_SOCKET;
318 /* connect to the target url, if we haven't connected yet
319 or if it was dropped */
320 hConnection = InternetConnectA( pData->hInternet,
321 szHost, urlComponents.nPort, "", "", INTERNET_SERVICE_HTTP,
322 0, (DWORD_PTR) soap );
323 if ( !hConnection )
325 soap->error = GetLastError();
326 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
327 "wininet %p: connect, error %d (%s) in InternetConnect\n",
328 soap, soap->error, wininet_error_message(soap,soap->error) ));
329 return SOAP_INVALID_SOCKET;
333 Note that although we specify HTTP/1.1 for the connection here, the
334 actual connection may be HTTP/1.0 depending on the settings in the
335 control panel. See the "Internet Options", "HTTP 1.1 settings".
337 dwFlags = pData->dwRequestFlags;
338 if ( soap->omode & SOAP_IO_KEEPALIVE )
340 dwFlags |= INTERNET_FLAG_KEEP_CONNECTION;
342 if ( urlComponents.nScheme == INTERNET_SCHEME_HTTPS )
344 dwFlags |= INTERNET_FLAG_SECURE;
346 hHttpRequest = HttpOpenRequestA(
347 hConnection, "POST", szUrlPath, "HTTP/1.1", NULL, NULL,
348 dwFlags, (DWORD_PTR) soap );
349 if ( !hHttpRequest )
351 InternetCloseHandle( hConnection );
352 soap->error = GetLastError();
353 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
354 "wininet %p: connect, error %d (%s) in HttpOpenRequest\n",
355 soap, soap->error, wininet_error_message(soap,soap->error) ));
356 return SOAP_INVALID_SOCKET;
359 /* save the connection handle in our data structure */
360 pData->hConnection = hConnection;
362 /* return the http request handle as our file descriptor. */
363 _ASSERTE( sizeof(soap->socket) >= sizeof(HINTERNET) );
364 return (SOAP_SOCKET) hHttpRequest;
367 /* gsoap documentation:
368 Called by http_post and http_response (through the callbacks). Emits HTTP
369 key: val header entries. Should return SOAP_OK, or a gSOAP error code.
370 Built-in gSOAP function: http_post_header.
372 static int
373 wininet_post_header(
374 struct soap * soap,
375 const char * a_pszKey,
376 const char * a_pszValue )
378 HINTERNET hHttpRequest = (HINTERNET) soap->socket;
379 char szHeader[4096];
380 int nLen;
381 BOOL bResult = FALSE;
382 struct wininet_data * pData =
383 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
385 soap->error = SOAP_OK;
387 /* ensure that our connection hasn't been disconnected */
388 if ( !wininet_have_connection( soap, pData ) )
390 return SOAP_EOF;
393 /* if this is the initial POST header then we initialize our send buffer */
394 if ( a_pszKey && !a_pszValue )
396 _ASSERTE( !pData->pBuffer );
397 pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
398 pData->uiBufferLen = 0;
400 /* if we are using chunk output then we start with a chunk size */
401 pData->bIsChunkSize = ( (soap->omode & SOAP_IO) == SOAP_IO_CHUNK );
403 else if ( a_pszValue )
405 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
406 "wininet %p: post_header, adding '%s: %s'\n",
407 soap, a_pszKey, a_pszValue ));
409 /* determine the maximum length of this message so that we can
410 correctly determine when we have completed the send */
411 if ( !strcmp( a_pszKey, "Content-Length" ) )
413 _ASSERTE( pData->uiBufferLenMax == INVALID_BUFFER_LENGTH );
414 pData->uiBufferLenMax = strtoul( a_pszValue, NULL, 10 );
417 nLen = _snprintf(
418 szHeader, 4096, "%s: %s\r\n", a_pszKey, a_pszValue );
419 if ( nLen < 0 )
421 return SOAP_EOM;
423 bResult = HttpAddRequestHeadersA( hHttpRequest, szHeader, nLen,
424 HTTP_ADDREQ_FLAG_ADD_IF_NEW );
425 #ifdef SOAP_DEBUG
427 we don't return an error if this fails because it isn't
428 (or shouldn't be) critical.
430 if ( !bResult )
432 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
433 "wininet %p: post_header, error %d (%s) in HttpAddRequestHeaders\n",
434 soap, soap->error, wininet_error_message(soap,GetLastError()) ));
436 #endif
438 return SOAP_OK;
441 /* gsoap documentation:
442 Called for all send operations to emit contents of s of length n.
443 Should return SOAP_OK, or a gSOAP error code. Built-in gSOAP
444 function: fsend
446 Notes:
447 I do a heap of buffering here because we need the entire message available
448 in a single buffer in order to iterate through the sending loop. I had
449 hoped that the SOAP_IO_STORE flag would have worked to do the same, however
450 this still breaks the messages up into blocks. Although there were a number
451 of ways this could've been implemented, this works and supports all of the
452 possible SOAP_IO flags, even though the entire message is still buffered
453 the same as if SOAP_IO_STORE was used.
455 static int
456 wininet_fsend(
457 struct soap * soap,
458 const char * a_pBuffer,
459 size_t a_uiBufferLen )
461 HINTERNET hHttpRequest = (HINTERNET) soap->socket;
462 BOOL bResult;
463 BOOL bRetryPost;
464 DWORD dwStatusCode;
465 DWORD dwStatusCodeLen;
466 int nResult = SOAP_OK;
467 struct wininet_data * pData =
468 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
470 soap->error = SOAP_OK;
472 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
473 "wininet %p: fsend, data len = %lu bytes\n", soap, a_uiBufferLen ));
475 /* allow the request to be sent with a NULL buffer */
476 if (a_uiBufferLen == 0)
478 pData->uiBufferLenMax = 0;
481 /* ensure that our connection hasn't been disconnected */
482 if ( !wininet_have_connection( soap, pData ) )
484 return SOAP_EOF;
487 /* initialize on our first time through. pData->pBuffer will always be
488 non-null if this is not the first call. */
489 if ( !pData->pBuffer )
492 If we are using chunked sending, then we don't know how big the
493 buffer will need to be. So we start with a 0 length buffer and
494 grow it later to ensure that it is always large enough.
496 uiBufferLenMax = length of the allocated memory
497 uiBufferLen = length of the data in the buffer
499 if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK )
501 /* we make the initial allocation large enough for this chunksize
502 buffer, plus the next chunk of actual data, and a few extra
503 bytes for the final "0" chunksize block. */
504 size_t uiChunkSize = strtoul( a_pBuffer, NULL, 16 );
505 pData->uiBufferLenMax = uiChunkSize + a_uiBufferLen + 16;
507 else if ( a_uiBufferLen == pData->uiBufferLenMax )
510 If the currently supplied buffer from gsoap holds the entire
511 message then we just use their buffer and avoid any memory
512 allocation. This will only be true when (1) we are not using
513 chunked send (so uiBufferLenMax has been previously set to
514 the Content-Length header length), and (2) gsoap is sending
515 the entire message at one time.
517 pData->pBuffer = (char *) a_pBuffer;
518 pData->uiBufferLen = a_uiBufferLen;
521 _ASSERTE( pData->uiBufferLenMax != INVALID_BUFFER_LENGTH );
525 If we can't use the gsoap buffer, then we need to allocate our own
526 buffer for the entire message. This is because authentication may
527 require the entire message to be sent multiple times. Since this send
528 is only a part of the message, we need to buffer until we have the
529 entire message.
531 if ( pData->pBuffer != a_pBuffer )
534 We already have a buffer pointer, this means that it isn't the
535 first time we have been called. We have allocated a buffer and
536 are current filling it.
538 If we don't have enough room in the our buffer to add this new
539 data, then we need to reallocate. This case will only occur with
540 chunked sends.
542 size_t uiNewBufferLen = pData->uiBufferLen + a_uiBufferLen;
543 if ( !pData->pBuffer || uiNewBufferLen > pData->uiBufferLenMax )
545 while ( uiNewBufferLen > pData->uiBufferLenMax )
547 pData->uiBufferLenMax = pData->uiBufferLenMax * 2;
549 pData->pBuffer = (char *) realloc( pData->pBuffer, pData->uiBufferLenMax );
550 if ( !pData->pBuffer )
552 return SOAP_EOM;
555 memcpy( pData->pBuffer + pData->uiBufferLen,
556 a_pBuffer, a_uiBufferLen );
557 pData->uiBufferLen = uiNewBufferLen;
559 /* if we are doing chunked transfers, and this is a chunk size block,
560 and it is "0", then this is the last block in the transfer and we
561 can set the maximum size now to continue to the actual send. */
562 if ( (soap->mode & SOAP_IO) == SOAP_IO_CHUNK
563 && pData->bIsChunkSize
564 && a_pBuffer[2] == '0' && !isalnum(a_pBuffer[3]) )
566 pData->uiBufferLenMax = pData->uiBufferLen;
570 /* if we haven't got the entire length of the message yet, then
571 we return to gsoap and let it continue */
572 if ( pData->uiBufferLen < pData->uiBufferLenMax )
574 /* toggle our chunk size marker if we are chunking */
575 pData->bIsChunkSize =
576 ((soap->mode & SOAP_IO) == SOAP_IO_CHUNK)
577 && !pData->bIsChunkSize;
578 return SOAP_OK;
580 _ASSERTE( pData->uiBufferLen == pData->uiBufferLenMax );
582 /* we've now got the entire message, now we can enter our sending loop */
583 bRetryPost = TRUE;
584 while ( bRetryPost )
586 bRetryPost = FALSE;
588 bResult = HttpSendRequestA(
589 hHttpRequest, NULL, 0, pData->pBuffer, (DWORD)pData->uiBufferLen );
590 if ( !bResult )
592 soap->error = GetLastError();
593 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
594 "wininet %p: fsend, error %d (%s) in HttpSendRequest\n",
595 soap, soap->error, wininet_error_message(soap,soap->error) ));
597 /* see if we can handle this error, see the MSDN documentation
598 for InternetErrorDlg for details */
599 switch ( soap->error )
601 case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
602 case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR:
603 case ERROR_INTERNET_INCORRECT_PASSWORD:
604 case ERROR_INTERNET_INVALID_CA:
605 case ERROR_INTERNET_POST_IS_NON_SECURE:
606 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
607 case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
608 case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
610 wininet_rseReturn errorResolved = rseDisplayDlg;
611 if (pData->pRseCallback)
613 errorResolved = pData->pRseCallback(hHttpRequest, soap->error);
615 if (errorResolved == rseDisplayDlg)
617 errorResolved = (wininet_rseReturn)
618 wininet_resolve_send_error( hHttpRequest, soap->error );
619 if ( errorResolved == rseTrue )
621 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
622 "wininet %p: fsend, error %d has been resolved\n",
623 soap, soap->error ));
624 bRetryPost = TRUE;
627 we would have been disconnected by the error. Since we
628 are going to try again, we will automatically be
629 reconnected. Therefore we want to disregard any
630 previous disconnection messages.
632 pData->bDisconnect = FALSE;
633 continue;
637 break;
640 /* if the error wasn't handled then we exit */
641 nResult = SOAP_HTTP_ERROR;
642 break;
645 /* get the status code from the response to determine if we need
646 to authorize */
647 dwStatusCodeLen = sizeof(dwStatusCode);
648 bResult = HttpQueryInfo(
649 hHttpRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
650 &dwStatusCode, &dwStatusCodeLen, NULL);
651 if ( !bResult )
653 soap->error = GetLastError();
654 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
655 "wininet %p: fsend, error %d (%s) in HttpQueryInfo\n",
656 soap, soap->error, wininet_error_message(soap,soap->error) ));
657 nResult = SOAP_HTTP_ERROR;
658 break;
661 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
662 "wininet %p: fsend, HTTP status code = %lu\n",
663 soap, dwStatusCode));
666 if we need authentication, then request the user for the
667 appropriate data. Their reply is saved into the request so
668 that we can use it later.
670 switch ( dwStatusCode )
672 case HTTP_STATUS_DENIED:
673 case HTTP_STATUS_PROXY_AUTH_REQ:
675 wininet_rseReturn errorResolved = rseDisplayDlg;
676 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
677 "wininet %p: fsend, user authenication required\n",
678 soap ));
679 if (pData->pRseCallback)
681 errorResolved = pData->pRseCallback(hHttpRequest, dwStatusCode);
683 if (errorResolved == rseDisplayDlg)
685 errorResolved = (wininet_rseReturn)
686 wininet_resolve_send_error( hHttpRequest, ERROR_INTERNET_INCORRECT_PASSWORD );
688 if ( errorResolved == rseTrue )
690 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
691 "wininet %p: fsend, authentication has been provided\n",
692 soap ));
695 we may have been disconnected by the error. Since we
696 are going to try again, we will automatically be
697 reconnected. Therefore we want to disregard any previous
698 disconnection messages.
700 pData->bDisconnect = FALSE;
701 bRetryPost = TRUE;
702 continue;
705 break;
709 /* if we have an allocated buffer then we can deallocate it now */
710 if ( pData->pBuffer != a_pBuffer )
712 free( pData->pBuffer );
715 pData->pBuffer = 0;
716 pData->uiBufferLen = 0;
717 pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
719 return nResult;
722 /* gsoap documentation:
723 Called for all receive operations to fill buffer s of maximum length n.
724 Should return the number of bytes read or 0 in case of an error, e.g. EOF.
725 Built-in gSOAP function: frecv
727 static size_t
728 wininet_frecv(
729 struct soap * soap,
730 char * a_pBuffer,
731 size_t a_uiBufferLen )
733 HINTERNET hHttpRequest = (HINTERNET) soap->socket;
734 DWORD dwBytesRead = 0;
735 size_t uiTotalBytesRead = 0;
736 BOOL bResult;
738 soap->error = SOAP_OK;
740 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
741 "wininet %p: frecv, available buffer len = %lu\n",
742 soap, a_uiBufferLen ));
745 NOTE: we do not check here that our connection hasn't been
746 disconnected because in HTTP/1.0 connections, it will always have been
747 disconnected by now. This is because the response is checked by the
748 wininet_fsend function to ensure that we didn't need any special
749 authentication. At that time the connection would have been
750 disconnected. This is okay however as we can still read the response
751 from the request handle.
756 /* read from the connection up to our maximum amount of data */
757 _ASSERTE( a_uiBufferLen <= ULONG_MAX );
758 bResult = InternetReadFile(
759 hHttpRequest,
760 &a_pBuffer[uiTotalBytesRead],
761 (DWORD) a_uiBufferLen - uiTotalBytesRead,
762 &dwBytesRead );
763 if ( bResult )
765 uiTotalBytesRead += dwBytesRead;
767 else
769 soap->error = GetLastError();
770 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
771 "wininet %p: frecv, error %d (%s) in InternetReadFile\n",
772 soap, soap->error, wininet_error_message(soap,soap->error) ));
775 while ( bResult && dwBytesRead && uiTotalBytesRead < a_uiBufferLen );
777 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
778 "wininet %p: recv, received %lu bytes\n", soap, uiTotalBytesRead ));
780 return uiTotalBytesRead;
783 /* gsoap documentation:
784 Called by client proxy multiple times, to close a socket connection before
785 a new socket connection is established and at the end of communications
786 when the SOAP_IO_KEEPALIVE flag is not set and soap.keep_alive = 0
787 (indicating that the other party supports keep alive). Should return
788 SOAP_OK, or a gSOAP error code. Built-in gSOAP function: tcp_disconnect
790 static int
791 wininet_disconnect(
792 struct soap * soap )
794 struct wininet_data * pData =
795 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
797 soap->error = SOAP_OK;
799 DBGLOG(TEST, SOAP_MESSAGE(fdebug, "wininet %p: disconnect\n", soap ));
801 /* force a disconnect by setting the disconnect flag to TRUE */
802 pData->bDisconnect = TRUE;
803 wininet_have_connection( soap, pData );
805 return SOAP_OK;
808 /* this is mostly for debug tracing */
809 void CALLBACK
810 wininet_callback(
811 HINTERNET hInternet,
812 DWORD_PTR dwContext,
813 DWORD dwInternetStatus,
814 LPVOID lpvStatusInformation,
815 DWORD dwStatusInformationLength )
817 struct soap * soap = (struct soap *) dwContext;
819 UNUSED_ARG( hInternet );
820 UNUSED_ARG( lpvStatusInformation );
821 UNUSED_ARG( dwStatusInformationLength );
823 switch ( dwInternetStatus )
825 case INTERNET_STATUS_RESOLVING_NAME:
826 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
827 "wininet %p: INTERNET_STATUS_RESOLVING_NAME\n", soap ));
828 break;
829 case INTERNET_STATUS_NAME_RESOLVED:
830 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
831 "wininet %p: INTERNET_STATUS_NAME_RESOLVED\n", soap ));
832 break;
833 case INTERNET_STATUS_CONNECTING_TO_SERVER:
834 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
835 "wininet %p: INTERNET_STATUS_CONNECTING_TO_SERVER\n", soap));
836 break;
837 case INTERNET_STATUS_CONNECTED_TO_SERVER:
838 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
839 "wininet %p: INTERNET_STATUS_CONNECTED_TO_SERVER\n", soap));
840 break;
841 case INTERNET_STATUS_SENDING_REQUEST:
842 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
843 "wininet %p: INTERNET_STATUS_SENDING_REQUEST\n", soap));
844 break;
845 case INTERNET_STATUS_REQUEST_SENT:
846 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
847 "wininet %p: INTERNET_STATUS_REQUEST_SENT, bytes sent = %lu\n",
848 soap, *(DWORD *)lpvStatusInformation ));
849 break;
850 case INTERNET_STATUS_RECEIVING_RESPONSE:
851 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
852 "wininet %p: INTERNET_STATUS_RECEIVING_RESPONSE\n", soap));
853 break;
854 case INTERNET_STATUS_RESPONSE_RECEIVED:
855 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
856 "wininet %p: INTERNET_STATUS_RESPONSE_RECEIVED, bytes received = %lu\n",
857 soap, *(DWORD *)lpvStatusInformation ));
858 break;
859 case INTERNET_STATUS_CTL_RESPONSE_RECEIVED:
860 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
861 "wininet %p: INTERNET_STATUS_CTL_RESPONSE_RECEIVED\n", soap));
862 break;
863 case INTERNET_STATUS_PREFETCH:
864 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
865 "wininet %p: INTERNET_STATUS_PREFETCH\n", soap));
866 break;
867 case INTERNET_STATUS_CLOSING_CONNECTION:
868 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
869 "wininet %p: INTERNET_STATUS_CLOSING_CONNECTION\n", soap));
870 break;
871 case INTERNET_STATUS_CONNECTION_CLOSED:
872 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
873 "wininet %p: INTERNET_STATUS_CONNECTION_CLOSED\n", soap));
875 /* the connection has been closed, so we close the handle here */
876 struct wininet_data * pData =
877 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
878 if ( pData->hConnection )
881 we only mark this for disconnection otherwise we get
882 errors when reading the data from the handle. In every
883 function that we use the connection we will check first to
884 see if it has been disconnected.
886 pData->bDisconnect = TRUE;
889 break;
890 case INTERNET_STATUS_HANDLE_CREATED:
891 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
892 "wininet %p: INTERNET_STATUS_HANDLE_CREATED\n", soap));
893 break;
894 case INTERNET_STATUS_HANDLE_CLOSING:
895 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
896 "wininet %p: INTERNET_STATUS_HANDLE_CLOSING\n", soap));
897 break;
898 #ifdef INTERNET_STATUS_DETECTING_PROXY
899 case INTERNET_STATUS_DETECTING_PROXY:
900 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
901 "wininet %p: INTERNET_STATUS_DETECTING_PROXY\n", soap));
902 break;
903 #endif
904 case INTERNET_STATUS_REQUEST_COMPLETE:
905 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
906 "wininet %p: INTERNET_STATUS_REQUEST_COMPLETE\n", soap));
907 break;
908 case INTERNET_STATUS_REDIRECT:
909 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
910 "wininet %p: INTERNET_STATUS_REDIRECT, new url = %s\n",
911 soap, (char*) lpvStatusInformation ));
912 break;
913 case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
914 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
915 "wininet %p: INTERNET_STATUS_INTERMEDIATE_RESPONSE\n", soap));
916 break;
917 #ifdef INTERNET_STATUS_USER_INPUT_REQUIRED
918 case INTERNET_STATUS_USER_INPUT_REQUIRED:
919 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
920 "wininet %p: INTERNET_STATUS_USER_INPUT_REQUIRED\n", soap));
921 break;
922 #endif
923 case INTERNET_STATUS_STATE_CHANGE:
924 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
925 "wininet %p: INTERNET_STATUS_STATE_CHANGE\n", soap));
926 break;
927 #ifdef INTERNET_STATUS_COOKIE_SENT
928 case INTERNET_STATUS_COOKIE_SENT:
929 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
930 "wininet %p: INTERNET_STATUS_COOKIE_SENT\n", soap));
931 break;
932 #endif
933 #ifdef INTERNET_STATUS_COOKIE_RECEIVED
934 case INTERNET_STATUS_COOKIE_RECEIVED:
935 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
936 "wininet %p: INTERNET_STATUS_COOKIE_RECEIVED\n", soap));
937 break;
938 #endif
939 #ifdef INTERNET_STATUS_PRIVACY_IMPACTED
940 case INTERNET_STATUS_PRIVACY_IMPACTED:
941 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
942 "wininet %p: INTERNET_STATUS_PRIVACY_IMPACTED\n", soap));
943 break;
944 #endif
945 #ifdef INTERNET_STATUS_P3P_HEADER
946 case INTERNET_STATUS_P3P_HEADER:
947 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
948 "wininet %p: INTERNET_STATUS_P3P_HEADER\n", soap));
949 break;
950 #endif
951 #ifdef INTERNET_STATUS_P3P_POLICYREF
952 case INTERNET_STATUS_P3P_POLICYREF:
953 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
954 "wininet %p: INTERNET_STATUS_P3P_POLICYREF\n", soap));
955 break;
956 #endif
957 #ifdef INTERNET_STATUS_COOKIE_HISTORY
958 case INTERNET_STATUS_COOKIE_HISTORY:
959 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
960 "wininet %p: INTERNET_STATUS_COOKIE_HISTORY\n", soap));
961 break;
962 #endif
967 check to ensure that our connection hasn't been disconnected
968 and disconnect remaining handles if necessary.
970 static BOOL
971 wininet_have_connection(
972 struct soap * soap,
973 struct wininet_data * a_pData )
975 /* close the http request if we don't have a connection */
976 BOOL bCloseRequest = a_pData->bDisconnect || !a_pData->hConnection;
977 if ( bCloseRequest && soap->socket != SOAP_INVALID_SOCKET )
979 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
980 "wininet %p: closing request\n", soap));
982 InternetCloseHandle( (HINTERNET) soap->socket );
983 soap->socket = SOAP_INVALID_SOCKET;
986 /* close the connection if we don't have a request */
987 if ( soap->socket == SOAP_INVALID_SOCKET && a_pData->hConnection )
989 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
990 "wininet %p: closing connection\n", soap));
992 InternetCloseHandle( a_pData->hConnection );
993 a_pData->hConnection = NULL;
995 a_pData->bDisconnect = FALSE;
997 /* clean up the send details if we don't have a request */
998 if ( soap->socket == SOAP_INVALID_SOCKET )
1000 if ( a_pData->pBuffer )
1002 free( a_pData->pBuffer );
1003 a_pData->pBuffer = 0;
1005 a_pData->uiBufferLen = 0;
1006 a_pData->uiBufferLenMax = INVALID_BUFFER_LENGTH;
1009 /* we now either still have both request and connection, or neither */
1010 return (a_pData->hConnection != NULL);
1013 static DWORD
1014 wininet_set_timeout(
1015 struct soap * soap,
1016 struct wininet_data * a_pData,
1017 const char * a_pszTimeout,
1018 DWORD a_dwOption,
1019 int a_nTimeout )
1021 UNUSED_ARG( soap );
1022 UNUSED_ARG( a_pszTimeout );
1024 if ( a_nTimeout > 0 )
1026 DWORD dwTimeout = a_nTimeout * 1000;
1027 if ( !InternetSetOption( a_pData->hInternet,
1028 a_dwOption, &dwTimeout, sizeof(DWORD) ) )
1030 DWORD dwErrorCode = GetLastError();
1031 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1032 "wininet %p: failed to set %s timeout, error %d (%s)\n",
1033 soap, a_pszTimeout, dwErrorCode,
1034 wininet_error_message(soap,dwErrorCode) ));
1035 return dwErrorCode;
1038 return 0;
1042 static BOOL
1043 wininet_flag_set_option(
1044 HINTERNET a_hHttpRequest,
1045 DWORD a_dwOption,
1046 DWORD a_dwFlag )
1048 DWORD dwBuffer;
1049 DWORD dwBufferLength = sizeof(DWORD);
1051 BOOL bSuccess = InternetQueryOption(
1052 a_hHttpRequest,
1053 a_dwOption,
1054 &dwBuffer,
1055 &dwBufferLength );
1056 if (bSuccess) {
1057 dwBuffer |= a_dwFlag;
1058 bSuccess = InternetSetOption(
1059 a_hHttpRequest,
1060 a_dwOption,
1061 &dwBuffer,
1062 dwBufferLength);
1064 #ifdef SOAP_DEBUG
1065 if ( !bSuccess ) {
1066 DWORD dwErrorCode = GetLastError();
1067 DBGLOG(TEST, SOAP_MESSAGE(fdebug,
1068 "wininet %p: failed to set option: %X, error %d (%s)\n",
1069 a_hHttpRequest, a_dwOption, dwErrorCode,
1070 wininet_error_message(soap,dwErrorCode) ));
1072 #endif
1073 return bSuccess;
1077 static BOOL
1078 wininet_resolve_send_error(
1079 HINTERNET a_hHttpRequest,
1080 DWORD a_dwErrorCode )
1082 DWORD dwResult = InternetErrorDlg(
1083 GetDesktopWindow(),
1084 a_hHttpRequest,
1085 a_dwErrorCode,
1086 FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
1087 FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
1088 FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
1089 NULL );
1090 BOOL bRetVal = (a_dwErrorCode == ERROR_INTERNET_INCORRECT_PASSWORD) ?
1091 (dwResult == ERROR_INTERNET_FORCE_RETRY) :
1092 (dwResult == ERROR_SUCCESS);
1093 /* If appropriate for your application, it is possible to ignore
1094 errors in future once they have been handled or ignored once.
1095 For example, to ignore invalid SSL certificate dates on this
1096 connection once the client has indicated to ignore them this
1097 time, uncomment this code.
1100 if (bRetVal)
1102 switch (a_dwErrorCode)
1104 case ERROR_INTERNET_SEC_CERT_CN_INVALID:
1105 wininet_flag_set_option(a_hHttpRequest,
1106 INTERNET_OPTION_SECURITY_FLAGS,
1107 SECURITY_FLAG_IGNORE_CERT_CN_INVALID );
1108 break;
1112 return bRetVal;
1116 #ifdef SOAP_DEBUG
1118 static const char *
1119 wininet_error_message(
1120 struct soap * soap,
1121 DWORD a_dwErrorMsgId )
1123 HINSTANCE hModule;
1124 DWORD dwResult;
1125 DWORD dwFormatFlags;
1126 struct wininet_data * pData =
1127 (struct wininet_data *) soap_lookup_plugin( soap, wininet_id );
1129 /* free any existing error message */
1130 wininet_free_error_message( pData );
1132 dwFormatFlags =
1133 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1134 FORMAT_MESSAGE_IGNORE_INSERTS |
1135 FORMAT_MESSAGE_FROM_SYSTEM;
1137 /* load wininet.dll for the error messages */
1138 hModule = LoadLibraryExA( "wininet.dll", NULL,
1139 LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES );
1140 if ( hModule )
1142 dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
1145 /* format the messages */
1146 dwResult = FormatMessageA(
1147 dwFormatFlags,
1148 hModule,
1149 a_dwErrorMsgId,
1150 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1151 (LPSTR) &pData->pszErrorMessage,
1153 NULL );
1155 /* free the library if we loaded it */
1156 if ( hModule )
1158 FreeLibrary( hModule );
1161 /* remove the CR LF from the error message */
1162 if ( dwResult > 2 )
1164 pData->pszErrorMessage[dwResult-2] = 0;
1165 return pData->pszErrorMessage;
1167 else
1169 const static char szUnknown[] = "(unknown)";
1170 return szUnknown;
1174 static void
1175 wininet_free_error_message(
1176 struct wininet_data * a_pData )
1178 if ( a_pData->pszErrorMessage )
1180 LocalFree( a_pData->pszErrorMessage );
1181 a_pData->pszErrorMessage = 0;
1185 #endif /* SOAP_DEBUG */