UrlUnescape() and unicode chars - windows

I am trying to write the program on C++ that will decode the URL-encoded string that contains some URL-encoded unicode characters.
#include <windows.h>
#include <string>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
int _tmain(int argc, _TCHAR* argv[])
{
std::wstring test = L"bla+%D0%B1%D0%BB%D0%BE%D1%84+%E6%97%A5%E6%9C%AC%E8%AA%9E";
PWSTR urlencodedStr = const_cast<WCHAR*>(test.c_str());
WCHAR decodedStr[1025];
DWORD size = 1024;
HRESULT hres = UrlUnescape(urlencodedStr, decodedStr, &size, NULL);
if (hres == S_OK)
MessageBox(NULL, decodedStr, L"decoded string", MB_OK);
return 0;
}
I'm expecting to get L"bla блоф 日本語" in decodedStr. But i'm getting L"bla+блоф+日本語" instead.
I am using unicode charset in my build.
What am i doing wrong?

UrlUnescape converts URL-decoded %xx bytes into characters using the default (ANSI) code page by default. This is almost never what you want.
From Windows 8 onwards, you can pass the UNESCAPE_AS_UTF8 flag to make it behave. If you can't depend on Win8, you'll have to use/write a different URL-decoding library call that doesn't suffer from this problem.
Also there is the issue of the +: in plain-URL-encoding (for example for use in a path part), this means a plus, but in form-url-encoding (for example in a query parameter), which is what you seem to have here, it means a space. A good URL decoder will give you the option to say which one you mean; UrlUnescape does not. An alternative is to manually replace the + with space on input before URL-decoding; this is one special case and no other characters are similarly affected.

Ok. So i wrote my own function to decode URL-encoded strings with unicode characters. Here it is:
#include <windows.h>
#include <string>
#include <shlwapi.h>
#include <sstream>
#include <iostream>
#include <wininet.h> // For INTERNET_MAX_URL_LENGTH
#pragma comment(lib, "Shlwapi.lib")
bool IsHexChar(const WCHAR _char)
{
return ((_char == L'A') ||
(_char == L'B') ||
(_char == L'C') ||
(_char == L'D') ||
(_char == L'E') ||
(_char == L'F') ||
iswalnum(_char));
}
std::wstring UrlDecode(const std::wstring& _encodedStr)
{
std::string charStr;
for (size_t i = 0; i < _encodedStr.length(); ++i)
{
if ((_encodedStr[i] == L'%') && (IsHexChar(_encodedStr[i+1])) && (IsHexChar(_encodedStr[i+2])))
{
std::wstring hexCodeStr = L"0x";
hexCodeStr += _encodedStr[i+1];
hexCodeStr += _encodedStr[i+2];
unsigned int hexCharCode;
std::wstringstream ss;
ss << std::hex << hexCodeStr;
ss >> hexCharCode;
charStr += static_cast<char>(hexCharCode);
i += 2;
}
else if (_encodedStr[i] == L'+')
charStr += L' ';
else
charStr += _encodedStr[i];
}
WCHAR decodedStr[INTERNET_MAX_URL_LENGTH];
MultiByteToWideChar(CP_UTF8, 0, charStr.c_str(), -1, decodedStr, sizeof(decodedStr));
return decodedStr;
}
Use like this:
std::wstring encodedStr = L"bla+%D0%B1%D0%BB%D0%BE%D1%84+%E6%97%A5%E6%9C%AC%E8%AA%9E";
std::wstring decodedStr = UrlDecode(encodedStr);

Related

How to drive my credential provider with CredUIPromptForWindowsCredentials

