Since March 10, 2003 - Version 2.1
hypothetic.org

MSN Messenger Protocol

Home Page

Forum

About
Contact us

Resources

Research

Documentation
 General
 Notification
 Switchboard
 Client
 Reference

Validate XHTML
Validate CSS
Forum - Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Printable Version

Download the Entire Forum | RSS Feed
 New Topic  |  Go to Forum Index  |  Go to Topic  |  Search  |  Log In 
 Newer Topic  |  Older Topic 
 Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: Skywing 
Date:   10-22-03 20:20 (5021 days ago)

I'm fairly certain that I've worked out all the bugs in my TWN authentication provider, so I'm posting it to assist people in porting their clients to use MSNP8 and MSNP9.

PassportSecurity is a class for managing asynchronous authentication for TWN using WinInet. The class is designed to operate in a completely asynchronous manner, and does not block the caller under any circumstances.

The class is derived from an abstract MessengerSecurity base class. MessengerSecurity provides a generic, high-level asynchronous interface for performing authentication for the MSN Messenger protocol. It's structured so that the user of the class does not need to know any details of the authentication/security package implementation.

Documentation on using MessengerSecurity:

MessengerSecurity() - Constructor, no parameters. A MessengerSecurity security package must be allocated via the new operator; it is an error to declare an automatic instance of a MessengerSecurity security package.

void Release() - Decrements the reference count of the underlying security package. This is necessary as due to the asynchronous nature of some security packages (i.e. PassportSecurity), multiple threads may hold references to the security package at once. Release() should be called after you are finished using the security package.

void SetCallbackInformation(MessengerSecurityCallback _Callback, LPARAM _CallbackParam) - Sets a callback and a pointer-sized user-defined-parameter (this parameter is not interpreted by the security package and is passed directly to the callback routine) that is used to communicate the result of an authentication operation back to the user of the security package. This is guaranteed to be executed in the context of the thread that created the security package instance. To this end, the thread that created the security package should be alertable (able to receive APCs).

bool GetChallengeData(std::string& _ChallengeData) - Retrieves the information needed to be passed to the notification server to complete the authentication process. Returns true on success, false on failure. This is typically called from the MessengerSecurityCallback procedure registered by the user. GetChallengeData should not be called until notification of success or failure of the authentication operation has been received via the registered callback.

bool GetFailureDescription(std::string& _Description) - Retrieves a human-readable string explaining why an authentication operation failed. This should only be called if the authentication operation failed.

LPCSTR GetPackageCode() - Returns the name of the authentication package as it appears in the MSN protocol, e.g. "MD5" or "TWN".

bool RequestSigninData(const std::string& _AccountName, const std::string& _AccountPassword, const std::string& _ChallengeData) - Initiates the security-package-defined authentication process. Returns true if the operation was successfully initiated, or false if an error occured.

void CancelPending() - Cancels a pending authentication operation.

Note that the destructor is not public. Users must not directly delete a security provider object, but must instead use the Release function.

The protected and private members of security packages should not be used directly.

------------------------------

The interface definitions:



/* Copyright (C) 2003 Skywing. You are free to use these routines in your own program, but no warranty is provided and you may not remove the copyright notice from the interface source code and the implementation source code. Additionally, you may not take credit for writing this code. */

class MessengerSecurity {

public:

MessengerSecurity() : ReferenceCount(1) {}

virtual void Release()
{
RemoveReference();
}

typedef VOID (CALLBACK * MessengerSecurityCallback)(bool Success,
MessengerSecurity* Security, LPARAM Param);

virtual void SetCallbackInformation(MessengerSecurityCallback _Callback,
LPARAM _CallbackParam) = 0;

virtual bool GetChallengeData(std::string& _ChallengeData) = 0;
virtual bool GetFailureDescription(std::string& _Description) = 0;
virtual LPCSTR GetPackageCode() = 0;

virtual bool RequestSigninData(const std::string& _AccountName, const std::string& _AccountPassword,
const std::string& _ChallengeData) = 0;

virtual void CancelPending() = 0;

protected:

virtual ~MessengerSecurity() {}

LONG AddReference()
{
return InterlockedIncrement(&ReferenceCount);
}

LONG RemoveReference()
{
LONG References = InterlockedDecrement(&ReferenceCount);

if(!References)
delete this;

return References;
}

LONG ReferenceCount;

};

