Index: src/ch/AsyncHttpFile.cpp =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/AsyncHttpFile.cpp (.../AsyncHttpFile.cpp) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/ch/AsyncHttpFile.cpp (.../AsyncHttpFile.cpp) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -272,13 +272,17 @@ // then a separate call to close need to be performed. m_dwExpectedState = 0; SetUrlHandle(NULL); - ::InternetCloseHandle(m_hInternet); + if(m_hInternet != nullptr) + { + ::InternetCloseHandle(m_hInternet); + m_hInternet = nullptr; + } } if(m_hFinishedEvent) { ::CloseHandle(m_hFinishedEvent); - m_hFinishedEvent = NULL; + m_hFinishedEvent = nullptr; } return S_OK; Index: src/ch/UpdateChecker.cpp =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdateChecker.cpp (.../UpdateChecker.cpp) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/ch/UpdateChecker.cpp (.../UpdateChecker.cpp) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -55,6 +55,9 @@ { Cleanup(); + if(m_hKillEvent) + ::CloseHandle(m_hKillEvent); + ::DeleteCriticalSection(&m_cs); } @@ -72,6 +75,8 @@ if(!pszSite) return false; + Cleanup(); + DWORD dwConnectionFlags = 0; if(bOnlyIfConnected && !InternetGetConnectedState(&dwConnectionFlags, 0)) @@ -110,15 +115,12 @@ if(m_hKillEvent) ::SetEvent(m_hKillEvent); WaitForSingleObject(m_hThread, 5000); - if(m_hKillEvent) - ::CloseHandle(m_hKillEvent); + m_hThread = NULL; } m_httpFile.Close(); ::EnterCriticalSection(&m_cs); - m_hThread = NULL; - m_hKillEvent = NULL; m_strSite.Empty(); m_eUpdateChannel = UpdateVersionInfo::eStable; m_strLanguage.Empty(); @@ -127,6 +129,7 @@ m_strReadableVersion.Empty(); m_strReleaseDate.Empty(); m_strDownloadAddress.Empty(); + m_strReleaseNotes.Empty(); m_eResult = CUpdateChecker::eResult_Undefined; ::LeaveCriticalSection(&m_cs); } @@ -194,11 +197,13 @@ /// @brief Retrieves the address of a site to check the updates at. /// @param[out] rstrAddress Receives the address. // ============================================================================ -void CUpdateChecker::GetSiteAddress(CString& rstrAddress) const +CString CUpdateChecker::GetSiteAddress() const { ::EnterCriticalSection(&m_cs); - rstrAddress = m_strSite; + CString strAddress = m_strSite; ::LeaveCriticalSection(&m_cs); + + return strAddress; } // ============================================================================ @@ -244,110 +249,115 @@ { CUpdateChecker* pUpdateChecker = (CUpdateChecker*)pParam; - // mark as started - pUpdateChecker->SetResult(eResult_Pending, 0); + try + { + // mark as started + pUpdateChecker->SetResult(eResult_Pending, 0); - // get the real address of file to download - CString strSite; - pUpdateChecker->GetSiteAddress(strSite); - strSite += _T("/chupdate.php"); + // get the real address of file to download + CString strSite = pUpdateChecker->GetSiteAddress(); - CAsyncHttpFile::EWaitResult eWaitResult = CAsyncHttpFile::ePending; - size_t stFileSize = 0; - std::stringstream dataBuffer; + CAsyncHttpFile::EWaitResult eWaitResult = CAsyncHttpFile::ePending; + size_t stFileSize = 0; + std::stringstream dataBuffer; - // open the connection and try to get to the file - std::wstring wstrUserAgent = pUpdateChecker->m_tUpdateHeaders.GetUserAgent(); - std::wstring wstrHeaders = pUpdateChecker->m_tUpdateHeaders.GetHeaders((PCTSTR)pUpdateChecker->m_strLanguage); - HRESULT hResult = pUpdateChecker->m_httpFile.Open(strSite, wstrUserAgent.c_str(), wstrHeaders.c_str()); - if(SUCCEEDED(hResult)) - { - eWaitResult = pUpdateChecker->m_httpFile.WaitForResult(pUpdateChecker->m_hKillEvent); - switch(eWaitResult) + // open the connection and try to get to the file + std::wstring wstrUserAgent = pUpdateChecker->m_tUpdateHeaders.GetUserAgent(); + std::wstring wstrHeaders = pUpdateChecker->m_tUpdateHeaders.GetHeaders((PCTSTR)pUpdateChecker->m_strLanguage, pUpdateChecker->m_eUpdateChannel); + HRESULT hResult = pUpdateChecker->m_httpFile.Open(strSite, wstrUserAgent.c_str(), wstrHeaders.c_str()); + if(SUCCEEDED(hResult)) { - case CAsyncHttpFile::eFinished: - break; - case CAsyncHttpFile::eKilled: - pUpdateChecker->SetResult(eResult_Killed, 0); - return 1; - case CAsyncHttpFile::eError: - pUpdateChecker->SetResult(eResult_Error, pUpdateChecker->m_httpFile.GetErrorCode()); - return 1; - case CAsyncHttpFile::eTimeout: - case CAsyncHttpFile::ePending: - default: - pUpdateChecker->SetResult(eResult_Error, 0); - return 1; + eWaitResult = pUpdateChecker->m_httpFile.WaitForResult(pUpdateChecker->m_hKillEvent); + switch(eWaitResult) + { + case CAsyncHttpFile::eFinished: + break; + case CAsyncHttpFile::eKilled: + pUpdateChecker->SetResult(eResult_Killed, 0); + return 1; + case CAsyncHttpFile::eError: + pUpdateChecker->SetResult(eResult_Error, pUpdateChecker->m_httpFile.GetErrorCode()); + return 1; + case CAsyncHttpFile::eTimeout: + case CAsyncHttpFile::ePending: + default: + pUpdateChecker->SetResult(eResult_Error, 0); + return 1; + } + + // get the file size + hResult = pUpdateChecker->m_httpFile.GetFileSize(stFileSize); } - // get the file size - hResult = pUpdateChecker->m_httpFile.GetFileSize(stFileSize); - } - - if(SUCCEEDED(hResult)) - { - bool bIsClosed = false; - char* pbyBuffer = new char[stFileSize]; - do + if(SUCCEEDED(hResult)) { - hResult = pUpdateChecker->m_httpFile.RequestData(pbyBuffer, stFileSize); - if(SUCCEEDED(hResult)) + bool bIsClosed = false; + char* pbyBuffer = new char[ stFileSize ]; + do { - eWaitResult = pUpdateChecker->m_httpFile.WaitForResult(pUpdateChecker->m_hKillEvent); - switch(eWaitResult) + hResult = pUpdateChecker->m_httpFile.RequestData(pbyBuffer, stFileSize); + if(SUCCEEDED(hResult)) { - case CAsyncHttpFile::eFinished: - break; - case CAsyncHttpFile::eKilled: - pUpdateChecker->SetResult(eResult_Killed, 0); - return 1; - break; - case CAsyncHttpFile::eError: - pUpdateChecker->SetResult(eResult_Error, pUpdateChecker->m_httpFile.GetErrorCode()); - return 1; - case CAsyncHttpFile::eTimeout: - case CAsyncHttpFile::ePending: - default: - pUpdateChecker->SetResult(eResult_Error, 0); - return 1; + eWaitResult = pUpdateChecker->m_httpFile.WaitForResult(pUpdateChecker->m_hKillEvent); + switch(eWaitResult) + { + case CAsyncHttpFile::eFinished: + break; + case CAsyncHttpFile::eKilled: + pUpdateChecker->SetResult(eResult_Killed, 0); + return 1; + break; + case CAsyncHttpFile::eError: + pUpdateChecker->SetResult(eResult_Error, pUpdateChecker->m_httpFile.GetErrorCode()); + return 1; + case CAsyncHttpFile::eTimeout: + case CAsyncHttpFile::ePending: + default: + pUpdateChecker->SetResult(eResult_Error, 0); + return 1; + } } - } - if(SUCCEEDED(hResult)) - hResult = pUpdateChecker->m_httpFile.GetRetrievedDataSize(stFileSize); + if(SUCCEEDED(hResult)) + hResult = pUpdateChecker->m_httpFile.GetRetrievedDataSize(stFileSize); - if(SUCCEEDED(hResult) && stFileSize) - dataBuffer.write(pbyBuffer, stFileSize); + if(SUCCEEDED(hResult) && stFileSize) + dataBuffer.write(pbyBuffer, stFileSize); - bIsClosed = pUpdateChecker->m_httpFile.IsClosed(); + bIsClosed = pUpdateChecker->m_httpFile.IsClosed(); + } + while(stFileSize && !bIsClosed && SUCCEEDED(hResult)); + + delete[] pbyBuffer; } - while(stFileSize && !bIsClosed && SUCCEEDED(hResult)); - delete [] pbyBuffer; - } + if(FAILED(hResult)) + { + pUpdateChecker->SetResult(eResult_Error, pUpdateChecker->m_httpFile.GetErrorCode()); + return 1; + } - if(FAILED(hResult)) - { - pUpdateChecker->SetResult(eResult_Error, pUpdateChecker->m_httpFile.GetErrorCode()); - return 1; - } + pUpdateChecker->m_httpFile.Close(); - pUpdateChecker->m_httpFile.Close(); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// + UpdateVersionInfo::EVersionType eUpdateChannel = pUpdateChecker->GetUpdateChannel(); - ///////////////////////////////////////////////////////////////////////////////////////////////////////////// - UpdateVersionInfo::EVersionType eUpdateChannel = pUpdateChecker->GetUpdateChannel(); - - UpdateResponse response(dataBuffer); - UpdateVersionInfo vi; - bool bHasUpdates = response.GetVersions().FindUpdateInfo(eUpdateChannel ? UpdateVersionInfo::eBeta : UpdateVersionInfo::eStable, vi); - if(bHasUpdates) + UpdateResponse response(dataBuffer); + UpdateVersionInfo vi; + bool bHasUpdates = response.GetVersions().FindUpdateInfo(eUpdateChannel, vi); + if(bHasUpdates) + { + pUpdateChecker->SetVersionsAndAddress(vi.GetDownloadLink().c_str(), vi.GetNumericVersion().c_str(), vi.GetReadableVersion().c_str(), + FormatDate(vi.GetDateRelease()).c_str(), vi.GetReleaseNotes().c_str()); + pUpdateChecker->SetResult(eResult_RemoteVersionNewer, 0); + } + else + pUpdateChecker->SetResult(eResult_VersionCurrent, 0); + } + catch(const std::exception&) { - pUpdateChecker->SetVersionsAndAddress(vi.GetDownloadLink().c_str(), vi.GetNumericVersion().c_str(), vi.GetReadableVersion().c_str(), - FormatDate(vi.GetDateRelease()).c_str(), vi.GetReleaseNotes().c_str()); - pUpdateChecker->SetResult(eResult_RemoteVersionNewer, 0); + pUpdateChecker->SetResult(eResult_Error, 0); } - else - pUpdateChecker->SetResult(eResult_VersionCurrent, 0); return 0; } Index: src/ch/UpdateChecker.h =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdateChecker.h (.../UpdateChecker.h) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/ch/UpdateChecker.h (.../UpdateChecker.h) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -48,7 +48,7 @@ ~CUpdateChecker(); /// Starts the 'check for updates' thread - bool AsyncCheckForUpdates(const wchar_t* pszSite, const wchar_t* pszLanguage, UpdateVersionInfo::EVersionType bCheckBeta, bool bOnlyIfConnected); + bool AsyncCheckForUpdates(const wchar_t* pszSite, const wchar_t* pszLanguage, UpdateVersionInfo::EVersionType eUpdateChannel, bool bOnlyIfConnected); /// Stops checking and cleanups the object void Cleanup(); @@ -75,7 +75,7 @@ /// Sets the versions and download address void SetVersionsAndAddress(PCTSTR pszAddress, PCTSTR pszNumericVersion, PCTSTR pszReadableVersion, PCTSTR pszReleaseDate, PCTSTR pszReleaseNotes); /// Retrieves the site address - void GetSiteAddress(CString& rstrAddress) const; + CString GetSiteAddress() const; /// Returns information if we're interested in beta versions UpdateVersionInfo::EVersionType GetUpdateChannel(); @@ -97,7 +97,6 @@ CAsyncHttpFile m_httpFile; UpdateHeaders m_tUpdateHeaders; - HANDLE m_hThread; HANDLE m_hKillEvent; mutable CRITICAL_SECTION m_cs; Index: src/ch/UpdateHeaders.cpp =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdateHeaders.cpp (.../UpdateHeaders.cpp) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/ch/UpdateHeaders.cpp (.../UpdateHeaders.cpp) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -41,7 +41,7 @@ return wstrUserAgent; } -std::wstring UpdateHeaders::GetHeaders(const std::wstring& wstrLanguagePath) +std::wstring UpdateHeaders::GetHeaders(const std::wstring& wstrLanguagePath, UpdateVersionInfo::EVersionType eUpdateChannel) { std::wstring wstrLanguage; size_t stPos = wstrLanguagePath.rfind(L'\\'); @@ -71,10 +71,31 @@ wstrHeaders += L"CopyHandler-Language: " + wstrLanguage + L"\r\n"; // #todo update channel - wstrHeaders += L"CopyHandler-UpdateChannel: beta\r\n"; + wstrHeaders += L"CopyHandler-UpdateChannel: " + GetUpdateChannel(eUpdateChannel) + L"\r\n"; // finalize wstrHeaders += L"\r\n\r\n"; return wstrHeaders; } + +std::wstring UpdateHeaders::GetUpdateChannel(UpdateVersionInfo::EVersionType eUpdateChannel) +{ + switch(eUpdateChannel) + { + case UpdateVersionInfo::eAlpha: + return L"alpha"; + + case UpdateVersionInfo::eBeta: + return L"beta"; + + case UpdateVersionInfo::eReleaseCandidate: + return L"rc"; + + case UpdateVersionInfo::eStable: + return L"stable"; + + default: + return L"unknown"; + } +} Index: src/ch/UpdateHeaders.h =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdateHeaders.h (.../UpdateHeaders.h) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/ch/UpdateHeaders.h (.../UpdateHeaders.h) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -20,16 +20,20 @@ #define __UPDATEHEADERS_H__ #include "WindowsVersion.h" +#include "UpdateVersionInfo.h" class UpdateHeaders { public: UpdateHeaders(); std::wstring GetUserAgent(); - std::wstring GetHeaders(const std::wstring& wstrLanguagePath); + std::wstring GetHeaders(const std::wstring& wstrLanguagePath, UpdateVersionInfo::EVersionType eUpdateChannel); private: + static std::wstring GetUpdateChannel(UpdateVersionInfo::EVersionType eUpdateChannel); + +private: WindowsVersion m_tWindowsVersion; }; Index: src/ch/UpdateResponse.cpp =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdateResponse.cpp (.../UpdateResponse.cpp) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/ch/UpdateResponse.cpp (.../UpdateResponse.cpp) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -41,13 +41,21 @@ std::wstring wstrReleaseDate; std::wstring wstrReleaseNotes; + // add version information; note that assumption here that we receive version informations + // sorted by stability (decreasing) - that way we only present the user suggestions to download + // newest versions with highest stability + unsigned long long ullLastVersionNumber = 0; for(const wiptree::value_type& node : pt.get_child(L"UpdateInfo")) { try { UpdateVersionInfo::EVersionType eType = ParseVersionName(node.first); UpdateVersionInfo vi = ParseVersionInfo(node.second); - m_tVersions.Add(eType, std::move(vi)); + + if(vi.GetFullNumericVersion() > ullLastVersionNumber) + m_tVersions.Add(eType, std::move(vi)); + + ullLastVersionNumber = vi.GetFullNumericVersion(); } catch(const std::exception&) // ignore exceptions from version parsing code { Index: src/ch/UpdaterDlg.cpp =================================================================== diff -u -r501360c5c84079dd290b59ca7904c84bad7e38f9 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdaterDlg.cpp (.../UpdaterDlg.cpp) (revision 501360c5c84079dd290b59ca7904c84bad7e38f9) +++ src/ch/UpdaterDlg.cpp (.../UpdaterDlg.cpp) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -57,15 +57,13 @@ InitUpdateChannelCombo(); // disable button initially - CWnd* pWnd = GetDlgItem(IDC_OPEN_WEBPAGE_BUTTON); - if(pWnd) - pWnd->EnableWindow(FALSE); + EnableOpenWebPageButton(false); if(!m_bBackgroundMode) ShowWindow(SW_SHOW); // start the updater - m_ucChecker.AsyncCheckForUpdates(_T(PRODUCT_SITE), GetPropValue(GetConfig()), (UpdateVersionInfo::EVersionType)GetPropValue(GetConfig()), m_bBackgroundMode); + CheckForUpdates(); // start a timer to display progress SetTimer(UPDATER_TIMER, 50, NULL); @@ -108,7 +106,9 @@ if(eResult != m_eLastState) { - switch(m_ucChecker.GetResult()) + m_eLastState = eResult; + + switch(eResult) { case CUpdateChecker::eResult_Undefined: TRACE(_T("CUpdateChecker::eResult_Undefined\n")); @@ -168,7 +168,8 @@ fmt.SetFormat(strFmt); fmt.SetParam(_t("%site"), _t(PRODUCT_SITE)); fmt.SetParam(_t("%thisver"), _T(PRODUCT_VERSION)); - fmt.SetParam(L"%numericver", PRODUCT_NUMERIC_VERSION); + fmt.SetParam(L"%thisnumericver", PRODUCT_NUMERIC_VERSION); + fmt.SetParam(L"%numericver", m_ucChecker.GetNumericVersion()); fmt.SetParam(_t("%officialver"), m_ucChecker.GetReadableVersion()); fmt.SetParam(L"%reldate", m_ucChecker.GetReleaseDate()); @@ -186,12 +187,9 @@ UpdateSecondaryText(strEntireText); // Update button state - CWnd* pWnd = GetDlgItem(IDC_OPEN_WEBPAGE_BUTTON); - if(pWnd) - pWnd->EnableWindow(bEnableButton); + EnableOpenWebPageButton(bEnableButton); + EnableUpdateRelatedControls(eResult > CUpdateChecker::eResult_Pending); - m_eLastState = eResult; - // handle background mode if(m_bBackgroundMode) { @@ -205,6 +203,8 @@ return; case eRes_Show: ShowWindow(SW_SHOW); + m_bBackgroundMode = false; // when we show this window for the first time the user is responsible for closing the dialog; + // otherwise window might close by itself when checking for updates from within the open window break; default: BOOST_ASSERT(FALSE); @@ -308,6 +308,7 @@ eFrequency = (EUpdatesFrequency)iCurSel; SetPropValue(GetConfig(), eFrequency); + GetConfig().Write(); } void CUpdaterDlg::OnSelchangeChannelCombo() @@ -321,4 +322,27 @@ eFrequency = (UpdateVersionInfo::EVersionType)iCurSel; SetPropValue(GetConfig(), eFrequency); + GetConfig().Write(); + + CheckForUpdates(); } + +void CUpdaterDlg::EnableOpenWebPageButton(bool bEnable) +{ + CWnd* pWnd = GetDlgItem(IDC_OPEN_WEBPAGE_BUTTON); + if(pWnd) + pWnd->EnableWindow(bEnable ? TRUE : FALSE); +} + +void CUpdaterDlg::CheckForUpdates() +{ + EnableUpdateRelatedControls(false); + m_eLastState = CUpdateChecker::eResult_Undefined; + + m_ucChecker.AsyncCheckForUpdates(_T(UPDATE_CHECK_LINK), GetPropValue(GetConfig()), (UpdateVersionInfo::EVersionType)GetPropValue(GetConfig()), m_bBackgroundMode); +} + +void CUpdaterDlg::EnableUpdateRelatedControls(bool bEnable) +{ + m_ctlUpdateChannel.EnableWindow(bEnable ? TRUE : FALSE); +} Index: src/ch/UpdaterDlg.h =================================================================== diff -u -r501360c5c84079dd290b59ca7904c84bad7e38f9 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/UpdaterDlg.h (.../UpdaterDlg.h) (revision 501360c5c84079dd290b59ca7904c84bad7e38f9) +++ src/ch/UpdaterDlg.h (.../UpdaterDlg.h) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -30,6 +30,8 @@ virtual BOOL OnInitDialog(); + void CheckForUpdates(); + afx_msg void OnBnClickedOpenWebpageButton(); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnSelchangeFreqCombo(); @@ -47,6 +49,8 @@ void InitRichEdit(); void InitUpdateChannelCombo(); void InitUpdateFreqCombo(); + void EnableOpenWebPageButton(bool bEnable); + void EnableUpdateRelatedControls(bool bEnable); protected: CStatic m_ctlMainText; Index: src/ch/ch.rc =================================================================== diff -u -r501360c5c84079dd290b59ca7904c84bad7e38f9 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/ch/ch.rc (.../ch.rc) (revision 501360c5c84079dd290b59ca7904c84bad7e38f9) +++ src/ch/ch.rc (.../ch.rc) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -1180,7 +1180,7 @@ STRINGTABLE BEGIN - IDS_UPDATER_EQUAL_VERSION_STRING "Your current version: %thisver" + IDS_UPDATER_EQUAL_VERSION_STRING "Your current version: %thisver (%thisnumericver)" IDS_UPDATER_ERROR_STRING "Check for updates error" IDS_UPDATER_WAITING_STRING "Connecting with %site..." IDS_SHELL_EXTENSION_MISMATCH_STRING Index: src/common/version.h =================================================================== diff -u -r50ad2dc9f0b42ba432bb54e4a042582277410773 -r1f27a2022090cf7aaf827a3f1ad90d6fe0038518 --- src/common/version.h (.../version.h) (revision 50ad2dc9f0b42ba432bb54e4a042582277410773) +++ src/common/version.h (.../version.h) (revision 1f27a2022090cf7aaf827a3f1ad90d6fe0038518) @@ -33,7 +33,8 @@ // copyright information #define COPYRIGHT_INFO "Copyright (C) 2001-2016 J�zef Starosczyk" -#define PRODUCT_SITE "http://www.copyhandler.com" -#define CONTACT_INFO "http://www.copyhandler.com/contact" +#define PRODUCT_SITE "http://www.copyhandler.com" +#define UPDATE_CHECK_LINK "https://www.copyhandler.com/update-check" +#define CONTACT_INFO "http://www.copyhandler.com/contact" #endif