2 See the header file for details. This file is distributed under the MIT licence.
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)
25 static const char wininet_id
[] = "wininet-2.0";
27 /* plugin private 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 */
40 /* this is only used for DBGLOG output */
41 char * pszErrorMessage
; /* wininet/system error message */
45 /* forward declarations */
49 struct wininet_data
* a_pData
,
50 DWORD a_dwRequestFlags
);
54 struct soap_plugin
* a_pDst
,
55 struct soap_plugin
* a_pSrc
);
59 struct soap_plugin
* a_pPluginData
);
63 const char * a_pszEndpoint
,
64 const char * a_pszHost
,
69 const char * a_pszKey
,
70 const char * a_pszValue
);
74 const char * a_pBuffer
,
75 size_t a_uiBufferLen
);
80 size_t a_uiBufferLen
);
88 DWORD dwInternetStatus
,
89 LPVOID lpvStatusInformation
,
90 DWORD dwStatusInformationLength
);
92 wininet_have_connection(
94 struct wininet_data
* a_pData
);
98 struct wininet_data
* a_pData
,
99 const char * a_pszTimeout
,
103 wininet_resolve_send_error(
104 HINTERNET a_hHttpRequest
,
105 DWORD a_dwErrorCode
);
108 /* this is only used for DBGLOG output */
110 wininet_error_message(
111 struct soap
* a_pData
,
112 DWORD a_dwErrorMsgId
);
114 wininet_free_error_message(
115 struct wininet_data
* a_pData
);
117 #define wininet_free_error_message(x)
120 /* plugin registration */
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
)
138 if ( !wininet_init( soap
,
139 (struct wininet_data
*) a_pPluginData
->data
,
140 (DWORD
) (size_t) a_dwRequestFlags
) )
142 free( a_pPluginData
->data
);
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
));
157 /* initialize private data */
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
);
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
;
205 wininet_set_rse_callback(
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 */
222 struct soap_plugin
* a_pDst
,
223 struct soap_plugin
* a_pSrc
)
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 */
237 struct soap_plugin
* a_pPluginData
)
239 struct wininet_data
* pData
=
240 (struct wininet_data
*) a_pPluginData
->data
;
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
;
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
274 const char * a_pszEndpoint
,
275 const char * a_pszHost
,
278 URL_COMPONENTSA urlComponents
;
279 char szUrlPath
[MAX_PATH
];
280 char szHost
[MAX_PATH
];
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
);
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
);
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.
375 const char * a_pszKey
,
376 const char * a_pszValue
)
378 HINTERNET hHttpRequest
= (HINTERNET
) soap
->socket
;
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
) )
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 );
418 szHeader
, 4096, "%s: %s\r\n", a_pszKey
, a_pszValue
);
423 bResult
= HttpAddRequestHeadersA( hHttpRequest
, szHeader
, nLen
,
424 HTTP_ADDREQ_FLAG_ADD_IF_NEW
);
427 we don't return an error if this fails because it isn't
428 (or shouldn't be) critical.
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()) ));
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
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.
458 const char * a_pBuffer
,
459 size_t a_uiBufferLen
)
461 HINTERNET hHttpRequest
= (HINTERNET
) soap
->socket
;
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
) )
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
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
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
)
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
;
580 _ASSERTE( pData
->uiBufferLen
== pData
->uiBufferLenMax
);
582 /* we've now got the entire message, now we can enter our sending loop */
588 bResult
= HttpSendRequestA(
589 hHttpRequest
, NULL
, 0, pData
->pBuffer
, (DWORD
)pData
->uiBufferLen
);
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
));
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
;
640 /* if the error wasn't handled then we exit */
641 nResult
= SOAP_HTTP_ERROR
;
645 /* get the status code from the response to determine if we need
647 dwStatusCodeLen
= sizeof(dwStatusCode
);
648 bResult
= HttpQueryInfo(
649 hHttpRequest
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
,
650 &dwStatusCode
, &dwStatusCodeLen
, NULL
);
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
;
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",
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",
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
;
709 /* if we have an allocated buffer then we can deallocate it now */
710 if ( pData
->pBuffer
!= a_pBuffer
)
712 free( pData
->pBuffer
);
716 pData
->uiBufferLen
= 0;
717 pData
->uiBufferLenMax
= INVALID_BUFFER_LENGTH
;
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
731 size_t a_uiBufferLen
)
733 HINTERNET hHttpRequest
= (HINTERNET
) soap
->socket
;
734 DWORD dwBytesRead
= 0;
735 size_t uiTotalBytesRead
= 0;
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(
760 &a_pBuffer
[uiTotalBytesRead
],
761 (DWORD
) a_uiBufferLen
- uiTotalBytesRead
,
765 uiTotalBytesRead
+= dwBytesRead
;
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
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
);
808 /* this is mostly for debug tracing */
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
));
829 case INTERNET_STATUS_NAME_RESOLVED
:
830 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
831 "wininet %p: INTERNET_STATUS_NAME_RESOLVED\n", soap
));
833 case INTERNET_STATUS_CONNECTING_TO_SERVER
:
834 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
835 "wininet %p: INTERNET_STATUS_CONNECTING_TO_SERVER\n", soap
));
837 case INTERNET_STATUS_CONNECTED_TO_SERVER
:
838 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
839 "wininet %p: INTERNET_STATUS_CONNECTED_TO_SERVER\n", soap
));
841 case INTERNET_STATUS_SENDING_REQUEST
:
842 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
843 "wininet %p: INTERNET_STATUS_SENDING_REQUEST\n", soap
));
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
));
850 case INTERNET_STATUS_RECEIVING_RESPONSE
:
851 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
852 "wininet %p: INTERNET_STATUS_RECEIVING_RESPONSE\n", soap
));
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
));
859 case INTERNET_STATUS_CTL_RESPONSE_RECEIVED
:
860 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
861 "wininet %p: INTERNET_STATUS_CTL_RESPONSE_RECEIVED\n", soap
));
863 case INTERNET_STATUS_PREFETCH
:
864 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
865 "wininet %p: INTERNET_STATUS_PREFETCH\n", soap
));
867 case INTERNET_STATUS_CLOSING_CONNECTION
:
868 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
869 "wininet %p: INTERNET_STATUS_CLOSING_CONNECTION\n", soap
));
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
;
890 case INTERNET_STATUS_HANDLE_CREATED
:
891 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
892 "wininet %p: INTERNET_STATUS_HANDLE_CREATED\n", soap
));
894 case INTERNET_STATUS_HANDLE_CLOSING
:
895 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
896 "wininet %p: INTERNET_STATUS_HANDLE_CLOSING\n", soap
));
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
));
904 case INTERNET_STATUS_REQUEST_COMPLETE
:
905 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
906 "wininet %p: INTERNET_STATUS_REQUEST_COMPLETE\n", soap
));
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
));
913 case INTERNET_STATUS_INTERMEDIATE_RESPONSE
:
914 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
915 "wininet %p: INTERNET_STATUS_INTERMEDIATE_RESPONSE\n", soap
));
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
));
923 case INTERNET_STATUS_STATE_CHANGE
:
924 DBGLOG(TEST
, SOAP_MESSAGE(fdebug
,
925 "wininet %p: INTERNET_STATUS_STATE_CHANGE\n", soap
));
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
));
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
));
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
));
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
));
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
));
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
));
967 check to ensure that our connection hasn't been disconnected
968 and disconnect remaining handles if necessary.
971 wininet_have_connection(
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
);
1014 wininet_set_timeout(
1016 struct wininet_data
* a_pData
,
1017 const char * a_pszTimeout
,
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
) ));
1043 wininet_flag_set_option(
1044 HINTERNET a_hHttpRequest,
1049 DWORD dwBufferLength = sizeof(DWORD);
1051 BOOL bSuccess = InternetQueryOption(
1057 dwBuffer |= a_dwFlag;
1058 bSuccess = InternetSetOption(
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) ));
1078 wininet_resolve_send_error(
1079 HINTERNET a_hHttpRequest
,
1080 DWORD a_dwErrorCode
)
1082 DWORD dwResult
= InternetErrorDlg(
1086 FLAGS_ERROR_UI_FILTER_FOR_ERRORS
|
1087 FLAGS_ERROR_UI_FLAGS_GENERATE_DATA
|
1088 FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS
,
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.
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 );
1119 wininet_error_message(
1121 DWORD a_dwErrorMsgId
)
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
);
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
);
1142 dwFormatFlags
|= FORMAT_MESSAGE_FROM_HMODULE
;
1145 /* format the messages */
1146 dwResult
= FormatMessageA(
1150 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
1151 (LPSTR
) &pData
->pszErrorMessage
,
1155 /* free the library if we loaded it */
1158 FreeLibrary( hModule
);
1161 /* remove the CR LF from the error message */
1164 pData
->pszErrorMessage
[dwResult
-2] = 0;
1165 return pData
->pszErrorMessage
;
1169 const static char szUnknown
[] = "(unknown)";
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 */