class PassportSecurity : public MessengerSecurity {

public:

enum StateEnum {
ClosedRequest,
ConnectRequest,
OpenRequest,
SendRequest,
EndRequest,
ReadResponse,
CloseRequestFailure,
CloseRequestSuccess
};

enum RequestEnum {
RequestUnknown,
RequestPassportRedirect,
RequestPassportSignin
};

private:

virtual ~PassportSecurity()
{
CancelPending();
InternetSetStatusCallback(WinInet, 0);

if(OwnerThread)
CloseHandle(OwnerThread);

if(OwnWinInet)
InternetCloseHandle(WinInet);
}

void OnRedirectConnect();
void OnRedirectOpen();
void OnRedirectSend();
void OnRedirectEnd();
void OnRedirectRead();
void OnRedirectSuccess();

void OnSigninConnect();
void OnSigninOpen();
void OnSigninSend();
void OnSigninEnd();
void OnSigninRead();
void OnSigninSuccess();
void OnSigninRedirect(const std::string& RedirectURI);

HINTERNET WinInet;
HINTERNET ServerHandle;
HINTERNET RequestHandle;
INTERNET_BUFFERS RequestBuffers;
LPBYTE ResponseBuffer;
DWORD ResponseBytesTotal, ResponseBytesRead;

HANDLE OwnerThread;

StateEnum State;
RequestEnum Request;

std::string AccountName, AccountPassword, ChallengeData, LoginPath, Description;

MessengerSecurityCallback Callback;
LPARAM CallbackParam;

bool OwnWinInet;

public:

class WinInetInitFailure {};

PassportSecurity(HINTERNET _WinInet = 0) : WinInet(_WinInet), ServerHandle(0), RequestHandle(0),
State(ClosedRequest), OwnerThread(0), Request(RequestUnknown), Callback(0), CallbackParam(0),
ResponseBuffer(0)
{
OwnWinInet = !WinInet;

if(OwnWinInet) {
WinInet = InternetOpen(MSNCLIENT_NAME, INTERNET_OPEN_TYPE_PRECONFIG, 0, 0,
INTERNET_FLAG_ASYNC);
if(!WinInet)
throw WinInetInitFailure();
}

ZeroMemory(&RequestBuffers, sizeof(RequestBuffers));

RequestBuffers.dwStructSize = sizeof(RequestBuffers);

DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &OwnerThread, 0,
FALSE, DUPLICATE_SAME_ACCESS);
InternetSetStatusCallback(WinInet, StatusCallback);
}

virtual void SetCallbackInformation(MessengerSecurityCallback _Callback, LPARAM _CallbackParam)
{
Callback = _Callback;
CallbackParam = _CallbackParam;
}

virtual bool GetChallengeData(std::string& _ChallengeData)
{
if(Request != RequestPassportSignin && State != CloseRequestSuccess)
return false;

_ChallengeData = ChallengeData;

return true;
}

virtual bool GetFailureDescription(std::string& _Description)
{
if(Description.size()) {
_Description = Description;
return true;
} else
return false;
}

virtual LPCSTR GetPackageCode()
{
return "TWN";
}

virtual bool RequestSigninData(const std::string& _AccountName, const std::string& _AccountPassword,
const std::string& _ChallengeData);

