Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 173 additions & 1 deletion src/winmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ static constexpr wchar_t FLAG_HELP[] = L"--help";

static constexpr wchar_t FLAG_UUZIP[] = L"-unzipTo";
static constexpr wchar_t FLAG_CLEANUP[] = L"-clean";
static constexpr wchar_t FLAG_DEACTIVATE[] = L"-deactivate";
static constexpr wchar_t FLAG_ACTIVATE[] = L"-activate";

static constexpr wchar_t FLAG_INFOURL[] = L"-infoUrl=";
static constexpr wchar_t FLAG_FORCEDOMAIN[] = L"-forceDomain=";
Expand Down Expand Up @@ -171,6 +173,16 @@ gup -unzipTo [-clean] FOLDER_TO_ACTION ZIP_URL\r\n\
-unzipTo : Download zip file from ZIP_URL then unzip it into FOLDER_TO_ACTION.\r\n\
ZIP_URL : The URL to download zip file.\r\n\
FOLDER_TO_ACTION : The folder where we clean or/and unzip to.\r\n\
\r\n\
Plugin deactivate/activate mode:\r\n\
\r\n\
gup -deactivate \"appPath2Launch\" \"SRC_ROOT\" \"DEST_ROOT\" \"folder1\" \"folder2\" ...\r\n\
gup -activate \"appPath2Launch\" \"SRC_ROOT\" \"DEST_ROOT\" \"folder1\" \"folder2\" ...\r\n\
\r\n\
-deactivate : Move the given plugin folders from SRC_ROOT (plugins dir)\r\n\
to DEST_ROOT (plugins_disabled dir).\r\n\
-activate : Move the given plugin folders from SRC_ROOT (plugins_disabled dir)\r\n\
back to DEST_ROOT (plugins dir).\r\n\
";

HFONT hCmdLineEditFont = nullptr;
Expand Down Expand Up @@ -508,6 +520,53 @@ bool deleteFileOrFolder(const wstring& f2delete)
return (res == 0);
};

// Moves a file or folder using SHFileOperation (FO_MOVE).
// If overwriteIfExists is true and fTo already exists, fTo is deleted first.
// Conflict resolution (asking the user Yes/No/Yes to all/No to all) is expected
// to happen upstream (in Notepad++, before it exits and launches gup), since
// gup itself runs headless after the host application has already closed.
bool moveFileOrFolder(const wstring& fFrom, const wstring& fTo, bool overwriteIfExists)
{
if (overwriteIfExists && ::PathFileExists(fTo.c_str()))
{
deleteFileOrFolder(fTo);
}

auto lenFrom = fFrom.length();
wchar_t* actionFrom = new wchar_t[lenFrom + 2];
lstrcpy(actionFrom, fFrom.c_str());
actionFrom[lenFrom] = 0;
actionFrom[lenFrom + 1] = 0;

auto lenTo = fTo.length();
wchar_t* actionTo = new wchar_t[lenTo + 2];
lstrcpy(actionTo, fTo.c_str());
actionTo[lenTo] = 0;
actionTo[lenTo + 1] = 0;

SHFILEOPSTRUCT fileOpStruct = { 0 };
fileOpStruct.hwnd = NULL;
fileOpStruct.pFrom = actionFrom;
fileOpStruct.pTo = actionTo;
fileOpStruct.wFunc = FO_MOVE;
fileOpStruct.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO;
fileOpStruct.fAnyOperationsAborted = false;
fileOpStruct.hNameMappings = NULL;
fileOpStruct.lpszProgressTitle = NULL;

int res = SHFileOperation(&fileOpStruct);
if (res != 0)
{
// beware that some of the SHFileOperation error codes are not the standard WIN32 ones
// (e.g. 124 (0x7C) here means DE_INVALIDFILES and not the usual ERROR_INVALID_LEVEL)
WRITE_LOG(GUP_LOG_FILENAME, L"moveFileOrFolder, SHFileOperation failed with error code: ", std::to_wstring(res).c_str());
}

delete[] actionFrom;
delete[] actionTo;
return (res == 0);
};


// unzipDestTo should be plugin home root + plugin folder name
// ex: %APPDATA%\..\local\Notepad++\plugins\myAwesomePlugin
Expand Down Expand Up @@ -1320,6 +1379,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int)
bool isHelp = isInList(FLAG_HELP, params);
bool isCleanUp = isInList(FLAG_CLEANUP, params);
bool isUnzip = isInList(FLAG_UUZIP, params);
bool isDeactivate = isInList(FLAG_DEACTIVATE, params);
bool isActivate = isInList(FLAG_ACTIVATE, params);

