Subversion Repositories Games.Chess Giants

Rev

Rev 179 | Rev 186 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // dialog_registration.cpp
  2.  
  3. #include "../common.h"
  4.  
  5.  
  6. // dialog template
  7. #define THIS_DIALOG DIALOG_REGISTER
  8.  
  9.  
  10. // global variables used in this module only
  11. static wchar_t transaction_uuid[128] = L"";
  12. static bool was_donatebutton_clicked = false;
  13. static HWND hThisDialogWnd = NULL;
  14.  
  15.  
  16. // prototypes of local functions
  17. static void RegistrationCheckThread (void *thread_parms);
  18. static int CALLBACK DialogProc_ThisDialog (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam);
  19. static bool RetrieveActivationCode (wchar_t *candidate_email_or_uuid, unsigned int *returned_code, wchar_t **returned_email, wchar_t **failure_reason);
  20.  
  21.  
  22. void DialogBox_Registration (void)
  23. {
  24.    // helper function to fire up the modal dialog box
  25.  
  26.    // 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!
  27.    if (!RetrieveActivationCode (NULL, NULL, NULL, NULL))
  28.       return; // if it's not, don't display anything
  29.  
  30.    // generate a random transaction UUID for this session
  31.    srand ((unsigned int) time (NULL));
  32.    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 ());
  33.  
  34.    // start a new thread that will ask every 10 seconds if our transaction ID has donated
  35.    _beginthread (RegistrationCheckThread, 0, NULL); // fire up the thread
  36.  
  37.    // display the dialog box (set the parent to be the desktop)
  38.    DialogBox (hAppInstance, MAKEINTRESOURCE (THIS_DIALOG), NULL, DialogProc_ThisDialog);
  39.  
  40.    return; // finished processing this dialog
  41. }
  42.  
  43.  
  44. static void RegistrationCheckThread (void *thread_parms)
  45. {
  46.    // thread entrypoint for the thread that periodically checks whether transaction_uuid has just donated
  47.  
  48.    unsigned int returned_code; // 32 bits minimum
  49.    wchar_t *returned_email;
  50.  
  51.    // loop endlessly
  52.    for (;;)
  53.    {
  54.       // query the remote server for the code associated with this transaction UUID
  55.       if (RetrieveActivationCode (transaction_uuid, &returned_code, &returned_email, NULL) && IsRegistrationCorrect (returned_email, returned_code))
  56.          break; // stop looping as soon as we get correct values
  57.  
  58.       Sleep (10 * 1000); // next check in 10 seconds
  59.    }
  60.  
  61.    // the user just registered, save activation email and activation code
  62.    wcscpy_s (options.registration.user_email, WCHAR_SIZEOF (options.registration.user_email), returned_email);
  63.    options.registration.activation_code = returned_code;
  64.  
  65.    if (IsWindow (hThisDialogWnd))
  66.       ShowWindow (hThisDialogWnd, SW_HIDE); // make the dialog box disappear
  67.    MessageBox (NULL, LOCALIZE (L"Registration_ThankYou"), PROGRAM_NAME, MB_ICONINFORMATION | MB_OK); // display a "thank you" message
  68.    was_donatebutton_clicked = false; // prevent the reminder dialog box to open
  69.    if (IsWindow (hThisDialogWnd))
  70.       PostMessage (hThisDialogWnd, WM_CLOSE, 0, 0); // then close the dialog box
  71.  
  72.    return; // _endthread() implied
  73. }
  74.  
  75.  
  76. static int CALLBACK DialogProc_ThisDialog (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
  77. {
  78.    // message handler for the dialog box
  79.  
  80.    static wchar_t temp_string[1024];
  81.  
  82.    unsigned short wParam_hiword;
  83.    unsigned short wParam_loword;
  84.    unsigned int returned_code; // 32 bits minimum
  85.    wchar_t *failure_reason;
  86.    int write_index;
  87.    int read_index;
  88.    int length;
  89.  
  90.    // filter out the commonly used message values
  91.    wParam_hiword = HIWORD (wParam);
  92.    wParam_loword = LOWORD (wParam);
  93.  
  94.    // have we just fired up this window ?
  95.    if (message == WM_INITDIALOG)
  96.    {
  97.       hThisDialogWnd = hWnd; // save the dialog handle
  98.  
  99.       // center the window
  100.       CenterWindow (hWnd, hMainWnd);
  101.  
  102.       // set dialog icons (small one for title bar & big one for task manager)
  103.       SendMessage (hWnd, WM_SETICON, ICON_SMALL, (LPARAM) LoadIcon (hAppInstance, MAKEINTRESOURCE (ICON_MAIN)));
  104.       SendMessage (hWnd, WM_SETICON, ICON_BIG, (LPARAM) LoadIcon (hAppInstance, MAKEINTRESOURCE (ICON_MAIN)));
  105.  
  106.       // set window title and control texts
  107.       SetWindowText (hWnd, LOCALIZE (L"Registration_Title"));
  108.  
  109.       // set the static texts
  110.       Static_SetText (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_QUESTION), LOCALIZE (L"Registration_Question"));
  111.       Static_SetText (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_INSTRUCTIONS), LOCALIZE (L"Registration_Instructions"));
  112.       // set email address font
  113.       SendMessage (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS), WM_SETFONT, (WPARAM) GetStockObject (SYSTEM_FONT), 0);
  114.       if (options.registration.user_email[0] != 0)
  115.          SetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, options.registration.user_email);
  116.       else
  117.          SetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, LOCALIZE (L"Registration_YourEmailHere"));
  118.       SetWindowText (GetDlgItem (hWnd, BUTTON_VALIDATECODE), LOCALIZE (L"Registration_ValidateCode"));
  119.       Static_SetText (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_STATUSBAR), LOCALIZE (L"Registration_StatusBar"));
  120.  
  121.       // set focus to the first settable item
  122.       SendMessage (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS), EM_SETSEL, 0, -1); // select all text
  123.       SetFocus (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS));
  124.  
  125.       // convert the Paypal bitmap and the status bar message to hyperlinks
  126.       ConvertStaticToHyperlink (GetDlgItem (hWnd, BUTTON_DONATE));
  127.       ConvertStaticToHyperlink (GetDlgItem (hWnd, STATICTEXT_REGISTRATION_STATUSBAR));
  128.    }
  129.  
  130.    // else did we click the close button on the title bar ?
  131.    else if (message == WM_CLOSE)
  132.    {
  133.       if (was_donatebutton_clicked)
  134.       {
  135.          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
  136.          SendMessage (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS), EM_SETSEL, 0, -1); // select all text
  137.          SetFocus (GetDlgItem (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS)); // focus on the email address field
  138.          was_donatebutton_clicked = false; // once is enough
  139.          return (true); // prevent window close this time
  140.       }
  141.       else
  142.          EndDialog (hWnd, 0); // close the dialog box
  143.  
  144.       hThisDialogWnd = NULL; // this window is about to no longer have a handle, so remember it
  145.    }
  146.  
  147.    // else did we take action on one of the controls ?
  148.    else if (message == WM_COMMAND)
  149.    {
  150.       // did we cancel the dialog box ? (IDCANCEL is a system-wide dialog box handler value, that catches the ESC key)
  151.       if (wParam_loword == IDCANCEL)
  152.          EndDialog (hWnd, 0); // close the dialog box
  153.  
  154.       // else did the user enter something in the edit boxes ?
  155.       else if (wParam_hiword == EN_CHANGE)
  156.       {
  157.          // enable the validation button only if the PayPal email field has data
  158.          GetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, temp_string, WCHAR_SIZEOF (temp_string));
  159.  
  160.          // strip spaces and unprintable characters from candidate email, and bring it lowercase
  161.          length = wcslen (temp_string);
  162.          write_index = 0;
  163.          for (read_index = 0; read_index < length; read_index++)
  164.             if (!iswspace (temp_string[read_index]))
  165.             {
  166.                temp_string[write_index] = tolower (temp_string[read_index]); // force lowercase
  167.                write_index++; // keep only valid signs and discard spaces
  168.             }
  169.          temp_string[write_index] = 0; // ensure string is correctly terminated
  170.  
  171.          EnableWindow (GetDlgItem (hWnd, BUTTON_VALIDATECODE), ((temp_string[0] != 0) && (wcslen (temp_string) > 5) && (wcscmp (temp_string, LOCALIZE (L"Registration_YourEmailHere")) != 0)
  172.                                                                 && (wcschr (temp_string, L'@') != NULL) && (wcschr (temp_string, L'.') != NULL)));
  173.       }
  174.  
  175.       // else was it the validation button ?
  176.       else if (wParam_loword == BUTTON_VALIDATECODE)
  177.       {
  178.          // enable the validation button only if the PayPal email field has data
  179.          GetDlgItemText (hWnd, EDITBOX_REGISTRATION_EMAILADDRESS, temp_string, WCHAR_SIZEOF (temp_string));
  180.  
  181.          // query the remote server for the code associated with this email. If it fails, ask why.
  182.          if (!RetrieveActivationCode (temp_string, &returned_code, NULL, &failure_reason)
  183.              || ((failure_reason != NULL) && (failure_reason[0] != 0)))
  184.             MessageBox (hWnd, failure_reason, PROGRAM_NAME, MB_ICONERROR | MB_OK); // registration failed. Display an error and DON'T make the dialog box disappear
  185.  
  186.          // else is the retrieved data correct ?
  187.          else if (IsRegistrationCorrect (temp_string, returned_code))
  188.          {
  189.             // if so, save activation email and activation code
  190.             wcscpy_s (options.registration.user_email, WCHAR_SIZEOF (options.registration.user_email), temp_string);
  191.             options.registration.activation_code = returned_code;
  192.             MessageBox (hWnd, LOCALIZE (L"Registration_ThankYou"), PROGRAM_NAME, MB_ICONINFORMATION | MB_OK);
  193.             EndDialog (hWnd, 0); // then display a thank you dialog box and make the dialog box disappear
  194.          }
  195.  
  196.          // else the supplied data is wrong
  197.          else
  198.          {
  199.             swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Registration_Error"), AUTHOR_EMAIL);
  200.             MessageBox (hWnd, temp_string, PROGRAM_NAME, MB_ICONERROR | MB_OK); // wrong activation. Display an error and DON'T make the dialog box disappear
  201.          }
  202.       }
  203.  
  204.       // else was it the PayPal button ?
  205.       else if (wParam_loword == BUTTON_DONATE)
  206.       {
  207.          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
  208.  
  209.          if (wcscmp (languages[language_id].name, L"French") == 0)
  210.             wcscpy_s (temp_string, sizeof (temp_string), PAYPAL_URL_FR); // select the French PayPal page
  211.          else
  212.             wcscpy_s (temp_string, sizeof (temp_string), PAYPAL_URL_XX); // select the international (English) PayPal page
  213.          wcscat_s (temp_string, sizeof (temp_string), transaction_uuid); // in all cases, append the transaction UUID that our server generated for us
  214.  
  215.          ShellExecute (NULL, L"open", temp_string, NULL, NULL, SW_MAXIMIZE); // open PayPal in the default browser, maximized
  216.          was_donatebutton_clicked = true; // remember the user clicked the Donate button
  217.       }
  218.  
  219.       // else was it the status bar hyperlink ?
  220.       else if (wParam_loword == STATICTEXT_REGISTRATION_STATUSBAR)
  221.          ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the main page in the default browser, maximized
  222.    }
  223.  
  224.    // call the default dialog message processing function to keep things going
  225.    return (false);
  226. }
  227.  
  228.  
  229. static bool RetrieveActivationCode (wchar_t *candidate_email_or_uuid, unsigned int *returned_code, wchar_t **returned_email, wchar_t **failure_reason)
  230. {
  231.    // this function queries the remote server for registration status. If OK, the function returns TRUE and
  232.    // the code and email are returned in the returned_code and returned_email pointers, and failure_reason
  233.    // is set to point to a NULL string. On error, FALSE is returned and failure_reason points to the cause
  234.    // of the error (localized). If candidate_email_or_uuid is a NULL string, only a server reachability test
  235.    // is performed.
  236.  
  237.    #define REGISTRATION_HOST "pmbaty.com"
  238.    #define REGISTRATION_SCRIPT "/chess/whatsmycode.php"
  239.  
  240.    static char http_buffer[1024]; // used both for request and reply
  241.    static char ascii_email_or_uuid[128];
  242.    static wchar_t wide_email[128];
  243.  
  244.    struct sockaddr_in service;
  245.    struct hostent *hostinfo;
  246.    char *data_start;
  247.    int write_index;
  248.    int read_index;
  249.    int length;
  250.    SOCKET s;
  251.  
  252.    // get our distribution server's IP address from the host name
  253.    hostinfo = gethostbyname (REGISTRATION_HOST);
  254.    if (hostinfo == NULL)
  255.    {
  256.       if (failure_reason != NULL)
  257.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  258.       return (false); // couldn't resolve hostname, return an error condition
  259.    }
  260.  
  261.    // fill in the sockaddr server structure with the server hostinfo data
  262.    service.sin_family = AF_INET;
  263.    service.sin_addr.s_addr = inet_addr (inet_ntoa (*(struct in_addr *) hostinfo->h_addr_list[0]));
  264.    service.sin_port = htons (80); // connect to webserver there (port 80)
  265.  
  266.    // create our socket
  267.    if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
  268.    {
  269.       if (failure_reason != NULL)
  270.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  271.       return (0); // couldn't create socket, return an error condition
  272.    }
  273.  
  274.    // connect to the distributor's webserver using that socket
  275.    if (connect (s, (struct sockaddr *) &service, sizeof (service)) == -1)
  276.    {
  277.       closesocket (s);
  278.       if (failure_reason != NULL)
  279.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  280.       return (0); // unable to connect to webserver, return an error condition
  281.    }
  282.  
  283.    // build the HTTP query and send it
  284.    if (candidate_email_or_uuid != NULL)
  285.       ConvertTo7BitASCII (ascii_email_or_uuid, sizeof (ascii_email_or_uuid), candidate_email_or_uuid);
  286.    sprintf_s (http_buffer, sizeof (http_buffer),
  287.               "GET " REGISTRATION_SCRIPT "?%s=%s HTTP/1.1\r\n"
  288.               "Host: " REGISTRATION_HOST "\r\n"
  289.               "Connection: close\r\n"
  290.               "\r\n",
  291.               (candidate_email_or_uuid != NULL ? (wcschr (candidate_email_or_uuid, '@') != NULL ? "email" : "uuid") : "test"),
  292.               (candidate_email_or_uuid != NULL ? ascii_email_or_uuid : "1"));
  293.    length = strlen (http_buffer);
  294.    write_index = send (s, http_buffer, length, 0); // send the HTTP query
  295.    if (write_index != length)
  296.    {
  297.       closesocket (s);
  298.       if (failure_reason != NULL)
  299.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  300.       return (0); // unable to send HTTP query, return an error condition
  301.    }
  302.  
  303.    // read the reply (10 seconds timeout)
  304.    http_buffer[0] = 0;
  305.    read_index = RecvWithTimeout (s, 10.0f, http_buffer, sizeof (http_buffer), 0);
  306.    if (read_index < 1)
  307.    {
  308.       closesocket (s);
  309.       if (failure_reason != NULL)
  310.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  311.       return (0); // empty or erroneous HTTP reply, return an error condition
  312.    }
  313.  
  314.    closesocket (s); // finished communicating, close TCP socket
  315.  
  316.    // terminate recv buffer and see where the useful data starts
  317.    http_buffer[read_index] = 0;
  318.    //MessageBoxA (NULL, http_buffer, "HTTP response", MB_OK);
  319.    if (candidate_email_or_uuid == NULL)
  320.    {
  321.       if (failure_reason != NULL)
  322.          *failure_reason = L""; // no error, set an empty string as the failure reason
  323.       if ((data_start = strchr (http_buffer, '\n')) != NULL)
  324.          *data_start = 0; // see where the first reply line ends and break the string there
  325.       return ((strncmp (http_buffer, "HTTP/", 5) == 0) && (strstr (http_buffer, " 200 ") != NULL)); // reachability test only
  326.    }
  327.    else if ((data_start = strstr (http_buffer, "Success=")) != NULL)
  328.    {
  329.       if (failure_reason != NULL)
  330.          *failure_reason = L""; // no error, set an empty string as the failure reason
  331.       if (returned_code != NULL)
  332.          *returned_code = (unsigned int) _atoi64 (&data_start[strlen ("Success=")]);
  333.       if (returned_email != NULL)
  334.       {
  335.          data_start = strchr (data_start, ',');
  336.          if (data_start != NULL)
  337.          {
  338.             if (strchr (data_start, '\r') != 0)
  339.                *strchr (data_start, '\r') = 0; // don't report the newline
  340.             ConvertToWideChar (wide_email, WCHAR_SIZEOF (wide_email), data_start + 1);
  341.             *returned_email = wide_email;
  342.          }
  343.          else
  344.             *returned_email = L"";
  345.       }
  346.       return (true); // and return the candidate code we got
  347.    }
  348.    else if ((data_start = strstr (http_buffer, "Error=")) != NULL)
  349.    {
  350.       if (failure_reason != NULL)
  351.       {
  352.          data_start += strlen ("Error=");
  353.          if (strchr (data_start, '\r') != NULL) *strchr (data_start, '\r') = 0;
  354.          if (strchr (data_start, '\n') != NULL) *strchr (data_start, '\n') = 0;
  355.          if      (_stricmp (data_start, "WrongEmail") == 0)         *failure_reason = LOCALIZE (L"Registration_WrongEmail");
  356.          else if (_stricmp (data_start, "UnknownEmail") == 0)       *failure_reason = LOCALIZE (L"Registration_UnknownEmail");
  357.          else if (_stricmp (data_start, "TooManyActivations") == 0) *failure_reason = LOCALIZE (L"Registration_TooManyActivations");;
  358.       }
  359.       return (false); // server returned an error, return an error condition
  360.    }
  361.  
  362.    if (failure_reason != NULL)
  363.       *failure_reason = LOCALIZE (L"Registration_NetworkFailure"); // this should never happen
  364.    return (false); // server returned an error, return an error condition
  365.  
  366.    #undef REGISTRATION_SCRIPT
  367.    #undef REGISTRATION_HOST
  368. }
  369.