virtual void CancelPending()
{
if(RequestHandle) {
InternetSetStatusCallback(RequestHandle, 0);
InternetCloseHandle(RequestHandle);
RequestHandle = 0;
}

if(ServerHandle) {
InternetSetStatusCallback(ServerHandle, 0);
InternetCloseHandle(ServerHandle);
ServerHandle = 0;
}

if(ResponseBuffer) {
delete [] ResponseBuffer;
ResponseBuffer = 0;
}

Request = RequestUnknown;
State = ClosedRequest;

Description.clear();
}

private:

static VOID APIENTRY StatusAPC(ULONG_PTR Context);
static VOID CALLBACK StatusCallback(IN HINTERNET hInternet, IN DWORD_PTR dwContext,
IN DWORD dwInternetStatus, IN LPVOID lpvStatusInformation OPTIONAL,
IN DWORD dwStatusInformationLength);
};


----------------

Implementation:



/* Copyright (C) 2003 Skywing. You are free to use these routines in your own program, but no warranty is provided and you may not remove the copyright notice from the interface source code and the implementation source code. Additionally, you may not take credit for writing this code. */

// namespace std should be selected into the global namespace

void PassportSecurity::OnRedirectConnect()
{
LPCSTR AcceptTypes[] = {"text/plain", 0};

State = OpenRequest;
RequestHandle = HttpOpenRequest(ServerHandle, "GET", "/rdr/pprdr.asp", "HTTP/1.1", 0, AcceptTypes,
INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE
| INTERNET_FLAG_SECURE, (DWORD_PTR)this);

if(RequestHandle) {
InternetSetStatusCallback(RequestHandle, StatusCallback);
OnRedirectOpen();
} else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}
}

void PassportSecurity::OnRedirectOpen()
{
State = SendRequest;

ZeroMemory(&RequestBuffers, sizeof(RequestBuffers));

RequestBuffers.dwStructSize = sizeof(RequestBuffers);

if(HttpSendRequestEx(RequestHandle, &RequestBuffers, 0, HSR_ASYNC | HSR_INITIATE,
(DWORD_PTR)this))
OnRedirectSend();
else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}
}

void PassportSecurity::OnRedirectSend()
{
State = EndRequest;

if(HttpEndRequest(RequestHandle, 0, HSR_ASYNC | HSR_INITIATE, (DWORD_PTR)this))
OnRedirectEnd();
else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
} else
return;
}

