Subversion Repositories Games.Chess Giants

Rev

Rev 186 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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