getParamVal('v', params, version);
getParamVal('p', params, customParam);
Expand Down Expand Up @@ -1512,7 +1573,118 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR lpszCmdLine, int)

return 0;
}


// deactivate plugin(s): move folders from plugins dir to plugins_disabled dir
// gup.exe -deactivate "appPath2Launch" "plugins_root" "disabled_root" "fold1" "fold2" ...
if (isDeactivate)
{
if (nbParam < 4)
{
WRITE_LOG(GUP_LOG_FILENAME, L"-1 in plugin updater's part - if (isDeactivate): ", L"nbParam < 4");
return -1;
}

wstring prog2Launch = params[0];
wstring srcRoot = params[1];
wstring destRoot = params[2];

#ifdef _DEBUG
// Don't check any thing in debug mode
#else
// Check signature of the launched program, with the same certif as gup.exe
SecurityGuard securityGuard4PluginsInstall;

if (!securityGuard4PluginsInstall.initFromSelfCertif())
{
securityGuard.writeSecurityError(L"Above certificate init error from \"gup -deactivate\" (deactivate plugins)", L"");
return -1;
}

bool isSecured = securityGuard4PluginsInstall.verifySignedBinary(prog2Launch.c_str());
if (!isSecured)
{
securityGuard.writeSecurityError(L"Above certificate verification error from \"gup -deactivate\" (deactivate plugins)", L"");
return -1;
}
#endif

if (!::PathFileExists(destRoot.c_str()))
{
::CreateDirectory(destRoot.c_str(), NULL);
}

for (size_t i = 3; i < nbParam; ++i)
{
wstring srcPath = srcRoot;
::PathAppend(srcPath, params[i]);

wstring destPath = destRoot;
::PathAppend(destPath, params[i]);

// Overwrite conflicts are already resolved by the caller (Notepad++) before
// invoking gup: every folder name reaching this point has been confirmed by
// the user, so it is safe (and necessary) to overwrite an existing destination.
moveFileOrFolder(srcPath, destPath, true);
}

safeLaunchAsUser(prog2Launch);

return 0;
}

// activate plugin(s): move folders from plugins_disabled dir back to plugins dir
// gup.exe -activate "appPath2Launch" "disabled_root" "plugins_root" "fold1" "fold2" ...
if (isActivate)
{
if (nbParam < 4)
{
WRITE_LOG(GUP_LOG_FILENAME, L"-1 in plugin updater's part - if (isActivate): ", L"nbParam < 4");
return -1;
}

wstring prog2Launch = params[0];
wstring srcRoot = params[1];
wstring destRoot = params[2];

#ifdef _DEBUG
// Don't check any thing in debug mode
#else
// Check signature of the launched program, with the same certif as gup.exe
SecurityGuard securityGuard4PluginsInstall;

if (!securityGuard4PluginsInstall.initFromSelfCertif())
{
securityGuard.writeSecurityError(L"Above certificate init error from \"gup -activate\" (activate plugins)", L"");
return -1;
}

bool isSecured = securityGuard4PluginsInstall.verifySignedBinary(prog2Launch.c_str());
if (!isSecured)
{
securityGuard.writeSecurityError(L"Above certificate verification error from \"gup -activate\" (activate plugins)", L"");
return -1;
}
#endif

for (size_t i = 3; i < nbParam; ++i)
{
wstring srcPath = srcRoot;
::PathAppend(srcPath, params[i]);

wstring destPath = destRoot;
::PathAppend(destPath, params[i]);

// Overwrite conflicts are already resolved by the caller (Notepad++) before
// invoking gup: every folder name reaching this point has been confirmed by
// the user, so it is safe (and necessary) to overwrite an existing destination.
moveFileOrFolder(srcPath, destPath, true);
}

safeLaunchAsUser(prog2Launch);

return 0;
}

// update:
// gup.exe -unzip -clean "appPath2Launch" "dest_folder" "pluginFolderName1 http://pluginFolderName1/pluginFolderName1.zip sha256Hash1" "pluginFolderName2 http://pluginFolderName2/pluginFolderName2.zip sha256Hash2" "plugin Folder Name3 http://plugin_Folder_Name3/plugin_Folder_Name3.zip sha256Hash3"
// gup.exe -unzip -clean "c:\npp\notepad++.exe" "c:\donho\notepad++\plugins" "toto http://toto/toto.zip 7c31a97b..." "ti et ti http://ti_ti/ti_ti.zip 087a0591..." "tata http://tata/tata.zip 2e9766c..."
Expand Down