// dialog_registration.cpp
#include "../common.h"
#ifndef NO_REGISTRATION
// dialog template
#define THIS_DIALOG DIALOG_REGISTER
// global variables used in this module only
static wchar_t transaction_uuid[128] = L"";
static bool was_donatebutton_clicked = false;
static HWND hThisDialogWnd = NULL;
// prototypes of local functions
static void RegistrationCheckThread (void *thread_parms);
static int CALLBACK DialogProc_ThisDialog (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
static bool RetrieveActivationCode (wchar_t *candidate_email_or_uuid, unsigned int *returned_code, wchar_t **returned_email, wchar_t **failure_reason);
void DialogBox_Registration (void)
{
// helper function to fire up the modal dialog box
// first, test if our server is reachable. We DO NOT want the user to donate if our server isn't here to receive the donation!
if (!RetrieveActivationCode (NULL, NULL, NULL, NULL))
return; // if it's not, don't display anything
// generate a random transaction UUID for this session
srand ((unsigned int) time (NULL));
swprintf_s (transaction_uuid, sizeof (transaction_uuid), L"%04x%04x-%04x-%04x-%04x-%04x%04x%04x", rand (), rand (), rand (), (rand () & 0x0fff) | 0x4000, (rand () & 0x3fff) | 0x8000, rand (), rand (), rand ());
// start a new thread that will ask every 10 seconds if our transaction ID has donated
_beginthread (RegistrationCheckThread, 0, NULL); // fire up the thread
// display the dialog box (set the parent to be the desktop)
DialogBox (hAppInstance, MAKEINTRESOURCE (THIS_DIALOG), NULL, DialogProc_ThisDialog);
return; // finished processing this dialog
}
static void RegistrationCheckThread (void *thread_parms)
{
// thread entrypoint for the thread that periodically checks whether transaction_uuid has just donated
unsigned int returned_code; // 32 bits minimum
wchar_t *returned_email;
// loop endlessly
for (;;)
{
// query the remote server for the code associated with this transaction UUID
if (RetrieveActivationCode (transaction_uuid, &returned_code, &returned_email, NULL) && IsRegistrationCorrect (returned_email, returned_code))
break; // stop looping as soon as we get correct values
Sleep (10 * 1000); // next check in 10 seconds
}
// the user just registered, save activation email and activation code
wcscpy_s (options.registration.user_email, WCHAR_SIZEOF (options.registration.user_email), returned_email);
options.registration.activation_code = returned_code;
if (IsWindow (hThisDialogWnd))
ShowWindow (hThisDialogWnd, SW_HIDE); // make the dialog box disappear
MessageBox (NULL, LOCALIZE (L"Registration_ThankYou"), PROGRAM_NAME, MB_ICONINFORMATION | MB_OK); // display a "thank you" message
was_donatebutton_clicked = false; // prevent the reminder dialog box to open
if (IsWindow (hThisDialogWnd))
PostMessage (hThisDialogWnd, WM_CLOSE, 0, 0); // then close the dialog box
return; // _endthread() implied
}
static int CALLBACK DialogProc_ThisDialog (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
// message handler for the dialog box
static wchar_t temp_string[1024];
unsigned short wParam_hiword;
unsigned short wParam_loword;
unsigned int returned_code; // 32 bits minimum
wchar_t *failure_reason;
int write_index;
int read_index;
int length;
// filter out the commonly used message values
wParam_hiword = HIWORD (wParam);
wParam_loword = LOWORD (wParam);
// have we just fired up this window ?
if (message == WM_INITDIALOG)
{
hThisDialogWnd = hWnd; // save the dialog handle
// center the window
CenterWindow (hWnd, hMainWnd);
// set dialog icons (small one for title bar & big one for task manager)
SendMessage (hWnd, WM_SETICON, ICON_SMALL, (LPARAM) LoadIcon (hAppInstance, MAKEINTRESOURCE (ICON_MAIN)));
SendMessage (hWnd, WM_SETICON, ICON_BIG, (LPARAM) LoadIcon (hAppInstance, MAKEINTRESOURCE (ICON_MAIN)));
// set window title and control texts
SetWindowText (hWnd, LOCALIZE (L"Registration_Title"));
// set the static texts
Static_SetText (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_QUESTION), LOCALIZE (L"Registration_Question"));
Static_SetText (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_INSTRUCTIONS), LOCALIZE (L"Registration_Instructions"));
// set email address font
SendMessage (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS), WM_SETFONT, (WPARAM) GetStockObject (SYSTEM_FONT), 0);
if (options.registration.user_email[0] != 0)
SetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, options.registration.user_email);
else
SetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, LOCALIZE (L"Registration_YourEmailHere"));
SetWindowText (GetDlgItem (hWnd, BUTTON_VALIDATECODE), LOCALIZE (L"Registration_ValidateCode"));
Static_SetText (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_STATUSBAR), LOCALIZE (L"Registration_StatusBar"));
// set focus to the first settable item
SendMessage (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS), EM_SETSEL, 0, -1); // select all text
SetFocus (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS));
// convert the Paypal bitmap and the status bar message to hyperlinks
ConvertStaticToHyperlink (GetDlgItem (hWnd, BUTTON_DONATE));
ConvertStaticToHyperlink (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_STATUSBAR));
}
// else did we click the close button on the title bar ?
else if (message == WM_CLOSE)
{
if (was_donatebutton_clicked)
{
MessageBox (hWnd, LOCALIZE (L"Registration_DontForget"), PROGRAM_NAME, MB_ICONWARNING | MB_OK); // users need to be reminded that they MUST write their email in the activation form
SendMessage (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS), EM_SETSEL, 0, -1); // select all text
SetFocus (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS)); // focus on the email address field
was_donatebutton_clicked = false; // once is enough
return (true); // prevent window close this time
}
else
EndDialog (hWnd, 0); // close the dialog box
hThisDialogWnd = NULL; // this window is about to no longer have a handle, so remember it
}
// else did we take action on one of the controls ?
else if (message == WM_COMMAND)
{
// did we cancel the dialog box ? (IDCANCEL is a system-wide dialog box handler value, that catches the ESC key)
if (wParam_loword == IDCANCEL)
EndDialog (hWnd, 0); // close the dialog box
// else did the user enter something in the edit boxes ?
else if (wParam_hiword == EN_CHANGE)
{
// enable the validation button only if the PayPal email field has data
GetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, temp_string, WCHAR_SIZEOF (temp_string));
// strip spaces and unprintable characters from candidate email, and bring it lowercase
length = wcslen (temp_string);
write_index = 0;
for (read_index = 0; read_index < length; read_index++)
if (!iswspace (temp_string[read_index]))
{
temp_string[write_index] = tolower (temp_string[read_index]); // force lowercase
write_index++; // keep only valid signs and discard spaces
}
temp_string[write_index] = 0; // ensure string is correctly terminated
EnableWindow (GetDlgItem (hWnd, BUTTON_VALIDATECODE), ((temp_string[0] != 0) && (wcslen (temp_string) > 5) && (wcscmp (temp_string, LOCALIZE (L"Registration_YourEmailHere")) != 0)
&& (wcschr (temp_string, L'@') != NULL) && (wcschr (temp_string, L'.') != NULL)));
}
// else was it the validation button ?
else if (wParam_loword == BUTTON_VALIDATECODE)
{
// enable the validation button only if the PayPal email field has data
GetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, temp_string, WCHAR_SIZEOF (temp_string));
// query the remote server for the code associated with this email. If it fails, ask why.
if (!RetrieveActivationCode (temp_string, &returned_code, NULL, &failure_reason)
|| ((failure_reason != NULL) && (failure_reason[0] != 0)))
MessageBox (hWnd, failure_reason, PROGRAM_NAME, MB_ICONERROR | MB_OK); // registration failed. Display an error and DON'T make the dialog box disappear
// else is the retrieved data correct ?
else if (IsRegistrationCorrect (temp_string, returned_code))
{
// if so, save activation email and activation code
wcscpy_s (options.registration.user_email, WCHAR_SIZEOF (options.registration.user_email), temp_string);
options.registration.activation_code = returned_code;
MessageBox (hWnd, LOCALIZE (L"Registration_ThankYou"), PROGRAM_NAME, MB_ICONINFORMATION | MB_OK);
was_donatebutton_clicked = false; // once is enough
EndDialog (hWnd, 0); // then display a thank you dialog box and make the dialog box disappear
}
// else the supplied data is wrong
else
{
swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Registration_Error"), AUTHOR_EMAIL);
MessageBox (hWnd, temp_string, PROGRAM_NAME, MB_ICONERROR | MB_OK); // wrong activation. Display an error and DON'T make the dialog box disappear
}
}
// else was it the PayPal button ?
else if (wParam_loword == BUTTON_DONATE)
{
MessageBox (hWnd, LOCALIZE (L"Registration_DontForget"), PROGRAM_NAME, MB_ICONWARNING | MB_OK); // users need to be reminded that they MUST write their email in the activation form
if (wcscmp (languages[language_id].name, L"French") == 0)
wcscpy_s (temp_string, sizeof (temp_string), PAYPAL_URL_FR); // select the French PayPal page
else
wcscpy_s (temp_string, sizeof (temp_string), PAYPAL_URL_XX); // select the international (English) PayPal page
wcscat_s (temp_string, sizeof (temp_string), transaction_uuid); // in all cases, append the transaction UUID that our server generated for us
ShellExecute (NULL, L"open", temp_string, NULL, NULL, SW_MAXIMIZE); // open PayPal in the default browser, maximized
was_donatebutton_clicked = true; // remember the user clicked the Donate button
}
// else was it the status bar hyperlink ?
else if (wParam_loword == STATICTEXT_REGISTRATION_STATUSBAR)
ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the main page in the default browser, maximized
}
// call the default dialog message processing function to keep things going
return (false);
}
static bool RetrieveActivationCode (wchar_t *candidate_email_or_uuid, unsigned int *returned_code, wchar_t **returned_email, wchar_t **failure_reason)
{
// this function queries the remote server for registration status. If OK, the function returns TRUE and
// the code and email are returned in the returned_code and returned_email pointers, and failure_reason
// is set to point to a NULL string. On error, FALSE is returned and failure_reason points to the cause
// of the error (localized). If candidate_email_or_uuid is a NULL string, only a server reachability test
// is performed.
#define REGISTRATION_HOST "pmbaty.com"
#define REGISTRATION_SCRIPT "/chess/whatsmycode.php"
static char http_buffer[1024]; // used both for request and reply
static char ascii_email_or_uuid[128];
static wchar_t wide_email[128];
struct sockaddr_in service;
struct hostent *hostinfo;
char *data_start;
int write_index;
int read_index;
int length;
SOCKET s;
// get our distribution server's IP address from the host name
hostinfo = gethostbyname (REGISTRATION_HOST);
if (hostinfo == NULL)
{
if (failure_reason != NULL)
*failure_reason = LOCALIZE (L"Registration_NetworkFailure");
return (false); // couldn't resolve hostname, return an error condition
}
// fill in the sockaddr server structure with the server hostinfo data
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr (inet_ntoa (*(struct in_addr *) hostinfo->h_addr_list[0]));
service.sin_port = htons (80); // connect to webserver there (port 80)
// create our socket
if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
if (failure_reason != NULL)
*failure_reason = LOCALIZE (L"Registration_NetworkFailure");
return (0); // couldn't create socket, return an error condition
}
// connect to the distributor's webserver using that socket
if (connect (s, (struct sockaddr *) &service, sizeof (service)) == -1)
{
closesocket (s);
if (failure_reason != NULL)
*failure_reason = LOCALIZE (L"Registration_NetworkFailure");
return (0); // unable to connect to webserver, return an error condition
}
// build the HTTP query and send it
if (candidate_email_or_uuid != NULL)
ConvertTo7BitASCII (ascii_email_or_uuid, sizeof (ascii_email_or_uuid), candidate_email_or_uuid);
sprintf_s (http_buffer, sizeof (http_buffer),
"GET " REGISTRATION_SCRIPT "?%s=%s HTTP/1.1\r\n"
"Host: " REGISTRATION_HOST "\r\n"
"Connection: close\r\n"
"\r\n",
(candidate_email_or_uuid != NULL ? (wcschr (candidate_email_or_uuid, '@') != NULL ? "email" : "uuid") : "test"),
(candidate_email_or_uuid != NULL ? ascii_email_or_uuid : "1"));
length = strlen (http_buffer);
write_index = send (s, http_buffer, length, 0); // send the HTTP query
if (write_index != length)
{
closesocket (s);
if (failure_reason != NULL)
*failure_reason = LOCALIZE (L"Registration_NetworkFailure");
return (0); // unable to send HTTP query, return an error condition
}
// read the reply (10 seconds timeout)
http_buffer[0] = 0;
read_index = RecvWithTimeout (s, 10.0f, http_buffer, sizeof (http_buffer), 0);
if (read_index < 1)
{
closesocket (s);
if (failure_reason != NULL)
*failure_reason = LOCALIZE (L"Registration_NetworkFailure");
return (0); // empty or erroneous HTTP reply, return an error condition
}
closesocket (s); // finished communicating, close TCP socket
// terminate recv buffer and see where the useful data starts
http_buffer[read_index] = 0;
//MessageBoxA (NULL, http_buffer, "HTTP response", MB_OK);
if (candidate_email_or_uuid == NULL)
{
if (failure_reason != NULL)
*failure_reason = L""; // no error, set an empty string as the failure reason
if ((data_start = strchr (http_buffer, '\n')) != NULL)
*data_start = 0; // see where the first reply line ends and break the string there
return ((strncmp (http_buffer, "HTTP/", 5) == 0) && (strstr (http_buffer, " 200 ") != NULL)); // reachability test only
}
else if ((data_start = strstr (http_buffer, "Success=")) != NULL)
{
if (failure_reason != NULL)
*failure_reason = L""; // no error, set an empty string as the failure reason
if (returned_code != NULL)
*returned_code = (unsigned int) _atoi64 (&data_start[strlen ("Success=")]);
if (returned_email != NULL)
{
data_start = strchr (data_start, ',');
if (data_start != NULL)
{
if (strchr (data_start, '\r') != 0)
*strchr (data_start, '\r') = 0; // don't report the newline
ConvertToWideChar (wide_email, WCHAR_SIZEOF (wide_email), data_start + 1);
*returned_email = wide_email;
}
else
*returned_email = L"";
}
return (true); // and return the candidate code we got
}
else if ((data_start = strstr (http_buffer, "Error=")) != NULL)
{
if (failure_reason != NULL)
{
data_start += strlen ("Error=");
if (strchr (data_start, '\r') != NULL) *strchr (data_start, '\r') = 0;
if (strchr (data_start, '\n') != NULL) *strchr (data_start, '\n') = 0;
if (_stricmp (data_start, "WrongEmail") == 0) *failure_reason = LOCALIZE (L"Registration_WrongEmail");
else if (_stricmp (data_start, "UnknownEmail") == 0) *failure_reason = LOCALIZE (L"Registration_UnknownEmail");
else if (_stricmp (data_start, "TooManyActivations") == 0) *failure_reason = LOCALIZE (L"Registration_TooManyActivations");;
}
return (false); // server returned an error, return an error condition
}
if (failure_reason != NULL)
*failure_reason = LOCALIZE (L"Registration_NetworkFailure"); // this should never happen
return (false); // server returned an error, return an error condition
#undef REGISTRATION_SCRIPT
#undef REGISTRATION_HOST
}
#endif // !NO_REGISTRATION