diff options
Diffstat (limited to 'mmc_updater/src')
59 files changed, 4906 insertions, 0 deletions
diff --git a/mmc_updater/src/AppInfo.cpp b/mmc_updater/src/AppInfo.cpp new file mode 100644 index 00000000..a5a9bb63 --- /dev/null +++ b/mmc_updater/src/AppInfo.cpp @@ -0,0 +1,23 @@ +#include "AppInfo.h" + +#include "FileUtils.h" +#include "Platform.h" +#include "StringUtils.h" +#include "StandardDirs.h" + +#include <iostream> + +std::string AppInfo::logFilePath() +{ + return StandardDirs::appDataPath(organizationName(),appName()) + '/' + "update-log.txt"; +} + +std::string AppInfo::updateErrorMessage(const std::string& details) +{ + std::string result = "There was a problem installing the update:\n\n"; + result += details; + result += "\n\nYou can try downloading and installing the latest version of " + "MultiMC from http://multimc.org/"; + return result; +} + diff --git a/mmc_updater/src/AppInfo.h b/mmc_updater/src/AppInfo.h new file mode 100644 index 00000000..51d95886 --- /dev/null +++ b/mmc_updater/src/AppInfo.h @@ -0,0 +1,39 @@ +#pragma once + +#include <string> + +/** This class provides project-specific updater properties, + * such as the name of the application being updated and + * the path to log details of the update install to. + */ +class AppInfo +{ + public: + // Basic application information + static std::string name(); + static std::string appName(); + static std::string organizationName(); + + static std::string logFilePath(); + + /** Returns a message to display to the user in the event + * of a problem installing the update. + */ + static std::string updateErrorMessage(const std::string& details); +}; + +inline std::string AppInfo::name() +{ + return "MultiMC Updater"; +} + +inline std::string AppInfo::appName() +{ + return "MultiMC"; +} + +inline std::string AppInfo::organizationName() +{ + return "MultiMC Contributors"; +} + diff --git a/mmc_updater/src/CMakeLists.txt b/mmc_updater/src/CMakeLists.txt new file mode 100644 index 00000000..9b39bb83 --- /dev/null +++ b/mmc_updater/src/CMakeLists.txt @@ -0,0 +1,121 @@ + +add_subdirectory(tests) + +find_package(Threads REQUIRED) +include(GenerateCppResourceFile) + +set (UPDATER_SOURCES + AppInfo.cpp + AppInfo.h + DirIterator.cpp + DirIterator.h + FileUtils.cpp + FileUtils.h + Log.cpp + Log.h + ProcessUtils.cpp + ProcessUtils.h + StandardDirs.cpp + StandardDirs.h + UpdateDialog.cpp + UpdateInstaller.cpp + UpdateInstaller.h + UpdateScript.cpp + UpdateScript.h + UpdaterOptions.cpp + UpdaterOptions.h +) + +add_definitions(-DTIXML_USE_STL) + +if (WIN32) + set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogWin32.cpp UpdateDialogWin32.h) +endif() + +if (UNIX) + set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogAscii.cpp UpdateDialogAscii.h) + add_definitions(-Wall -Werror -Wconversion) +if (APPLE) + set(MAC_DOCK_ICON_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_dock_icon.cpp) + set(MAC_INFO_PLIST_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_info_plist.cpp) + generate_cpp_resource_file(resource_macdockicon ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac.icns ${MAC_DOCK_ICON_CPP_FILE}) + generate_cpp_resource_file(resource_macplist ${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist ${MAC_INFO_PLIST_FILE}) + set(UPDATER_SOURCES ${UPDATER_SOURCES} + MacBundle.h + MacBundle.cpp + StandardDirs.mm + StlSymbolsLeopard.cpp + UpdateDialogCocoa.mm + UpdateDialogCocoa.h + mac_dock_icon.cpp + mac_info_plist.cpp + ) +else() # linuxes and other similar systems + find_package(GTK2 REQUIRED gtk) + include_directories(${GTK2_INCLUDE_DIRS}) + add_library(updatergtk SHARED UpdateDialogGtk.cpp UpdateDialogGtk.h) + target_link_libraries(updatergtk ${GTK2_LIBRARIES}) + + # embed the GTK helper library into the updater binary. + # At runtime it will be extracted and loaded if the + # GTK libraries are available + get_property(GTK_UPDATER_LIB TARGET updatergtk PROPERTY LOCATION) + set(GTK_BIN_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/libupdatergtk.cpp) + generate_cpp_resource_file(resource_updatergtk ${GTK_UPDATER_LIB} ${GTK_BIN_CPP_FILE}) + add_dependencies(resource_updatergtk updatergtk) + + set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogGtkFactory.cpp UpdateDialogGtkFactory.h ${GTK_BIN_CPP_FILE}) +endif() +endif() + +add_library(updatershared STATIC ${UPDATER_SOURCES}) + +target_link_libraries(updatershared + anyoption + tinyxml +) + +if (UNIX) + if (APPLE) + find_library(COCOA_LIBRARY Cocoa) + find_library(SECURITY_LIBRARY Security) + target_link_libraries(updatershared ${SECURITY_LIBRARY} ${COCOA_LIBRARY}) + else() + add_dependencies(updatershared resource_updatergtk) + endif() + target_link_libraries(updatershared pthread dl) +endif() + +if (WIN32) + set(EXE_FLAGS WIN32 resources/updater.rc) +endif() + +add_executable(updater ${EXE_FLAGS} main.cpp) + +target_link_libraries(updater + updatershared +) + + +#### Updater Executable #### +IF(WIN32) +INSTALL(TARGETS updater + BUNDLE DESTINATION . COMPONENT Runtime + LIBRARY DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION . COMPONENT Runtime +) +ENDIF() +IF(UNIX) +IF(APPLE) +INSTALL(TARGETS updater + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime +) +ELSE() +INSTALL(TARGETS updater + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime +) +ENDIF() +ENDIF() + diff --git a/mmc_updater/src/DirIterator.cpp b/mmc_updater/src/DirIterator.cpp new file mode 100644 index 00000000..a4604f05 --- /dev/null +++ b/mmc_updater/src/DirIterator.cpp @@ -0,0 +1,85 @@ +#include "DirIterator.h" + +#include "Log.h" +#include "StringUtils.h" + +#ifdef PLATFORM_UNIX + #include <dirent.h> +#endif + +#include <string.h> + +DirIterator::DirIterator(const char* path) +{ + m_path = path; + +#ifdef PLATFORM_UNIX + m_dir = opendir(path); + m_entry = 0; +#else + // to list the contents of a directory, the first + // argument to FindFirstFile needs to be a wildcard + // of the form: C:\path\to\dir\* + std::string searchPath = m_path; + if (!endsWith(searchPath,"/")) + { + searchPath.append("/"); + } + searchPath.append("*"); + m_findHandle = FindFirstFile(searchPath.c_str(),&m_findData); + m_firstEntry = true; +#endif +} + +DirIterator::~DirIterator() +{ +#ifdef PLATFORM_UNIX + closedir(m_dir); +#else + FindClose(m_findHandle); +#endif +} + +bool DirIterator::next() +{ +#ifdef PLATFORM_UNIX + m_entry = readdir(m_dir); + return m_entry != 0; +#else + bool result; + if (m_firstEntry) + { + m_firstEntry = false; + return m_findHandle != INVALID_HANDLE_VALUE; + } + else + { + result = FindNextFile(m_findHandle,&m_findData); + } + return result; +#endif +} + +std::string DirIterator::fileName() const +{ +#ifdef PLATFORM_UNIX + return m_entry->d_name; +#else + return m_findData.cFileName; +#endif +} + +std::string DirIterator::filePath() const +{ + return m_path + '/' + fileName(); +} + +bool DirIterator::isDir() const +{ +#ifdef PLATFORM_UNIX + return m_entry->d_type == DT_DIR; +#else + return (m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +#endif +} + diff --git a/mmc_updater/src/DirIterator.h b/mmc_updater/src/DirIterator.h new file mode 100644 index 00000000..f3fbb955 --- /dev/null +++ b/mmc_updater/src/DirIterator.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Platform.h" + +#include <string> + +#ifdef PLATFORM_UNIX +#include <dirent.h> +#endif + +/** Simple class for iterating over the files in a directory + * and reporting their names and types. + */ +class DirIterator +{ + public: + DirIterator(const char* path); + ~DirIterator(); + + // iterate to the next entry in the directory + bool next(); + + // methods to return information about + // the current entry + std::string fileName() const; + std::string filePath() const; + bool isDir() const; + + private: + std::string m_path; + +#ifdef PLATFORM_UNIX + DIR* m_dir; + dirent* m_entry; +#endif + +#ifdef PLATFORM_WINDOWS + HANDLE m_findHandle; + WIN32_FIND_DATA m_findData; + bool m_firstEntry; +#endif +}; + diff --git a/mmc_updater/src/FileUtils.cpp b/mmc_updater/src/FileUtils.cpp new file mode 100644 index 00000000..10435e49 --- /dev/null +++ b/mmc_updater/src/FileUtils.cpp @@ -0,0 +1,557 @@ +#include "FileUtils.h" + +#include "DirIterator.h" +#include "Log.h" +#include "Platform.h" +#include "StringUtils.h" + +#include <algorithm> +#include <assert.h> +#include <string.h> +#include <fstream> +#include <iostream> + +#ifdef PLATFORM_UNIX +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <libgen.h> +#endif + +FileUtils::IOException::IOException(const std::string& error) +{ + init(errno,error); +} + +FileUtils::IOException::IOException(int errorCode, const std::string& error) +{ + init(errorCode,error); +} + +void FileUtils::IOException::init(int errorCode, const std::string& error) +{ + m_error = error; + +#ifdef PLATFORM_UNIX + m_errorCode = errorCode; + + if (m_errorCode > 0) + { + m_error += " details: " + std::string(strerror(m_errorCode)); + } +#endif + +#ifdef PLATFORM_WINDOWS + m_errorCode = 0; + m_error += " GetLastError returned: " + intToStr(GetLastError()); +#endif +} + +FileUtils::IOException::~IOException() throw () +{ +} + +FileUtils::IOException::Type FileUtils::IOException::type() const +{ +#ifdef PLATFORM_UNIX + switch (m_errorCode) + { + case 0: + return NoError; + case EROFS: + return ReadOnlyFileSystem; + case ENOSPC: + return DiskFull; + default: + return Unknown; + } +#else + return Unknown; +#endif +} + +bool FileUtils::fileExists(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + struct stat fileInfo; + if (lstat(path,&fileInfo) != 0) + { + if (errno == ENOENT) + { + return false; + } + else + { + throw IOException("Error checking for file " + std::string(path)); + } + } + return true; +#else + DWORD result = GetFileAttributes(path); + if (result == INVALID_FILE_ATTRIBUTES) + { + return false; + } + return true; +#endif +} + +int FileUtils::fileMode(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + struct stat fileInfo; + if (stat(path,&fileInfo) != 0) + { + throw IOException("Error reading file permissions for " + std::string(path)); + } + return fileInfo.st_mode; +#else + // not implemented for Windows + return 0; +#endif +} + +void FileUtils::chmod(const char* path, int mode) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::chmod(path,static_cast<mode_t>(mode)) != 0) + { + throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(mode)); + } +#else + // TODO - Not implemented under Windows - all files + // get default permissions +#endif +} + +void FileUtils::moveFile(const char* src, const char* dest) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (rename(src,dest) != 0) + { + throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest)); + } +#else + if (!MoveFile(src,dest)) + { + throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest)); + } +#endif +} + +void FileUtils::mkpath(const char* dir) throw (IOException) +{ + std::string currentPath; + std::istringstream stream(dir); + while (!stream.eof()) + { + std::string segment; + std::getline(stream,segment,'/'); + currentPath += segment; + if (!currentPath.empty() && !fileExists(currentPath.c_str())) + { + mkdir(currentPath.c_str()); + } + currentPath += '/'; + } +} + +void FileUtils::mkdir(const char* dir) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::mkdir(dir,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) + { + throw IOException("Unable to create directory " + std::string(dir)); + } +#else + if (!CreateDirectory(dir,0 /* default security attributes */)) + { + throw IOException("Unable to create directory " + std::string(dir)); + } +#endif +} + +void FileUtils::rmdir(const char* dir) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::rmdir(dir) != 0) + { + throw IOException("Unable to remove directory " + std::string(dir)); + } +#else + if (!RemoveDirectory(dir)) + { + throw IOException("Unable to remove directory " + std::string(dir)); + } +#endif +} + +void FileUtils::createSymLink(const char* link, const char* target) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (symlink(target,link) != 0) + { + throw IOException("Unable to create symlink " + std::string(link) + " to " + std::string(target)); + } +#else + // symlinks are not supported under Windows (at least, not universally. + // Windows Vista and later do actually support symlinks) + LOG(Warn,"Skipping symlink creation - not implemented in Windows"); +#endif +} + +void FileUtils::removeFile(const char* src) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (unlink(src) != 0) + { + if (errno != ENOENT) + { + throw IOException("Unable to remove file " + std::string(src)); + } + } +#else + if (!DeleteFile(src)) + { + if (GetLastError() == ERROR_ACCESS_DENIED) + { + // if another process is using the file, try moving it to + // a temporary directory and then + // scheduling it for deletion on reboot + std::string tempDeletePathBase = tempPath(); + tempDeletePathBase += '/'; + tempDeletePathBase += fileName(src); + + int suffix = 0; + std::string tempDeletePath = tempDeletePathBase; + while (fileExists(tempDeletePath.c_str())) + { + ++suffix; + tempDeletePath = tempDeletePathBase + '_' + intToStr(suffix); + } + + LOG(Warn,"Unable to remove file " + std::string(src) + " - it may be in use. Moving to " + + tempDeletePath + " and scheduling delete on reboot."); + moveFile(src,tempDeletePath.c_str()); + MoveFileEx(tempDeletePath.c_str(),0,MOVEFILE_DELAY_UNTIL_REBOOT); + } + else if (GetLastError() != ERROR_FILE_NOT_FOUND) + { + throw IOException("Unable to remove file " + std::string(src)); + } + } +#endif +} + +std::string FileUtils::fileName(const char* path) +{ +#ifdef PLATFORM_UNIX + char* pathCopy = strdup(path); + std::string basename = ::basename(pathCopy); + free(pathCopy); + return basename; +#else + char baseName[MAX_PATH]; + char extension[MAX_PATH]; + _splitpath_s(path, + 0, /* drive */ + 0, /* drive length */ + 0, /* dir */ + 0, /* dir length */ + baseName, + MAX_PATH, /* baseName length */ + extension, + MAX_PATH /* extension length */ + ); + return std::string(baseName) + std::string(extension); +#endif +} + +std::string FileUtils::dirname(const char* path) +{ +#ifdef PLATFORM_UNIX + char* pathCopy = strdup(path); + std::string dirname = ::dirname(pathCopy); + free(pathCopy); + return dirname; +#else + char drive[3]; + char dir[MAX_PATH]; + + _splitpath_s(path, + drive, /* drive */ + 3, /* drive length */ + dir, + MAX_PATH, /* dir length */ + 0, /* filename */ + 0, /* filename length */ + 0, /* extension */ + 0 /* extension length */ + ); + + std::string result; + if (drive[0]) + { + result += std::string(drive); + } + result += dir; + + return result; +#endif +} + +void FileUtils::touch(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + // see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html + // + // we use utimes/futimes instead of utimensat/futimens for compatibility + // with older Linux and Mac + + if (fileExists(path)) + { + utimes(path,0 /* use current date/time */); + } + else + { + int fd = creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd != -1) + { + futimes(fd,0 /* use current date/time */); + close(fd); + } + else + { + throw IOException("Unable to touch file " + std::string(path)); + } + } +#else + HANDLE result = CreateFile(path,GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (result == INVALID_HANDLE_VALUE) + { + throw IOException("Unable to touch file " + std::string(path)); + } + else + { + CloseHandle(result); + } +#endif +} + +void FileUtils::rmdirRecursive(const char* path) throw (IOException) +{ + // remove dir contents + DirIterator dir(path); + while (dir.next()) + { + std::string name = dir.fileName(); + if (name != "." && name != "..") + { + if (dir.isDir()) + { + rmdir(dir.filePath().c_str()); + } + else + { + removeFile(dir.filePath().c_str()); + } + } + } + + // remove the directory itself + rmdir(path); +} + +std::string FileUtils::canonicalPath(const char* path) +{ +#ifdef PLATFORM_UNIX + // on Linux and Mac OS 10.6, realpath() can allocate the required + // amount of memory automatically, however Mac OS 10.5 does not support + // this, so we used a fixed-sized buffer on all platforms + char canonicalPathBuffer[PATH_MAX+1]; + if (realpath(path,canonicalPathBuffer) != 0) + { + return std::string(canonicalPathBuffer); + } + else + { + throw IOException("Error reading canonical path for " + std::string(path)); + } +#else + throw IOException("canonicalPath() not implemented"); +#endif +} + +std::string FileUtils::toWindowsPathSeparators(const std::string& str) +{ + std::string result = str; + std::replace(result.begin(),result.end(),'/','\\'); + return result; +} + +std::string FileUtils::toUnixPathSeparators(const std::string& str) +{ + std::string result = str; + std::replace(result.begin(),result.end(),'\\','/'); + return result; +} + +std::string FileUtils::tempPath() +{ +#ifdef PLATFORM_UNIX + std::string tmpDir(notNullString(getenv("TMPDIR"))); + if (tmpDir.empty()) + { + tmpDir = "/tmp"; + } + return tmpDir; +#else + char buffer[MAX_PATH+1]; + GetTempPath(MAX_PATH+1,buffer); + return toUnixPathSeparators(buffer); +#endif +} + +bool startsWithDriveLetter(const char* path) +{ + return strlen(path) >= 2 && + (isalpha(path[0])) && |
