#include "stdafx.h" #include #include "DenAgent.h" #include "DownloaderDlg.h" #include "AutoUpdate.h" static LPCSTR updateList = "updatelist.xml" ; // DownloadAgent /* static */ bool DownloadAgent::downloadFile(std::string remoteFile, std::string localFile) { // Perform a direct, one time download of a file cDownloaderDlg dlg; dlg.addFile(remoteFile, localFile); dlg.DoModal(); return (dlg.GetDownloadStatus() == cDownloaderDlg::DownloadStatus::DOWNLOAD_SUCCEEDED); } DownloadAgent::DownloadAgent() { m_ScheduledDownloads.clear(); } void DownloadAgent::scheduleDownload(std::string remoteFile, std::string localFile) { // Encapsulate this download in a download slot, and add it to the schedule list DownloadSlot slot; slot.remoteFile = remoteFile; slot.localFile = localFile; m_ScheduledDownloads.push_back(slot); } bool DownloadAgent::runDownloads() { if (!m_ScheduledDownloads.size()) return false; cDownloaderDlg dlg; // Add each schduled download to our downloader std::list::iterator it; for (it = m_ScheduledDownloads.begin(); it != m_ScheduledDownloads.end(); ++it) { DownloadSlot slot = *it; dlg.addFile(slot.remoteFile, slot.localFile); } // And run them dlg.DoModal(); return (dlg.GetDownloadStatus() == cDownloaderDlg::DownloadStatus::DOWNLOAD_SUCCEEDED); } // AutoUpdateSource AutoUpdateSource::AutoUpdateSource(std::string decalDir, AutoUpdateType type, std::string localTarget, std::string remoteSource, std::string requiredVersion) : m_DecalDir(decalDir) , m_LocalTarget(localTarget) , m_RequiredVersion(requiredVersion) , m_RemoteSource(remoteSource) , m_Type(type) { } bool AutoUpdateSource::needsUpdating() { USES_CONVERSION; // Get the version of the local file // Make sure the file exists OFSTRUCT targetFile; std::string localTarget = m_DecalDir + "/" + m_LocalTarget; if (OpenFile(localTarget.c_str(), &targetFile, OF_EXIST) == HFILE_ERROR) { // It didn't exist, we must update return true; } if (m_RequiredVersion.length() == 0) { // If we don't have a remote version, assume we want to update return true; } int reqMajor(0), reqMinor(0), reqPatch(0), reqBuild(0) ; sscanf(m_RequiredVersion.c_str(), "%i.%i.%i.%i", &reqMajor, &reqMinor, &reqPatch, &reqBuild); if (m_Type == AU_TYPE_XML) { // Extract the version node from an XML document MSXML::IXMLDOMDocumentPtr pDoc; try { pDoc.CreateInstance (__uuidof(MSXML::DOMDocument), NULL, CLSCTX_INPROC_SERVER); pDoc->async = false; // Construct a list of update targets based on the XML contents if (pDoc->load(static_cast(localTarget.c_str()))) { // Read the autoupdate version string // MSXML::IXMLDOMNodePtr pVersionNode = pDoc->selectSingleNode ( _T( "/*/@version" ) ); MSXML::IXMLDOMElementPtr pVersionNode = pDoc->selectSingleNode ("//revision"); if (pVersionNode != NULL) { // _variant_t vXMLVer = pVersionNode->text; _variant_t vXMLVer = pVersionNode->getAttribute("version") ; if (vXMLVer.vt == VT_BSTR) { int locMajor(0), locMinor(0), locPatch(0), locBuild(0) ; sscanf(OLE2T(vXMLVer.bstrVal), "%i.%i.%i.%i", &locMajor, &locMinor, &locPatch, &locBuild); if (locMajor==reqMajor) { if (locMinor==reqMinor) { if (locPatch==reqPatch) { return locBuild::iterator it; for (it = m_Requirements.begin(); it != m_Requirements.end(); ++it) { UpdateRequirement ur = *it; localTarget = m_DecalDir + "/" + ur.sFile; sscanf(ur.sVers.c_str(), "%i.%i.%i.%i", &reqMajor, &reqMinor, &reqPatch, &reqBuild); if (isOutDatedVersion(localTarget, reqMajor, reqMinor, reqPatch, reqBuild)) { std::string sMsg = m_LocalTarget + " cannot be updated because one or more dependencies is outdated"; ::MessageBox(0, _T(sMsg.c_str()), NULL, S_OK); return false; } } return true; } else // File is newer, ignore requirements and exit { return false; } } // if anything went wrong or we were unable to tell whether it should be updated // or not...assume it needs updating. return true ; } std::string AutoUpdateSource::getSource() { return m_RemoteSource; } std::string AutoUpdateSource::getDestination() { return m_LocalTarget; } AutoUpdateType AutoUpdateSource::getType() { return m_Type; } bool AutoUpdateSource::isOutDatedVersion(std::string sFile, int nMajor, int nMinor, int nPatch, int nBuild) { // Extract the DLL version DWORD dwDummy, dwVerSize; dwVerSize = ::GetFileVersionInfoSize(const_cast(sFile.c_str()), &dwDummy); if( dwVerSize == 0 ) return true; // if file vsn not available, assume needs updating else { BYTE *pbVersionInfo = reinterpret_cast< BYTE * >(::_alloca(dwVerSize)); ::GetFileVersionInfo(const_cast(sFile.c_str()), 0, dwVerSize, pbVersionInfo); VS_FIXEDFILEINFO *vffi; UINT nLength = sizeof(VS_FIXEDFILEINFO); if( ::VerQueryValue( pbVersionInfo, _T("\\"), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) ) { // Got it, so format it int locMajor(0), locMinor(0), locPatch(0), locBuild(0); locMajor = static_cast< int >( HIWORD( vffi->dwFileVersionMS ) ); locMinor = static_cast< int >( LOWORD( vffi->dwFileVersionMS ) ); locPatch = static_cast< int >( HIWORD( vffi->dwFileVersionLS ) ); locBuild = static_cast< int >( LOWORD( vffi->dwFileVersionLS ) ); if( locMajor == nMajor ) { if( locMinor == nMinor ) { if( locPatch == nPatch ) return locBuild < nBuild; else return locPatch < nPatch; } else return locMinor < nMinor ; } else return locMajor < nMajor ; } // problem with VerQueryValue... maybe updating will fix it. else return true; } } void AutoUpdateSource::AddRequirement(UpdateRequirement reqUpdate) { m_Requirements.push_back(reqUpdate); } // AutoUpdate AutoUpdate::AutoUpdate(std::string decalDir) { m_DecalDir = decalDir; m_LocalXML = ""; m_RemoteSources.clear(); } bool AutoUpdate::initialiseFromXML(std::string remoteXML) { USES_CONVERSION; m_RemoteXML = remoteXML + "/" + updateList ; // Download the remote XML document to a local file if (!DownloadAgent::downloadFile(m_RemoteXML, m_DecalDir + "/" + updateList)) { // We couldn't download the update list. We must bail here, or suffer! ::MessageBox(NULL, "Could not obtain updatelist.xml. Cannot update - Hosting server down?", NULL, MB_OK); return false; } // Parse it as an XML document MSXML::IXMLDOMDocumentPtr pDoc; // try { pDoc.CreateInstance (__uuidof(MSXML::DOMDocument), NULL, CLSCTX_INPROC_SERVER); pDoc->async = false; // Construct a list of update targets based on the XML contents std::string localXML = m_DecalDir + "/" + updateList ; if (!pDoc->load( static_cast(localXML.c_str()))) return false; // Read the autoupdate version string MSXML::IXMLDOMNodePtr pVersionNode = pDoc->selectSingleNode ( _T( "/*/@version" ) ); if (pVersionNode == NULL) { ::MessageBox(NULL,"No version information in updatelist.xml. Cannot update",NULL,MB_OK) ; return false; } // Check to make sure we understand this version char *version = OLE2T(pVersionNode->text); if (strcmp(version,"1.0.0.0")) { ::MessageBox(NULL,"Unknown version type in updatelist.xml. Cannot update",NULL,MB_OK) ; return false ; } // Now iterate over the Files entries, creating an AutoUpdateSource for each MSXML::IXMLDOMNodeListPtr xmlFiles = pDoc->selectNodes(_T("/updatelist/file")); if (xmlFiles.GetInterfacePtr() == NULL) return false; for (MSXML::IXMLDOMElementPtr xmlFile = xmlFiles->nextNode(); xmlFile.GetInterfacePtr() != NULL; xmlFile = xmlFiles->nextNode()) { std::string localFile, remoteFile, version; AutoUpdateType type; BSTR bResult; _variant_t vResult; // Read the type vResult = xmlFile->getAttribute("type"); if (vResult.vt != VT_BSTR) continue; bResult = vResult.bstrVal; char *cType = OLE2T(bResult); if (strcmp(cType, "xml") == 0) { type = AU_TYPE_XML; } else if (strcmp(cType, "dll") == 0) { type = AU_TYPE_DLL; } else if (strcmp(cType, "beta") == 0) { type = AU_TYPE_BETADLL ; } else { continue; } // Local name vResult = xmlFile->getAttribute("localname"); if (vResult.vt != VT_BSTR) continue; bResult = vResult.bstrVal; localFile = OLE2T(bResult); // Remote name vResult = xmlFile->getAttribute("remotename"); if (vResult.vt != VT_BSTR) continue; bResult = vResult.bstrVal; remoteFile = OLE2T(bResult); // And version vResult = xmlFile->getAttribute("version"); if (vResult.vt != VT_BSTR) continue; bResult = vResult.bstrVal; version = OLE2T(bResult); AutoUpdateSource aUSource(m_DecalDir, type, localFile, remoteFile, version); //if the source is dll || beta check for requirements if (type == AU_TYPE_DLL || type == AU_TYPE_BETADLL) { MSXML::IXMLDOMNodeListPtr xmlReqs = NULL;//xmlFile->selectNodes(_T("requirement")); //pDoc->selectNodes(_T("/updatelist/file")); xmlReqs = xmlFile->getElementsByTagName(_T("requirement")); if (xmlReqs.GetInterfacePtr() == NULL) continue; for (MSXML::IXMLDOMElementPtr xmlNode = xmlReqs->nextNode(); xmlNode.GetInterfacePtr() != NULL; xmlNode = xmlReqs->nextNode()) { std::string sLocalFile, sVersion; // Read the file name vResult = xmlNode->getAttribute("file"); if (vResult.vt != VT_BSTR) continue; bResult = vResult.bstrVal; sLocalFile = OLE2T(bResult); // Read the version vResult = xmlNode->getAttribute("version"); if (vResult.vt != VT_BSTR) continue; bResult = vResult.bstrVal; sVersion = OLE2T(bResult); UpdateRequirement uReq(sLocalFile, sVersion); aUSource.AddRequirement(uReq); } } // Add the source to the cache m_RemoteSources.push_back(aUSource); } // } catch (...) { // return false; // } return true; } bool AutoUpdate::needsUpdate() { // Ask all the dependancies if they want to update themselves // Check the versions of all our local XML documents to see if we need an update std::list::iterator it; for (it = m_RemoteSources.begin(); it != m_RemoteSources.end(); ++it) { AutoUpdateSource aUSource = *it; if (aUSource.needsUpdating()) return true; } // Nothing needed updating return false; } bool AutoUpdate::performUpdate() { // Construct a list of all the remote components that need updating std::list updateList; DownloadAgent dlAgent; bool DllUpdate = false ; bool BetaUpdate = false ; std::list::iterator itRemote = m_RemoteSources.begin();; while (itRemote != m_RemoteSources.end()) { AutoUpdateSource aUSource = *itRemote++; if (aUSource.needsUpdating()) { switch(aUSource.getType()) { case AU_TYPE_DLL: DllUpdate = true ; break ; case AU_TYPE_BETADLL: BetaUpdate = true ; break ; } if (aUSource.getType()==AU_TYPE_BETADLL) { BetaUpdate = true ; } updateList.push_back(aUSource); } } if (DllUpdate) { if ( MessageBox(NULL, "Updates to the Decal program are available.\n\n" "These are supported updates to the current release\n" "version of Decal.\n" "Installation of these updates is recommended.\n\n" "Do you wish to install these updates now?", "Decal Program Updates Available", MB_YESNO) == IDNO ){ DllUpdate = false ; } } if (BetaUpdate) { if (MessageBox(NULL, "Decal Beta updates are available.\n\n" "NOTE: Beta versions are not supported and have NOT\n" "been tested extensively.\n" " They can crash your system, and require a full\n" "re-install of the original Decal program.\n\n" "Do you wish to install these unsupported Beta updates?", "Beta program updates available", MB_YESNO|MB_DEFBUTTON2) == IDNO ) { BetaUpdate = false ; } } // Schedule downloads and processing for all selected types std::list::iterator itUpdates = updateList.begin();; while (itUpdates != updateList.end()) { AutoUpdateSource source = *itUpdates++ ; if ( (source.getType() == AU_TYPE_DLL && !DllUpdate) || (source.getType() == AU_TYPE_BETADLL && !BetaUpdate) ){ continue ; } dlAgent.scheduleDownload(source.getSource(), m_DecalDir + "\\" + source.getDestination() + ".tmp"); } // Download them to the local cache if (!dlAgent.runDownloads()) { // Something went horribly wrong return false; } // Delete the destinations and move the cached objects to their new homes itUpdates = updateList.begin() ; while (itUpdates != updateList.end()) { AutoUpdateSource aUSource = *itUpdates++; if ( (aUSource.getType() == AU_TYPE_DLL && !DllUpdate) || (aUSource.getType() == AU_TYPE_BETADLL && !BetaUpdate) ){ continue ; } std::string newFile = (m_DecalDir + "\\" + aUSource.getDestination()); std::string tmpFile = (m_DecalDir + "\\" + aUSource.getDestination() + ".tmp"); BOOL bResult = ::DeleteFile (newFile.c_str ()); DWORD dwLastError = 0; if (!bResult) { dwLastError = ::GetLastError (); } if (bResult || dwLastError == ERROR_FILE_NOT_FOUND) { MoveFile(tmpFile.c_str(), newFile.c_str()); // We may have to register DLLs if (aUSource.getType() == AU_TYPE_DLL || aUSource.getType() == AU_TYPE_BETADLL ) { HINSTANCE hDLL = LoadLibrary(newFile.c_str()); if (hDLL) { typedef CRuntimeClass * (*DLLREG)(); DLLREG DllReg = (DLLREG)GetProcAddress(hDLL, "DllRegisterServer"); if (DllReg != NULL) DllReg(); FreeLibrary(hDLL); } } } // Delete failed - file is in use else { MessageBox(NULL, "A file being updated is in use - close programs and try again.", "Decal", MB_OK); } } if (updateList.size() > 0) { MessageBox(NULL, "Your Decal components have been updated", "Decal", MB_OK); } else { MessageBox(NULL, "Your Decal components are already up to date", "Decal", MB_OK); } return true; }