Subversion Repositories Games.Chess Giants

Rev

Rev 185 | 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.             was_donatebutton_clicked = false; // once is enough
  194.             EndDialog (hWnd, 0); // then display a thank you dialog box and make the dialog box disappear
  195.          }
  196.  
  197.          // else the supplied data is wrong
  198.          else
  199.          {
  200.             swprintf_s (temp_string, WCHAR_SIZEOF (temp_string), LOCALIZE (L"Registration_Error"), AUTHOR_EMAIL);
  201.             MessageBox (hWnd, temp_string, PROGRAM_NAME, MB_ICONERROR | MB_OK); // wrong activation. Display an error and DON'T make the dialog box disappear
  202.          }
  203.       }
  204.  
  205.       // else was it the PayPal button ?
  206.       else if (wParam_loword == BUTTON_DONATE)
  207.       {
  208.          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
  209.  
  210.          if (wcscmp (languages[language_id].name, L"French") == 0)
  211.             wcscpy_s (temp_string, sizeof (temp_string), PAYPAL_URL_FR); // select the French PayPal page
  212.          else
  213.             wcscpy_s (temp_string, sizeof (temp_string), PAYPAL_URL_XX); // select the international (English) PayPal page
  214.          wcscat_s (temp_string, sizeof (temp_string), transaction_uuid); // in all cases, append the transaction UUID that our server generated for us
  215.  
  216.          ShellExecute (NULL, L"open", temp_string, NULL, NULL, SW_MAXIMIZE); // open PayPal in the default browser, maximized
  217.          was_donatebutton_clicked = true; // remember the user clicked the Donate button
  218.       }
  219.  
  220.       // else was it the status bar hyperlink ?
  221.       else if (wParam_loword == STATICTEXT_REGISTRATION_STATUSBAR)
  222.          ShellExecute (NULL, L"open", PROGRAM_URL, NULL, NULL, SW_MAXIMIZE); // open the main page in the default browser, maximized
  223.    }
  224.  
  225.    // call the default dialog message processing function to keep things going
  226.    return (false);
  227. }
  228.  
  229.  
  230. static bool RetrieveActivationCode (wchar_t *candidate_email_or_uuid, unsigned int *returned_code, wchar_t **returned_email, wchar_t **failure_reason)
  231. {
  232.    // this function queries the remote server for registration status. If OK, the function returns TRUE and
  233.    // the code and email are returned in the returned_code and returned_email pointers, and failure_reason
  234.    // is set to point to a NULL string. On error, FALSE is returned and failure_reason points to the cause
  235.    // of the error (localized). If candidate_email_or_uuid is a NULL string, only a server reachability test
  236.    // is performed.
  237.  
  238.    #define REGISTRATION_HOST "pmbaty.com"
  239.    #define REGISTRATION_SCRIPT "/chess/whatsmycode.php"
  240.  
  241.    static char http_buffer[1024]; // used both for request and reply
  242.    static char ascii_email_or_uuid[128];
  243.    static wchar_t wide_email[128];
  244.  
  245.    struct sockaddr_in service;
  246.    struct hostent *hostinfo;
  247.    char *data_start;
  248.    int write_index;
  249.    int read_index;
  250.    int length;
  251.    SOCKET s;
  252.  
  253.    // get our distribution server's IP address from the host name
  254.    hostinfo = gethostbyname (REGISTRATION_HOST);
  255.    if (hostinfo == NULL)
  256.    {
  257.       if (failure_reason != NULL)
  258.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  259.       return (false); // couldn't resolve hostname, return an error condition
  260.    }
  261.  
  262.    // fill in the sockaddr server structure with the server hostinfo data
  263.    service.sin_family = AF_INET;
  264.    service.sin_addr.s_addr = inet_addr (inet_ntoa (*(struct in_addr *) hostinfo->h_addr_list[0]));
  265.    service.sin_port = htons (80); // connect to webserver there (port 80)
  266.  
  267.    // create our socket
  268.    if ((s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
  269.    {
  270.       if (failure_reason != NULL)
  271.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  272.       return (0); // couldn't create socket, return an error condition
  273.    }
  274.  
  275.    // connect to the distributor's webserver using that socket
  276.    if (connect (s, (struct sockaddr *) &service, sizeof (service)) == -1)
  277.    {
  278.       closesocket (s);
  279.       if (failure_reason != NULL)
  280.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  281.       return (0); // unable to connect to webserver, return an error condition
  282.    }
  283.  
  284.    // build the HTTP query and send it
  285.    if (candidate_email_or_uuid != NULL)
  286.       ConvertTo7BitASCII (ascii_email_or_uuid, sizeof (ascii_email_or_uuid), candidate_email_or_uuid);
  287.    sprintf_s (http_buffer, sizeof (http_buffer),
  288.               "GET " REGISTRATION_SCRIPT "?%s=%s HTTP/1.1\r\n"
  289.               "Host: " REGISTRATION_HOST "\r\n"
  290.               "Connection: close\r\n"
  291.               "\r\n",
  292.               (candidate_email_or_uuid != NULL ? (wcschr (candidate_email_or_uuid, '@') != NULL ? "email" : "uuid") : "test"),
  293.               (candidate_email_or_uuid != NULL ? ascii_email_or_uuid : "1"));
  294.    length = strlen (http_buffer);
  295.    write_index = send (s, http_buffer, length, 0); // send the HTTP query
  296.    if (write_index != length)
  297.    {
  298.       closesocket (s);
  299.       if (failure_reason != NULL)
  300.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  301.       return (0); // unable to send HTTP query, return an error condition
  302.    }
  303.  
  304.    // read the reply (10 seconds timeout)
  305.    http_buffer[0] = 0;
  306.    read_index = RecvWithTimeout (s, 10.0f, http_buffer, sizeof (http_buffer), 0);
  307.    if (read_index < 1)
  308.    {
  309.       closesocket (s);
  310.       if (failure_reason != NULL)
  311.          *failure_reason = LOCALIZE (L"Registration_NetworkFailure");
  312.       return (0); // empty or erroneous HTTP reply, return an error condition
  313.    }
  314.  
  315.    closesocket (s); // finished communicating, close TCP socket
  316.  
  317.    // terminate recv buffer and see where the useful data starts
  318.    http_buffer[read_index] = 0;
  319.    //MessageBoxA (NULL, http_buffer, "HTTP response", MB_OK);
  320.    if (candidate_email_or_uuid == NULL)
  321.    {
  322.       if (failure_reason != NULL)
  323.          *failure_reason = L""; // no error, set an empty string as the failure reason
  324.       if ((data_start = strchr (http_buffer, '\n')) != NULL)
  325.          *data_start = 0; // see where the first reply line ends and break the string there
  326.       return ((strncmp (http_buffer, "HTTP/", 5) == 0) && (strstr (http_buffer, " 200 ") != NULL)); // reachability test only
  327.    }
  328.    else if ((data_start = strstr (http_buffer, "Success=")) != NULL)
  329.    {
  330.       if (failure_reason != NULL)
  331.          *failure_reason = L""; // no error, set an empty string as the failure reason
  332.       if (returned_code != NULL)
  333.          *returned_code = (unsigned int) _atoi64 (&data_start[strlen ("Success=")]);
  334.       if (returned_email != NULL)
  335.       {
  336.          data_start = strchr (data_start, ',');
  337.          if (data_start != NULL)
  338.          {
  339.             if (strchr (data_start, '\r') != 0)
  340.                *strchr (data_start, '\r') = 0; // don't report the newline
  341.             ConvertToWideChar (wide_email, WCHAR_SIZEOF (wide_email), data_start + 1);
  342.             *returned_email = wide_email;
  343.          }
  344.          else
  345.             *returned_email = L"";
  346.       }
  347.       return (true); // and return the candidate code we got
  348.    }
  349.    else if ((data_start = strstr (http_buffer, "Error=")) != NULL)
  350.    {
  351.       if (failure_reason != NULL)
  352.       {
  353.          data_start += strlen ("Error=");
  354.          if (strchr (data_start, '\r') != NULL) *strchr (data_start, '\r') = 0;
  355.          if (strchr (data_start, '\n') != NULL) *strchr (data_start, '\n') = 0;
  356.          if      (_stricmp (data_start, "WrongEmail") == 0)         *failure_reason = LOCALIZE (L"Registration_WrongEmail");
  357.          else if (_stricmp (data_start, "UnknownEmail") == 0)       *failure_reason = LOCALIZE (L"Registration_UnknownEmail");
  358.          else if (_stricmp (data_start, "TooManyActivations") == 0) *failure_reason = LOCALIZE (L"Registration_TooManyActivations");;
  359.       }
  360.       return (false); // server returned an error, return an error condition
  361.    }
  362.  
  363.    if (failure_reason != NULL)
  364.       *failure_reason = LOCALIZE (L"Registration_NetworkFailure"); // this should never happen
  365.    return (false); // server returned an error, return an error condition
  366.  
  367.    #undef REGISTRATION_SCRIPT
  368.    #undef REGISTRATION_HOST
  369. }
  370.