I've been working on a credential provider and I've been debugging it through logging. Recently learned about CredUIPromptForWindowsCredentials() API to be able to invoke it from other than login screen or remote desktop connection. The only way at this time I can seem to get my credential to display is to set the last param to CREDUIWIN_SECURE_PROMPT. I've tried various schemes of the flags with no luck. My CP works, that's not the problem. Problem is easier debugging. Only once have I had to go to rescue mode when I made my laptop unbootable. ;) The problem with using the CREDUIWIN_SECURE_PROMPT flag is that then I don't have access to the debugger because login takes over the screen and I can't get back to my debugger. I suppose the only workaround would be to remote debug on another machine with this API, but I'd prefer not to hassle with that.
My CP is registered at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{55157584-ff0f-48ce-9178-a4e290901663} and the default property is "MyCredProvider" (for this example). (GUID, prop name changed to protect the guilty. Also ignore LsaString where bad things would happen on a copy--of which I'm not doing.)
Any way to get my custom CP without using the secure prompt?
#include <windows.h>
#include <iostream>
#include <EvoApi.h>
#include <decrypt.h>
#include <atlbase.h>
#include <Lmwksta.h>
#include <StrSafe.h>
#include <LMAPIbuf.h>
#include <LMJoin.h>
#include <wincred.h>
#include <NTSecAPI.h>
#pragma warning(disable : 4996)
#pragma comment(lib, "netapi32.lib")
#pragma comment(lib, "credui.lib")
#pragma comment(lib, "secur32")
using namespace std;
template <size_t SIZE = 256>
struct LsaString : public LSA_STRING
{
LsaString()
{
MaximumLength = SIZE;
Length = 0;
Buffer = pBuf.get();
}
LsaString(LPCSTR pWhat)
{
MaximumLength = SIZE;
Length = 0;
Buffer = pBuf.get();
Init(pWhat);
}
void Init(LPCSTR pWhat)
{
size_t len = strlen(pWhat);
if (len >= SIZE)
throw;
strcpy(Buffer, pWhat);
Length = (USHORT) len;
}
unique_ptr<char[]> pBuf = make_unique< char[] >(SIZE);
};
int _tmain(int argc, wchar_t* argv[])
{
#if 1
wstring me(_T("MYLOGING"));
wstring url(_T("Header"));
wstring message(_T("Enter credentials for ..."));
CREDUI_INFOW credInfo;
credInfo.pszCaptionText = url.c_str();
credInfo.hbmBanner = nullptr;
credInfo.hwndParent = NULL;
credInfo.pszMessageText = message.c_str();
credInfo.cbSize = sizeof(CREDUI_INFOW);
ULONG authPackage = 0;
LSAHANDLE lsaHandle;
LsaConnectUntrusted(&lsaHandle);
LsaString<> lsaString("MyCredProvider");
//LsaString<> lsaString(MICROSOFT_KERBEROS_NAME_A); // works ... as far as finding in LsaLookupAuth...
//LsaString<> lsaString(NEGOSSP_NAME_A); // works ... as far as finding in LsaLookupAuth...
ULONG ulPackage = 0;
LsaLookupAuthenticationPackage(lsaHandle, &lsaString, &ulPackage);
void* pBlob;
ULONG blobSize = 0;
DWORD dwFlags = CREDUIWIN_GENERIC; //CREDUIWIN_SECURE_PROMPT
CredUIPromptForWindowsCredentials(&credInfo, 0, &ulPackage, NULL, 0, &pBlob, &blobSize, FALSE, dwFlags);
if (pBlob) CoTaskMemFree(pBlob);
return 0;
}

winAPI GetAdaptersAddresses unprintable friendly name

(https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses)
Why are some of the user friendly names in PIP_ADAPTER_ADDRESSES unprintable? (aswell as a few other attributes such as dns suffix)
By unprintable, I mean containing non-printable characters. for exmaple, the first character in one of the friendly names I tested had a unicode value fo 8207 (decimal)
A minimal complete viable example
#include <winsock2.h>
#include <iphlpapi.h>
#include <vector>
#include <iostream>
int main()
{
PIP_ADAPTER_ADDRESSES adapterAddresses;
DWORD dwReqSize;
DWORD retVal;
DWORD count = 0;
std::string tempForWstringConv;
retVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, NULL, &dwReqSize); // for knowing the required size
if (retVal != ERROR_BUFFER_OVERFLOW) {
return -1;
}
adapterAddresses = (PIP_ADAPTER_ADDRESSES)malloc(dwReqSize);
retVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, adapterAddresses, &dwReqSize); // this time actually getting the desired content
if (retVal != ERROR_SUCCESS) {
return -1;
}
for (PIP_ADAPTER_ADDRESSES adapter = adapterAddresses; adapter != NULL; adapter = adapter->Next)
{
//outLog.push_back(Adapter());
printf("\tFriendly name: %ls\n", adapter->FriendlyName);
}
return 0;
}
I finally found A solution!
meet _setmode(_fileno(stdout), _O_U16TEXT);
the problem was that the output buffer wasn't allowing these characters because the mode was incorrect. Alas, our desired output:
inorder to use this you MUST A: switch all occurences of cout to wcou; B: switch all occurences of printf to wprintf. C: include and

Solution to avoid the error: "Entry point not found " even though I'm checking the OS version before callingGetTcpTable2 api on windows XPindows XP?

