Related
I'm trying to send message to telegram chat from bot using winapi and c++.
Here is my code
char szData[1024];
// initialize WinInet
HINTERNET hInternet = ::InternetOpen(TEXT("WinInet Test"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet != NULL)
{
// open HTTP session
HINTERNET hConnect = ::InternetConnect(hInternet, L"api.telegram.org", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 1);
if (hConnect != NULL)
{
wstring request = L"/bot<bot_id>/sendMessage";
// open request
HINTERNET hRequest = ::HttpOpenRequest(hConnect, L"GET", (LPCWSTR)request.c_str(), NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE, 1);
if (hRequest != NULL)
{
// send request
const wchar_t* params = L"?chat_id=<chat_id>&text=test";
BOOL isSend = ::HttpSendRequest(hRequest, NULL, 0, (LPVOID)params, wcslen(params));
if (isSend)
{
for (;;)
{
// reading data
DWORD dwByteRead;
BOOL isRead = ::InternetReadFile(hRequest, szData, sizeof(szData) - 1, &dwByteRead);
// break cycle if error or end
if (isRead == FALSE || dwByteRead == 0)
break;
// saving result
szData[dwByteRead] = 0;
}
}
// close request
::InternetCloseHandle(hRequest);
}
// close session
::InternetCloseHandle(hConnect);
}
// close WinInet
::InternetCloseHandle(hInternet);
}
wstring answer = CharPToWstring(szData);
return answer;
But I've got {"ok":false,"error_code":400,"description":"Bad Request: message text is empty"} response. <chat_id> is id consisted of digits(12345678).
If I run this request in postman or in browser - then everything is ok.
I also tried to run this request using WinHttp* methods and result is the same.
What should I change in my request parameters to make it work?
There are a number of issues with this code:
You don't need to typecast the return value of wstring::c_str() to LPCWSTR (aka const wchar_t*), as it is already that type.
You can't send body data in a GET request. The Telegram Bot API expects body data to be sent in a POST request instead.
You are telling HttpSendRequest() to send body data from a wchar_t* UTF-16 string, but that is not the correct encoding that the server is expecting. You need to use a char* UTF-8 string instead.
You are not sending a Content-Type request header to tell the server what the format of the body data is. The API supports several different formats. In this case, since you are sending the data in application/x-www-form-urlencoded format, you need to add a Content-Type: application/x-www-form-urlencoded header to the request.
With all of that said, try this instead:
// initialize WinInet
HINTERNET hInternet = ::InternetOpenW(L"WinInet Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if (hInternet == NULL) ... // error handling
// open HTTP session
HINTERNET hConnect = ::InternetConnectW(hInternet, L"api.telegram.org", INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, 1);
if (hConnect == NULL) ... // error handling
// open request
wstring wsResource = L"/bot<bot_id>/sendMessage";
HINTERNET hRequest = ::HttpOpenRequestW(hConnect, L"POST", wsResource.c_str(), NULL, NULL, 0, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE, 1);
if (hRequest == NULL) ... // error handling
// send request
string sBody = u8"chat_id=<chat_id>&text=test";
BOOL isSend = ::HttpSendRequestW(hRequest, L"Content-Type: application/x-www-form-urlencoded", -1L, sBody.c_str(), sBody.size());
if (!isSend) ... // error handling
string sReply;
char szData[1024];
DWORD dwByteRead;
while (::InternetReadFile(hRequest, szData, sizeof(szData), &dwByteRead) && dwByteRead != 0)
{
// saving result
sReply.append(szData, dwByteRead);
}
...
// use sReply as needed ...
I am facing a weird situation with a Windows application (OCX control) written in C++, which was working fine until 2 days ago. No updates were added to the application. Then suddenly, it started to have errors in the communication with a remote test server. Basically, the error is that in the response of a GET request, I am seeing junk characters like this: ÑòWo †¶âBA etc
The function that I am using for the HTTP requests is:
TCHAR* openUrlFunction(TCHAR *strServer, TCHAR *strPath, TCHAR *userAgent){
HINTERNET httpSession = NULL;
HINTERNET httpConnect = NULL;
HINTERNET httpRequest = NULL;
HINTERNET hFile = NULL;
BOOL resDownload = FALSE;
TCHAR bufferIdent[60] = { 0 };
TCHAR bufferMirror[500] = { 0 };
TCHAR szURL[200] = { 0 };
TCHAR sError[7] = { 0 };
DWORD dwFlags;
// DWORD dwTimeOut = 25000;
DWORD dwBuffLen = sizeof(dwFlags);
int i = 0;
DWORD dwRead, error;
int len;
TCHAR *res1;
TCHAR delimeter[6] = _T("\f\n\r\t\v");
HRESULT resCat;
bufferIdent[0] = '\0';
bufferMirror[0] = '\0';
szURL[0] = '\0';
std::string ss;
httpSession = InternetOpen(strAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); // header
if (httpSession)
{
httpConnect = InternetConnect(httpSession, strServer, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if (httpConnect)
{
httpRequest = HttpOpenRequest(httpConnect, "GET", strPath, NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0); parametros.
if (HttpSendRequest(httpRequest, NULL, 0, NULL, 0)) {
if (httpRequest)
{
while (InternetReadFile(httpRequest, bufferMirror, 499, &dwRead)) // Se lee el retorno
{
if (dwRead == 0)
break;
StrTrim(bufferMirror, delimeter);
while (bufferMirror[i] != NULL){
if (bufferMirror[i] != char(10))
ss = ss + bufferMirror[i];
i++;
if (bufferMirror[i] == char(10))
break;
}
i = 0;
}
}
}
else{
error = GetLastError();
_stprintf_s(sError, 7, _T("%d"), error);
InternetCloseHandle(httpRequest);
InternetCloseHandle(httpConnect);
InternetCloseHandle(httpSession);
return sError;
}
InternetCloseHandle(httpRequest);
}
InternetCloseHandle(httpConnect);
}
InternetCloseHandle(httpSession);
len = ss.length();
if (len == 0){
return NULL;
}
res1 = new TCHAR[len + 1];
resCat = StringCchCopyN(res1, len + 1, ss.c_str(), len);
if (!SUCCEEDED(resCat))
return NULL;
return res1;
}
The interesting thing here was that I decided to monitor the traffic so I have installed Fiddler on the same machine (Windows 10) where the problem is happening. After started Fiddler, I made a test with the Application and voila, the response was clear and well-formed and no junk characters were present.
I suspect that the Fiddler Everywhere Certificate that is installed has something to do with the result. So, if I turn off Fiddler, and make a new test, I again receive the junk characters.
I have checked the TLS configuration in the computer and nothing is wrong there. I am not sure if a Windows update could have caused this situation.
We have 3 machines, and the problem is occurring on 2 of the 3. All of them are Windows 10, but maybe different updates.
One other important thing here is that the server is for testing, and the client did not install any SSL certificate.
Well after looking into this problem, I just found that the root of the issue was more related to the fact the one of the variables was not correctly initialized and before the http request, this variable could contain junk characters so the next steps of the process were affected for the garbage that the variable contained. Another thing was that the connection problem with WININET was solved after installing a right SSL certificate. Even after setting some flags to bypass the SSL validation, did not work. The connection only was possible when the server was configured with the SSL certificate.
I am trying to retrieve the source code of a web page using WinHTTP. Reading from this page: http://msdn.microsoft.com/en-us/library/windows/desktop/aa384110(v=vs.85).aspx
I was able to come up with this code. The HTTP status code is 200 which means the request was completed correctly. I end up getting the source code for the login page instead of my intended web page behind it.
Should I send a httprequest to the login page and then to the file path?
Here is the code:
#include <windows.h>
#include <winhttp.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <fstream>
using namespace std;
#pragma comment(lib, "winhttp.lib")
DWORD ChooseAuthScheme(DWORD dwSupportedSchemes)
{
// It is the server's responsibility only to accept
// authentication schemes that provide a sufficient
// level of security to protect the servers resources.
//
// The client is also obligated only to use an authentication
// scheme that adequately protects its username and password.
//
// Thus, this sample code does not use Basic authentication
// becaus Basic authentication exposes the client's username
// and password to anyone monitoring the connection.
if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE)
return WINHTTP_AUTH_SCHEME_NEGOTIATE;
else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM)
return WINHTTP_AUTH_SCHEME_NTLM;
else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT)
return WINHTTP_AUTH_SCHEME_PASSPORT;
else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST)
return WINHTTP_AUTH_SCHEME_DIGEST;
else
return 0;
}
struct SWinHttpSampleGet
{
LPCWSTR szServer;
LPCWSTR szPath;
BOOL fUseSSL;
LPCWSTR szServerUsername;
LPCWSTR szServerPassword;
LPCWSTR szProxyUsername;
LPCWSTR szProxyPassword;
};
void WinHttpAuthSample(IN SWinHttpSampleGet *pGetRequest)
{
string contents;
DWORD dwStatusCode = 0;
DWORD dwSupportedSchemes;
DWORD dwFirstScheme;
DWORD dwSelectedScheme;
DWORD dwTarget;
DWORD dwLastStatus = 0;
DWORD dwSize = sizeof(DWORD);
BOOL bResults = FALSE;
BOOL bDone = FALSE;
DWORD dwProxyAuthScheme = 0;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen(L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
INTERNET_PORT nPort = (pGetRequest->fUseSSL) ?
INTERNET_DEFAULT_HTTPS_PORT :
INTERNET_DEFAULT_HTTP_PORT;
// Specify an HTTP server.
if (hSession)
hConnect = WinHttpConnect(hSession,
pGetRequest->szServer,
nPort, 0);
// Create an HTTP request handle.
if (hConnect)
hRequest = WinHttpOpenRequest(hConnect,
L"GET",
pGetRequest->szPath,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
(pGetRequest->fUseSSL) ?
WINHTTP_FLAG_SECURE : 0);
// Continue to send a request until status code
// is not 401 or 407.
if (hRequest == NULL)
bDone = TRUE;
while (!bDone)
{
// If a proxy authentication challenge was responded to, reset
// those credentials before each SendRequest, because the proxy
// may require re-authentication after responding to a 401 or
// to a redirect. If you don't, you can get into a
// 407-401-407-401- loop.
if (dwProxyAuthScheme != 0)
bResults = WinHttpSetCredentials(hRequest,
WINHTTP_AUTH_TARGET_PROXY,
dwProxyAuthScheme,
pGetRequest->szProxyUsername,
pGetRequest->szProxyPassword,
NULL);
// Send a request.
bResults = WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0);
// End the request.
if (bResults)
bResults = WinHttpReceiveResponse(hRequest, NULL);
// Resend the request in case of
// ERROR_WINHTTP_RESEND_REQUEST error.
if (!bResults && GetLastError() == ERROR_WINHTTP_RESEND_REQUEST)
continue;
// Check the status code.
if (bResults)
bResults = WinHttpQueryHeaders(hRequest,
WINHTTP_QUERY_STATUS_CODE |
WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&dwStatusCode,
&dwSize,
NULL);
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer;
if (bResults)
{
switch (dwStatusCode)
{
case 200:
// The resource was successfully retrieved.
// You can use WinHttpReadData to read the
// contents of the server's response.
printf("The resource was successfully retrieved.\n");
do
{
// Check for available data.
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
{
printf("Error %u in WinHttpQueryDataAvailable.\n",
GetLastError());
break;
}
// No more available data.
if (!dwSize)
break;
// Allocate space for the buffer.
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer)
{
printf("Out of memory\n");
break;
}
// Read the Data.
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer,
dwSize, &dwDownloaded))
{
printf("Error %u in WinHttpReadData.\n", GetLastError());
}
else
{
printf("%s", pszOutBuffer);
contents += pszOutBuffer;
}
// Free the memory allocated to the buffer.
delete[] pszOutBuffer;
// This condition should never be reached since WinHttpQueryDataAvailable
// reported that there are bits to read.
if (!dwDownloaded)
break;
} while (dwSize > 0);
bDone = true;
break;
case 401:
// The server requires authentication.
printf(" The server requires authentication. Sending credentials...\n");
// Obtain the supported and preferred schemes.
bResults = WinHttpQueryAuthSchemes(hRequest,
&dwSupportedSchemes,
&dwFirstScheme,
&dwTarget);
// Set the credentials before resending the request.
if (bResults)
{
dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes);
if (dwSelectedScheme == 0)
bDone = TRUE;
else
bResults = WinHttpSetCredentials(hRequest,
dwTarget,
dwSelectedScheme,
pGetRequest->szServerUsername,
pGetRequest->szServerPassword,
NULL);
}
// If the same credentials are requested twice, abort the
// request. For simplicity, this sample does not check
// for a repeated sequence of status codes.
if (dwLastStatus == 401)
bDone = TRUE;
break;
case 407:
// The proxy requires authentication.
printf("The proxy requires authentication. Sending credentials...\n");
// Obtain the supported and preferred schemes.
bResults = WinHttpQueryAuthSchemes(hRequest,
&dwSupportedSchemes,
&dwFirstScheme,
&dwTarget);
// Set the credentials before resending the request.
if (bResults)
dwProxyAuthScheme = ChooseAuthScheme(dwSupportedSchemes);
// If the same credentials are requested twice, abort the
// request. For simplicity, this sample does not check
// for a repeated sequence of status codes.
if (dwLastStatus == 407)
bDone = TRUE;
break;
default:
// The status code does not indicate success.
printf("Error. Status code %d returned.\n", dwStatusCode);
bDone = TRUE;
}
}
// Keep track of the last status code.
dwLastStatus = dwStatusCode;
// If there are any errors, break out of the loop.
if (!bResults)
bDone = TRUE;
}
// Report any errors.
if (!bResults)
{
DWORD dwLastError = GetLastError();
printf("Error %d has occurred.\n", dwLastError);
}
ofstream fout("Output.txt");
fout << contents;
fout.close();
// Close any open handles.
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}
int main()
{
wstring Server = L"moodle.redlands.edu";
wstring Path = L"/course/view.php?id=2213/";
wstring User = L"test";
wstring Pass = L"password";
wstring User_Proxy = L"test";
wstring Pass_Proxy = L"password";
SWinHttpSampleGet Lawl{
Server.c_str(),
Path.c_str(),
1,
User.c_str(),
Pass.c_str(),
User_Proxy.c_str(), Pass_Proxy.c_str()
};
WinHttpAuthSample(&Lawl);
system("PAUSE");
return 0;
}
I'm trying to make this program connect to a website and submit form data in order to login, but I don't know what I'm doing wrong. I have heard of others like curl and Winsock but I chose the WinINet library. So just for the testing of this program I've been using the website Pastebin to post to. So far, I haven't seen any results from this. If this program succeeds in posting the form data it will give me the header to the location of the post on their site.
Am I writing the form data char* correctly? I have seen on other stackoverflow posts where they had a large amount of dashes before some number then the put their form data.
Do I need to add something to it make it simulate clicking the submit button?
Do I need to write out values for each elements on the form?
I have tried HttpAddRequestHeaders and that didn't help me.
Also, I get the ERROR_INSUFFICIENT_BUFFER error on HttpOpenRequest but it still returns a valid HINTERNET.
#include <Windows.h>
#include <WinInet.h>
#include <iostream>
#pragma comment( lib,"Wininet.lib")
using namespace std;
char* getheaders(HINTERNET hRequest){
DWORD dwInfoLevel=HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength=10;
char* pInfoBuffer=(char*)malloc(dwInfoBufferLength+1);
while(!HttpQueryInfo(hRequest,dwInfoLevel,pInfoBuffer,&dwInfoBufferLength,NULL)){
if (GetLastError()==ERROR_INSUFFICIENT_BUFFER){
free(pInfoBuffer);
pInfoBuffer=(char*)malloc(dwInfoBufferLength+1);
}else{
fprintf(stderr,"HttpQueryInfo failed, error = %d (0x%x)\n",GetLastError(),GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
return pInfoBuffer;
}
void readfile(HINTERNET hRequest,char** buffs,int size){
DWORD dwBytesAvailable;
DWORD dwBytesRead;
for(int i=0;i<size;i++){
if(!InternetQueryDataAvailable(hRequest,&dwBytesAvailable,0,0)) break;
buffs[i]=(char*)malloc(dwBytesAvailable+1);
bool bResult=InternetReadFile(hRequest,buffs[i],dwBytesAvailable,&dwBytesRead);
if(!bResult | dwBytesRead==0) break;
}
}
int main(int argc,char** argv){
char* hdrs="Content-Type: application/x-www-form-urlencoded";
char* frmdata="paste_code=test";
LPCSTR accept[2]={"*/*", NULL};
HINTERNET hSession = InternetOpen("http generic",INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hConnect = InternetConnect(hSession, "www.pastebin.com",INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
HINTERNET hRequest = HttpOpenRequest(hConnect, "GET","/", NULL, NULL, accept, 0, 0);
//ERROR_INSUFFICIENT_BUFFER (122) with "accept".
bool send=HttpSendRequest(hRequest, hdrs, strlen(hdrs), NULL,NULL);
if(!send){
printf("HttpSendRequest failed, code=%d",GetLastError());
system("pause>nul");
return 0;
}
char* heads=getheaders(hRequest);
printf("%s\n\n\n\n",heads);
HINTERNET hRequest2 = HttpOpenRequest(hConnect, "POST","/", NULL, NULL, accept, 0, 0);
//ERROR_INSUFFICIENT_BUFFER (122) with "accept".
send=HttpSendRequest(hRequest2, hdrs, strlen(hdrs), frmdata,strlen(frmdata));
if(!send){
printf("HttpSendRequest failed, code=%d",GetLastError());
system("pause>nul");
return 0;
}
heads=getheaders(hRequest);
printf("%s\n\n\n\n",heads);
InternetCloseHandle(hRequest);
InternetCloseHandle(hRequest2);
InternetCloseHandle(hConnect);
InternetCloseHandle(hSession);
system("pause>nul");
return 0;
}
Your code is nearly correct, you must make sure of the following points:
char* hdrs="Content-Type: application/x-www-form-urlencoded";
you must make sure that your return object from POST message will be of type x-www-form-urlencoded or JSON . if it's JSON you
need to define char* hdrs="Content-Type: application/json\r\n";
Note: you must append \r\n to the hdrs.
try to call readFile method with buffer of size 10000 for example
and print buffer , it will print the output of the response to the
connection
In HINTERNET hRequest2 = HttpOpenRequest(hConnect, "POST","/", NULL, NULL, accept, 0, 0); instead of "/" you must call the path of
the requested API for example: .
In HINTERNET hRequest2 = HttpOpenRequest(hConnect, "POST", "/users/jsonlogin", NULL, NULL, accept, 0, 0);
Hey all i want to login onto my works webpage with wininet, this is my current code:
int main()
{
HINTERNET hInet = InternetOpenA("UserAgent/1.0", INTERNET_OPEN_TYPE_PRECONFIG,0, 0, 0 );
if(!hInet)
{
printf("hInet Failed!\n");
return -1;
}
HINTERNET hConnection = InternetConnectA( hInet,"app.tamigo.com",INTERNET_DEFAULT_HTTPS_PORT,"","", INTERNET_SERVICE_HTTP,0,0);
if (!hConnection)
{
InternetCloseHandle(hInet);
printf("InternetConnectA failed!\n");
return -1;
}
HINTERNET hRequest = HttpOpenRequestA( hConnection, "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",NULL,"https://app.tamigo.com/Home/Pages/Login.aspx", NULL, INTERNET_FLAG_KEEP_CONNECTION, 0 );
if (!hRequest)
{
printf("BuildRequestHeader failed %d!\n",GetLastError());
InternetCloseHandle(hConnection);
InternetCloseHandle(hInet);
return -1;
}
HttpSendRequestA(hRequest, NULL, 0, NULL, 0);
DWORD dwInfoLevel = HTTP_QUERY_RAW_HEADERS_CRLF;
DWORD dwInfoBufferLength = 10;
BYTE *pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
while (!HttpQueryInfo(hRequest, dwInfoLevel, pInfoBuffer, &dwInfoBufferLength, NULL))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
free(pInfoBuffer);
pInfoBuffer = (BYTE *)malloc(dwInfoBufferLength+1);
}
else
{
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
break;
}
}
pInfoBuffer[dwInfoBufferLength] = '\0';
printf("%s", pInfoBuffer);
free(pInfoBuffer);
cin.get();
return 1;
}
if this code is right, i have to login with my username and pass,i got a cookie using "Firefox plugin Tamper Data". How can i set this cookie with wininet?
Thanks alot for reading and for your time
If the cookie already exists from a previous WinInet request, then WinInet will send it automatically. However, if the cookie does not exist in WinInet's cookie cache (if instance, if you got the cookie from another source), then you will have to use HttpAddRequestHeaders() to provide your own Cookie: request header before calling HttpSendRequest().