void PassportSecurity::OnRedirectEnd()
{
State = CloseRequestSuccess;

static const char PassportURLsStr[] = "PassportURLs";

DWORD Size = 0;
DWORD Index = 0;

if(HttpQueryInfo(RequestHandle, HTTP_QUERY_CUSTOM, (LPVOID)PassportURLsStr, &Size, &Index)
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

if(Size < sizeof(PassportURLsStr))
Size = sizeof(PassportURLsStr);
else
Size++;

Index = 0;

ResponseBuffer = new BYTE[Size+1]; // For null termination purposes

if(!ResponseBuffer) {
State = CloseRequestFailure;
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
AddReference();
return;
}

memcpy(ResponseBuffer, PassportURLsStr, sizeof(PassportURLsStr));

if(!HttpQueryInfo(RequestHandle, HTTP_QUERY_CUSTOM, ResponseBuffer, &Size, &Index)) {
State = CloseRequestFailure;
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
AddReference();
return;
}

ResponseBuffer[Size] = '\0';
OnRedirectSuccess();
/*
ResponseBytesRead = 0;
State = ReadResponse;

ZeroMemory(&RequestBuffers, sizeof(RequestBuffers));

RequestBuffers.dwStructSize = sizeof(RequestBuffers);
RequestBuffers.dwBufferTotal = RequestBuffers.dwBufferLength = ResponseBytesRead;
RequestBuffers.lpvBuffer = (LPVOID)ResponseBuffer;

if(InternetReadFileEx(RequestHandle, &RequestBuffers, IRF_ASYNC, (DWORD_PTR)this))
OnRedirectRead();
else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this); // Will deallocate ResponseBuffer
return;
}*/
}

void PassportSecurity::OnRedirectRead()
{
for(;;) {
ResponseBytesRead += RequestBuffers.dwBufferLength;

if(!RequestBuffers.dwBufferLength || ResponseBytesRead == ResponseBytesTotal) {
ResponseBuffer[ResponseBytesRead] = '\0'; // OK since we allocated one more byte
OnRedirectSuccess();
return;
}

// dumphex((char*)ResponseBuffer, ResponseBytesRead, -1);

RequestBuffers.dwBufferTotal = RequestBuffers.dwBufferLength = ResponseBytesRead
- ResponseBytesTotal;
RequestBuffers.lpvBuffer = (LPVOID)&ResponseBuffer[ResponseBytesRead];

if(InternetReadFileEx(RequestHandle, &RequestBuffers, IRF_ASYNC, (DWORD_PTR)this))
continue;
else if(GetLastError() == ERROR_IO_PENDING)
return;
else {
State = CloseRequestFailure;
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
AddReference();
return;
}
}
}

void PassportSecurity::OnRedirectSuccess()
{
static const char DALoginStr[] = "dalogin=";

for(char* Token = strtok((char*)ResponseBuffer, ","); Token; Token = strtok(0, ",")) {
if(!memicmp(Token, DALoginStr, sizeof(DALoginStr)-1)) {
char* Slash = strchr((char*)Token+sizeof(DALoginStr)-1, '/');

if(Slash) {
LoginPath = Slash;
*Slash = '\0';
} else
LoginPath = "/";

string LoginServer = (char*)Token+sizeof(DALoginStr)-1; // CancelPending frees it

CancelPending();

State = ConnectRequest;
Request = RequestPassportSignin;

ServerHandle = InternetConnect(WinInet, LoginServer.c_str(), INTERNET_DEFAULT_HTTPS_PORT,
0, 0, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)this);

if(ServerHandle) {
InternetSetStatusCallback(ServerHandle, StatusCallback);
OnSigninConnect();
return;
} else if(GetLastError() == ERROR_IO_PENDING)
return; // Wait for WinInet to notify us of things happening
else
break; // Failure notification handled after loop exits
}
}

State = CloseRequestFailure; // Need a DALogin field!
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
AddReference();
}

void PassportSecurity::OnSigninConnect()
{
LPCSTR AcceptTypes[] = {"text/plain", 0};

State = OpenRequest;
RequestHandle = HttpOpenRequest(ServerHandle, "GET", LoginPath.c_str(), "HTTP/1.1", 0,
AcceptTypes, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE
| INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_AUTO_REDIRECT, (DWORD_PTR)this);

if(RequestHandle) {
InternetSetStatusCallback(RequestHandle, StatusCallback);
OnSigninOpen();
} else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}
}

void PassportSecurity::OnSigninOpen()
{
State = SendRequest;

ZeroMemory(&RequestBuffers, sizeof(RequestBuffers));

ResponseBuffer = new BYTE[256+AccountName.size()+AccountPassword.size()+ChallengeData.size()];

if(!ResponseBuffer) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

RequestBuffers.dwHeadersLength = (DWORD)wsprintfA((LPSTR)ResponseBuffer, "Authorization: "
"Passport1.4 OrgVerb=GET,OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,sign-in=%s,pwd=%s,%s",
AccountName.c_str(), AccountPassword.c_str(), ChallengeData.c_str());

RequestBuffers.dwStructSize = sizeof(RequestBuffers);
RequestBuffers.lpcszHeader = (LPCSTR)ResponseBuffer;

if(HttpSendRequestEx(RequestHandle, &RequestBuffers, 0, HSR_ASYNC | HSR_INITIATE,
(DWORD_PTR)this))
OnSigninSend();
else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}
}