I'm aware that the GetTcpTable2 api is supported only on windows vista and above versions, hence the code checks for the OS version and only then enters the loop that calls the api. I'm compiling the code on windows 7, visual studio 2008 and the executable runs fine on windows 7 and other OS except Windows XP, the error thrown is :
The code snippet is:`
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
// Need to link with Iphlpapi.lib and Ws2_32.lib
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
/* Note: could also use malloc() and free() */
int main()
{
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((OSVERSIONINFO *)&osvi);
if(osvi.dwMajorVersion>=6)
{
// Declare and initialize variables
PMIB_TCPTABLE2 pTcpTable;
ULONG ulSize = 0;
DWORD dwRetVal = 0;
char szLocalAddr[128];
char szRemoteAddr[128];
struct in_addr IpAddr;
int i;
pTcpTable = (MIB_TCPTABLE2 *) MALLOC(sizeof (MIB_TCPTABLE2));
if (pTcpTable == NULL)
{
printf("Error allocating memory\n");
return 1;
}
ulSize = sizeof (MIB_TCPTABLE);
// Make an initial call to GetTcpTable2 to
// get the necessary size into the ulSize variable
if ((dwRetVal = GetTcpTable2(pTcpTable, &ulSize, TRUE)) ==
ERROR_INSUFFICIENT_BUFFER)
{
FREE(pTcpTable);
pTcpTable = (MIB_TCPTABLE2 *) MALLOC(ulSize);
if (pTcpTable == NULL)
{
printf("Error allocating memory\n");
return 1;
}
}
}
else
{
printf("Unsupported OS");
}
return 0;
}
`How do I get the executable to work on Windows XP without crashing/throwing the error shown in attached image?

gcc using unlink and readdir, 7 days old files needs to be deleted