void PassportSecurity::OnSigninSend()
{
State = EndRequest;

if(ResponseBuffer) {
delete [] ResponseBuffer;
ResponseBuffer = 0;
}

if(HttpEndRequest(RequestHandle, 0, HSR_ASYNC | HSR_INITIATE, (DWORD_PTR)this))
OnSigninEnd();
else if(GetLastError() != ERROR_IO_PENDING) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
} else
return;
}

void PassportSecurity::OnSigninEnd()
{
State = CloseRequestSuccess;

DWORD StatusCode;
DWORD Size = sizeof(StatusCode);

if(!HttpQueryInfo(RequestHandle, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &StatusCode,
&Size, 0)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

switch(StatusCode) {

case 200: // OK
break;

case 302: // Redirect
{
Size = 0;

if(HttpQueryInfo(RequestHandle, HTTP_QUERY_LOCATION, 0, &Size, 0)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
} else {
switch(GetLastError()) {

case ERROR_INSUFFICIENT_BUFFER:
break;

default:
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;

}
}

ResponseBuffer = new BYTE[Size+1]; // For null termination purposes

if(!ResponseBuffer) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

if(!HttpQueryInfo(RequestHandle, HTTP_QUERY_LOCATION, ResponseBuffer, &Size, 0)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

ResponseBuffer[Size] = '\0';
OnSigninRedirect((char*)ResponseBuffer);
}
return;

default:
{
Size = 0;

if(HttpQueryInfo(RequestHandle, HTTP_QUERY_WWW_AUTHENTICATE, 0, &Size, 0)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
} else {
switch(GetLastError()) {

case ERROR_INSUFFICIENT_BUFFER:
break;

default:
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;

}
}

ResponseBuffer = new BYTE[Size+1]; // For null termination purposes

if(!ResponseBuffer) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

if(!HttpQueryInfo(RequestHandle, HTTP_QUERY_WWW_AUTHENTICATE, ResponseBuffer, &Size, 0)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

ResponseBuffer[Size] = '\0';

static const char ErrorDescriptionStr[] = "cbtxt=";

for(char* Token = strtok((char*)ResponseBuffer, ","); Token; Token = strtok(0, ",")) {
if(!memicmp(Token, ErrorDescriptionStr, sizeof(ErrorDescriptionStr)-1)) {
URLDecode(Token+sizeof(ErrorDescriptionStr)-1, Description);
break;
}
}

delete [] ResponseBuffer;
ResponseBuffer = 0;

State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

}

bool GotAllCookies = false;

for(DWORD Index = 0; !GotAllCookies; ) {
Size = 0;

static const char AuthInfoStr[] = "Authentication-Info";

if(HttpQueryInfo(RequestHandle, HTTP_QUERY_CUSTOM, (LPVOID)AuthInfoStr, &Size, &Index)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
} else {
switch(GetLastError()) {

case ERROR_INSUFFICIENT_BUFFER:
break;

case ERROR_HTTP_HEADER_NOT_FOUND:
GotAllCookies = true;
continue;

default:
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;

}
}

if(Size < sizeof(AuthInfoStr))
Size = sizeof(AuthInfoStr);
else
Size++;

ResponseBuffer = new BYTE[Size+1]; // For null termination purposes

memcpy(ResponseBuffer, AuthInfoStr, sizeof(AuthInfoStr));

if(!ResponseBuffer) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

if(!HttpQueryInfo(RequestHandle, HTTP_QUERY_CUSTOM, ResponseBuffer, &Size, &Index)) {
State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
return;
}

ResponseBuffer[Size] = '\0';

OnSigninSuccess();
return;
}

State = CloseRequestSuccess;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
}

void PassportSecurity::OnSigninSuccess()
{
static const char DAStatusStr[] = "Passport1.4 da-status=";
static const char FromPPStr[] = "from-PP=";

static const char SuccessStr[] = "success";

bool Successful = false;
ChallengeData.clear();

for(char* Token = strtok((char*)ResponseBuffer, ","); Token; Token = strtok(0, ",")) {
if(!memicmp(Token, DAStatusStr, sizeof(DAStatusStr)-1)) {
Successful = !memicmp(Token+sizeof(DAStatusStr)-1, SuccessStr, sizeof(SuccessStr)-1);

if(!Successful)
break;
else if(ChallengeData.size())
break;
} else if(!memicmp(Token, FromPPStr, sizeof(FromPPStr)-1)) {
Token += sizeof(FromPPStr)-1;

char* BeginQuote = strchr(Token, '\'');

if(!BeginQuote) {
Successful = false;
break;
}

char* EndQuote = strrchr(++BeginQuote, '\'');

if(!EndQuote) {
Successful = false;
break;
}

*EndQuote = '\0';

ChallengeData = BeginQuote;

if(Successful)
break;
}
}

State = Successful ? CloseRequestSuccess : CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
}

void PassportSecurity::OnSigninRedirect(const string& RedirectURI)
{
CancelPending();

State = ConnectRequest;
Request = RequestPassportSignin;

const char* URI = RedirectURI.c_str();
const char* Server = strstr(URI, "://");

if(!Server)
Server = URI;
else
Server += 3;

const char* ServerEnd = strchr(Server, '/');

if(!ServerEnd) {
ServerEnd = Server+strlen(Server);
LoginPath = "/";
} else
LoginPath = ServerEnd;

size_t ServerSize = ServerEnd-Server;
char* LoginServer = (char*)alloca((ServerSize+1)*sizeof(char));
memcpy(LoginServer, Server, ServerSize);
LoginServer[ServerSize] = '\0';

ServerHandle = InternetConnect(WinInet, LoginServer, INTERNET_DEFAULT_HTTPS_PORT,
0, 0, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)this);

if(ServerHandle) {
InternetSetStatusCallback(ServerHandle, StatusCallback);
OnSigninConnect();
return;
} else if(GetLastError() == ERROR_IO_PENDING)
return; // Wait for WinInet to notify us of things happening

State = CloseRequestFailure;
AddReference();
QueueUserAPC(StatusAPC, OwnerThread, (ULONG_PTR)this);
}



VOID APIENTRY PassportSecurity::StatusAPC(ULONG_PTR Context)
{
PassportSecurity* CallbackData = (PassportSecurity*)Context;
bool RemoveTwo = false;

if(CallbackData->Callback) {
switch(CallbackData->State) {

case PassportSecurity::CloseRequestSuccess:
CallbackData->Callback(true, CallbackData, CallbackData->CallbackParam);
RemoveTwo = true; // Remove the "session" reference
break;

case PassportSecurity::CloseRequestFailure:
CallbackData->Callback(false, CallbackData, CallbackData->CallbackParam);
RemoveTwo = true; // Remove the "session" reference
break;

}
}

CallbackData->CancelPending();

if(CallbackData->RemoveReference() && RemoveTwo) // Remove the "APC" reference
CallbackData->RemoveReference();
}

VOID CALLBACK PassportSecurity::StatusCallback(IN HINTERNET hInternet, IN DWORD_PTR dwContext,
IN DWORD dwInternetStatus,
IN LPVOID lpvStatusInformation OPTIONAL,
IN DWORD dwStatusInformationLength)
{
PassportSecurity* CallbackData = (PassportSecurity*)dwContext;

switch(dwInternetStatus) {

case INTERNET_STATUS_HANDLE_CREATED:
{
INTERNET_ASYNC_RESULT& Result = *reinterpret_cast<LPINTERNET_ASYNC_RESULT>(lpvStatusInformation);
HINTERNET Created = (HINTERNET)Result.dwResult;

switch(CallbackData->State) {

case ConnectRequest:
CallbackData->ServerHandle = Created;
InternetSetStatusCallback(CallbackData->ServerHandle, StatusCallback);
break;

case OpenRequest:
CallbackData->RequestHandle = Created;
InternetSetStatusCallback(CallbackData->RequestHandle, StatusCallback);
break;

}
}
//CallbackData->AddReference();
return;

case INTERNET_STATUS_HANDLE_CLOSING:
//CallbackData->RemoveReference();
return;

case INTERNET_STATUS_REQUEST_COMPLETE:
{
if(dwStatusInformationLength < sizeof(INTERNET_ASYNC_RESULT))
return;

INTERNET_ASYNC_RESULT& Result = *reinterpret_cast<LPINTERNET_ASYNC_RESULT>(lpvStatusInformation);

if(Result.dwError) {
CallbackData->State = CloseRequestFailure;
CallbackData->AddReference();
QueueUserAPC(StatusAPC, CallbackData->OwnerThread, (ULONG_PTR)CallbackData);
return;
}

switch(CallbackData->Request) {

case RequestPassportRedirect:
switch(CallbackData->State) {

case ConnectRequest:
CallbackData->OnRedirectConnect();
break;

case OpenRequest:
CallbackData->OnRedirectOpen();
break;

case SendRequest:
CallbackData->OnRedirectSend();
break;

case EndRequest:
CallbackData->OnRedirectEnd();
break;

// case ReadResponse:
// CallbackData->OnRedirectRead();
// break;

}
break;

case RequestPassportSignin:
switch(CallbackData->State) {

case ConnectRequest:
CallbackData->OnSigninConnect();
break;

case OpenRequest:
CallbackData->OnSigninOpen();
break;

case SendRequest:
CallbackData->OnSigninSend();
break;

case EndRequest:
CallbackData->OnSigninEnd();
break;

}
break;

break;

}
}
break;

}
}

bool PassportSecurity::RequestSigninData(const string& _AccountName, const string& _AccountPassword,
const string& _ChallengeData)
{
CancelPending();

AccountName = _AccountName;
AccountPassword = _AccountPassword;
ChallengeData = _ChallengeData;

State = ConnectRequest;
Request = RequestPassportRedirect;
ServerHandle = InternetConnect(WinInet, "nexus.passport.com", INTERNET_DEFAULT_HTTPS_PORT, 0, 0,
INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)this);

if(ServerHandle) {
AddReference();
InternetSetStatusCallback(ServerHandle, StatusCallback);
OnRedirectConnect();
} else
return GetLastError() == ERROR_IO_PENDING;

return true;
}


- Skywing of Valhalla Legends.
Skywing's MSN Client is available at http://www.valhallalegends.com/skywing.

Post Edited (10-22-03 20:34)

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: Tommy (---.venlo1.lb.home.nl)
Date:   02-27-04 23:12 (4892 days ago)

Hi,

First of all, thanks for sharing this great class with all of us, it looks great!

However i have some problems using it.

-The function URLEncode is not declared anywhere, neither can i find it in the included headers. What file should i include for this function?

-I have no clue what to do with the callback function. What should it look like etc.

Finally, could you add an example on how to do the authen process with this code because i'm not sure how :$.

Thanks for all help!

Tommy

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: qFox 
Date:   02-28-04 11:10 (4892 days ago)

o dear
run for cover!

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: Skywing 
Date:   02-28-04 16:34 (4892 days ago)

inline std::string URLEncode(const std::string& Text)
{
std::string Result;
char Temp[4];

Temp[0] = '%';

for(std::string::const_iterator i = Text.begin(); i != Text.end(); ++i) {
if(*i) {
if(isspace(BYTE(*i)) || (BYTE(*i) & 0x80)) { // Spaces and extended characters must be encoded
wsprintfA(Temp+1, "%02x", (unsigned char)*i);
Result += Temp;
} else
Result += *i;
}
}

return Result;
}


I do something like:

MessengerSecurity* BaseNSSession::SelectSecurityPackage(LPCSTR SecurityPackage)
{
if(!CreateSecurityPackage(SecurityPackage)) /* Pick "MD5" or "TWN", else fail */
return 0;

m_SecurityPackage->SetCallbackInformation(SecurityPackageCompletion, (LPARAM)this); /* Set notify callback */

return m_SecurityPackage;
}


And...:

	static VOID CALLBACK SecurityPackageCompletion(bool Success, MessengerSecurity* Security,
LPARAM lParam)
{
reinterpret_cast<BaseNSSession*>(lParam)->OnSecurityPackageCompletion(Success);
}


int BaseNSSession::OnSecurityPackageCompletion(bool Success)
{
if(m_LoggedOn)
return 0;

if(!Success || !m_SecurityPackage)
return -1;

string Authorization;
if(!m_SecurityPackage->GetChallengeData(Authorization))
return -1;

size_t AuthorizationSize = Authorization.size()+1;

char* Params = (char*)alloca(AuthorizationSize+7);

memcpy(Params, m_SecurityPackage->GetPackageCode(), 3);
memcpy(Params+3, " S ", 3);
memcpy(Params+6, Authorization.c_str(), AuthorizationSize);

SendCommand("USR", Params);

return 0;
}


- Skywing of Valhalla Legends.
Skywing's MSN Client is available at http://www.valhallalegends.com/skywing.

Post Edited (02-28-04 16:36)

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: JDBarclay (200.173.29.---)
Date:   03-04-04 23:42 (4886 days ago)

Hi...
Wow! really really good, but let me ask a question(stupid question maybe)

Is this covering all the authentication process? I mean, why I don't see any "VER ..." or "CVR ..." commands?

Thanks

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: Skywing 
Date:   03-07-04 18:43 (4884 days ago)

This deals with the part of the logon process that involves communicating with the Passport nexus and logon server.

- Skywing of Valhalla Legends.
Skywing's MSN Client is available at http://www.valhallalegends.com/skywing.

Post Edited (03-07-04 18:44)

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: Davidl (---.jetstart.xtra.co.nz)
Date:   04-03-04 22:14 (4857 days ago)

I am creating a wxWindows (http://wxwindows.org) based MSN client for windows. I have used the callback example above, and use the library as follows:
(this code is called when a USR command is recieved)
-----------------
m_SecurityPackage=new PassportSecurity();
logevt("Authenticating with Passport...");
if (!m_SecurityPackage->RequestSigninData(usern,passw,sTemp))
{
wxMessageBox("Passport Authentication error.");
socket->Close();
return -1;
}

SleepEx(INFINITE,1);
logevt("Done waiting");
------------------
And the callback is:
static VOID CALLBACK SecurityPackageCompletion(bool Success, MessengerSecurity* Security,

LPARAM lParam)

{
wxMessageBox("Recieved Callback");
if (reinterpret_cast<MSNConnection*>(lParam)->OnSecurityPackageCompletion(Success))
{
logevt("Authenticated with passport");
}
else
wxBell();
wxMessageBox("Could not authenticate passport");

}
-----------------------
When I select signin in my app, and it reaches this part,
SleepEx waits for about 5 seconds (netstat shows connections to nexus.passport.com:https) but the callback is never called (as far as i know, at least the message box doesnt popup and nothing is sent to server.

What have i done wrong?
Thanks for any help.

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: Mephisto[zL] (---.ca.charter.com)
Date:   10-05-04 21:24 (4672 days ago)

><

Reply To This Message
 
 Re: Asynchronous TWN authentication class [C++, Win32, large amounts of code]
Author: cocoza (61.96.206.---)
Date:   12-22-04 10:10 (4594 days ago)

what is challenge data..

USR 3 TWN S lc=1033,id=507,tw=40,fs=1,ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1062764229,kpp=1,kv=5,ver=2.1.0173.1,tpf=43f8a4c8ed940c04e3740be46c4d1619\r\n

<- what's challenge data?

tpf=43f8a4c8ed940c04e3740be46c4d1619??

Reply To This Message
 Threaded View   Newer Topic  |  Older Topic