Using this code fetched from google.
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
struct dirent *entry;
DIR *dp;
chdir("/mnt/shared");
dp = opendir(".");
while( (entry = readdir(dp)) != NULL ) {
if ( strcmp(entry->d_name, ".") &&strcmp(entry->d_name, "..") ){
unlink(entry->d_name);
}
}
}`
In this could it be possible to delete files older than 7 days from the current date?
In perl i tried as follows, but wondering this could be achived with your help?
my $now = time();
my $DATEAGE = 60*60*24*7;
for my $file (#file_list) {
my #stats = stat($file);
if ($now-$stats[9] > $DATEAGE) {
print "$file\n";}
Build the full string of the file and use several syscalls(2) (notably stat(2)) ; read Advanced Linux Programming
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
struct dirent *entry;
DIR *dp;
time_t weekago;
time(&weekago);
weekago -= 86400*7;
dp = opendir("/mnt/shared");
if (!dp) { perror("/mnt/shared"); exit(EXIT_FAILURE); };
while( (entry = readdir(dp)) != NULL ) {
if ( strcmp(entry->d_name, ".")
&& strcmp(entry->d_name, "..") ){
char buf[256];
if (snprintf(buf, sizeof(buf),
"/mnt/shared/%s", entry->d_name)
>=sizeof(buf))
{ fprintf(stderr, "too long path %s\n", buf);
exit(EXIT_FAILURE);
};
struct stat st;
if (stat(buf,&st)) {
perror(buf);
exit(EXIT_FAILURE);
};
if ((st.st_mode & S_IFMT) == S_IFREG // a plain file
&& (st.st_mtime < weekago))
{
if (remove(buf)) perror(buf);
}
}
}
My untested code above is imperfect (and not very well indented): it don't handle file paths wider than 255. But you could improve it, e.g. using asprintf(3) to build the path in heap (then you'll need to free it).
Practically speaking, use find(1). If you need to recurse in a file tree in C, use nftw(3)

Retrieving VolumeDetails of WINDOWS Drives - stuck with 'char []' to 'LPCWSTR' conversion

I am trying to get the VolumeDetails of my WINDOWS system- Drive label plus its respective Volume Serial number. I've tried since an hour and built a code which gone wrong in syntax. At present I am getting the following error with it-
error C2664: 'GetVolumeInformationW' : cannot convert parameter 1 from 'char []' to 'LPCWSTR'
Here is my code:
// getVolDrive.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
//wchar_t mydrives[5];// = " A: ";
char mydrives[] = " A: ";
string retVolSno(char drives[]) //wchar_t drives[]
{
DWORD dwSerial;
stringstream ss;
cout<<drives<<endl;
if(!GetVolumeInformation(drives, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
{
ss<<"Error: "<<GetLastError();
}
else
{
ss<<hex<<dwSerial;
}
return ss.str();
}
int _tmain(int argc, _TCHAR* argv[])
{
string cVolSno;
ULONG DriveMask = _getdrives();
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while (DriveMask)
{
cout << "In While" << endl;
if(DriveMask & 1)
printf("%s", mydrives);
wcout << mydrives << endl;
cVolSno = retVolSno(mydrives);
cout<<cVolSno<<endl;
++mydrives[1];
DriveMask >>= 1;
}
}
//std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
//cout<<cVolSno<<endl;
_getch();
return 0;
}
I've also tried replacing char with wchar_t, I didn't got any build errors, but while executing the application, got Error Code 3- Path not found!.
CODE MODIFIED:
// getVolDrive.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <sstream>
#include <string>
#include <ctype.h>
#include <algorithm>
using namespace std;
//wchar_t mydrives[5];// = " A: ";
char mydrives[] = " A:\\\\ ";
string retVolSno(char drives[]) //wchar_t drives[]
{
DWORD dwSerial;
stringstream ss;
wchar_t text[10];
mbstowcs(text,drives,100); //strlen(drives)+1
LPWSTR ptr = text;
if(!GetVolumeInformation(ptr, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
{
ss<<"Error: "<<GetLastError();
}
else
{
ss<<hex<<dwSerial;
}
return ss.str();
}
int _tmain(int argc, _TCHAR* argv[])
{
string cVolSno;
ULONG DriveMask = _getdrives();
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
else
{
printf("This machine has the following logical drives:\n");
while (DriveMask)
{
if(DriveMask & 1)
printf("%s \n", mydrives);
cVolSno = retVolSno(mydrives);
std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
cout<<cVolSno<<endl;
++mydrives[1];
DriveMask >>= 1;
}
}
//std::transform(cVolSno.begin(), cVolSno.end(),cVolSno.begin(), ::toupper);
//cout<<cVolSno<<endl;
_getch();
return 0;
}
OUTPUT:
This machine has the following logical drives:
ERROR: 123
ERROR: 123
C:\\
ERROR: 123
D:\\
ERROR: 123
E:\\
ERROR: 123
I see at least these main issues:
1) wchar_t is the right type because you're compiling for UNICODE, you can write generic code using TCHAR macro or explicitly declare your buffer as wchar_t but that's what to do.
2) You have that error because you're passing wrong path to GetVolumeInformation() (trailing backslash is required so A: must become A:\).
Moreover please note that you have a little bit more easy way to achieve same result, you can use GetLogicalDriveStrings() to directly get a NULL delimited string list. Split it using, for example, this (don't forget UNICODE) and use c_str() with each entry.
EDIT about your modified code:
Why you drive path is A:\\ (escaped to A:\\\\)? Just one trailing backslash is needed so mydrives has to be declared as:
wchar_t mydrives[] = L"A:\\";
EDIT 2: there are more errors in your code so I'll post a reviewed version. There are more things I'd change but I'll point out just what doesn't actually work.
Function retVolSno to read volume serial number. Original version were almost right, in your modified version you perform useless character conversion. What you had to do was just to accept a wchar_t drive path.
Global variable mydrives. You actually don't need any global variable for that. It must be wchar_t and space before/after path are useless. One trailing backslash is needed. Line where you increment character value (++mydrives[0];) must be changed accordingly (index 0 instead of 1).
Check for drive availability. After if(DriveMask & 1) you did forget { then you won't print drive name but you'll perform GetVolumeInformation() even on unavailable drives (error 123). That's why indentation is important...
You're mixing UNICODE/NOT UNICODE and C/C++ stuff. I strongly suggest you pick one of them and you keep it (C or C++? UNICODE or NOT UNICODE?). For example you used C function printf() to print stuff and you have both std::string and wchar_t things.
Let's put everything together to have a working version. First the function to read serial number given drive path:
wstring getVolumeSerialNumber(const wchar_t* drivePath)
{
DWORD dwSerial;
wstringstream ss;
if (!GetVolumeInformation(drivePath, NULL, 0, &dwSerial, NULL, NULL, NULL, 0))
ss << L"Error: " << GetLastError();
else
ss << hex << dwSerial;
return ss.str();
}
It's almost the same as your original version, just changed to work with UNICODE characters. Then main function that cycles through available drives and print out their serial number:
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t drive[] = L"A:\\";
ULONG driveMask = _getdrives();
if (driveMask == 0)
wcout << L"_getdrives() failed with failure code: " << GetLastError() << endl;
else
{
wcout << L"This machine has the following logical drives:" << endl;
while (driveMask)
{
if (driveMask & 1)
{
wcout << drive << endl;
wcout << getVolumeSerialNumber(drive) << endl;
}
++drive[0];
driveMask >>= 1;
}
}
wcin.ignore();
return 0;
}
From the documentation , the first parameters should be with trailing slash if drive letter is passed.
lpRootPathName [in, optional]
A pointer to a string that contains the root directory of the volume to be described.
If this parameter is NULL, the root of the current directory is used.
A trailing backslash is required.
For example, you specify \\MyServer\MyShare as \\MyServer\MyShare\, or the C drive as C:\

Resources