/*
Module : ShellLink.cpp
Purpose: NSIS Plug-in for retriving shell link information
Created: 12/16/2003
Last Update: 01/14/2004
                          
Copyright (c) 2004 Angelo Mandato.  
See ShellLink.html for more information


Modified: 21/09/2005
Author:   Shengalts Aleksander aka Instructor (Shengalts@mail.ru)
Changes:  -code has been rewritten
          -added functions to change shell link information
          -reduced dll size 44Kb -> 4Kb

Modified by The Tor Project to remove the unneeded ConvFunc.h and updated the
include for pluginapi.h.
The original plugin is available at https://nsis.sourceforge.io/ShellLink_plug-in.

© 2004 Angelo Mandato

This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.

Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim
   that you wrote the original software. If you use this software in a product,
   an acknowledgment in the product documentation would be appreciated but is
   not required.
2. Altered versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This notice may not be removed or altered from any distribution.

*/

//  Uncomment for debugging message boxes
//#define SHELLLINK_DEBUG

#include <windows.h>
#include <shlobj.h>

#define xatoi
#include "nsis/pluginapi.h"

#define NSISFUNC(name) extern "C" void __declspec(dllexport) name(HWND hWndParent, int string_size, TCHAR* variables, stack_t** stacktop, extra_parameters* extra)

#define SHELLLINKTYPE_GETARGS 1
#define SHELLLINKTYPE_GETDESC 2
#define SHELLLINKTYPE_GETHOTKEY 3
#define SHELLLINKTYPE_GETICONLOC 4
#define SHELLLINKTYPE_GETICONINDEX 5
#define SHELLLINKTYPE_GETPATH 6
#define SHELLLINKTYPE_GETSHOWMODE 7
#define SHELLLINKTYPE_GETWORKINGDIR 8
#define SHELLLINKTYPE_SETARGS 9
#define SHELLLINKTYPE_SETDESC 10
#define SHELLLINKTYPE_SETHOTKEY 11
#define SHELLLINKTYPE_SETICONLOC 12
#define SHELLLINKTYPE_SETICONINDEX 13
#define SHELLLINKTYPE_SETPATH 14
#define SHELLLINKTYPE_SETSHOWMODE 15
#define SHELLLINKTYPE_SETWORKINGDIR 16
#define SHELLLINKTYPE_SETRUNASADMIN 17

void ShortCutData(int nType);

//Get
NSISFUNC(GetShortCutArgs)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETARGS);
}

NSISFUNC(GetShortCutDescription)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETDESC);
}

NSISFUNC(GetShortCutHotkey)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETHOTKEY);
}

NSISFUNC(GetShortCutIconLocation)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETICONLOC);
}

NSISFUNC(GetShortCutIconIndex)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETICONINDEX);
}

NSISFUNC(GetShortCutTarget)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETPATH);
}

NSISFUNC(GetShortCutShowMode)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETSHOWMODE);
}

NSISFUNC(GetShortCutWorkingDirectory)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_GETWORKINGDIR);
}

//Set
NSISFUNC(SetShortCutArgs)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETARGS);
}

NSISFUNC(SetShortCutDescription)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETDESC);
}

NSISFUNC(SetShortCutHotkey)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETHOTKEY);
}

NSISFUNC(SetShortCutIconLocation)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETICONLOC);
}

NSISFUNC(SetShortCutIconIndex)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETICONINDEX);
}

NSISFUNC(SetShortCutTarget)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETPATH);
}

NSISFUNC(SetShortCutShowMode)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETSHOWMODE);
}

NSISFUNC(SetShortCutWorkingDirectory)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETWORKINGDIR);
}

NSISFUNC(SetRunAsAdministrator)
{
  EXDLL_INIT();
	ShortCutData(SHELLLINKTYPE_SETRUNASADMIN);
}

void ShortCutData(int nType)
{
	HRESULT hRes;
	IShellLink* psl;
	IPersistFile* ppf;

  int nBuf;
  WORD wHotkey;
  TCHAR* szBuf = (TCHAR*)LocalAlloc(LPTR, sizeof(TCHAR)*MAX_PATH);
  TCHAR* szBuf2 = (TCHAR*)LocalAlloc(LPTR, sizeof(TCHAR)*MAX_PATH);

	popstring(szBuf);
	if (nType > SHELLLINKTYPE_GETWORKINGDIR) popstring(szBuf2);

	hRes=CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*) &psl);
	if (hRes == S_OK)
	{
		hRes=psl->QueryInterface(IID_IPersistFile, (LPVOID*) &ppf);
		if (hRes == S_OK)
		{
#ifdef UNICODE
			hRes=ppf->Load(szBuf, STGM_READWRITE);
#else
      WCHAR* wszPath = (WCHAR*)LocalAlloc(LPTR, sizeof(WCHAR)*MAX_PATH);
			MultiByteToWideChar(CP_ACP, 0, szBuf, -1, wszPath, MAX_PATH);
			hRes=ppf->Load(wszPath, STGM_READWRITE);
      LocalFree(wszPath);
#endif
			if (hRes == S_OK)
			{
				if (nType <= SHELLLINKTYPE_GETWORKINGDIR)
				{
					//Get
					switch(nType)
					{
						case SHELLLINKTYPE_GETARGS:
						{
							hRes=psl->GetArguments(szBuf, MAX_PATH);
							if (hRes != S_OK) szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETDESC: 
						{
							hRes=psl->GetDescription(szBuf, MAX_PATH);
							if (hRes != S_OK) szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETHOTKEY: 
						{
							hRes=psl->GetHotkey(&wHotkey);
							if (hRes == S_OK) wsprintf(szBuf, TEXT("%d"), wHotkey);
							else szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETICONLOC: 
						{
							hRes=psl->GetIconLocation(szBuf, MAX_PATH, &nBuf);
							if (hRes != S_OK) szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETICONINDEX: 
						{
							hRes=psl->GetIconLocation(szBuf, MAX_PATH, &nBuf);
							if (hRes == S_OK) wsprintf(szBuf, TEXT("%d"), nBuf);
							else szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETPATH: 
						{
							WIN32_FIND_DATA fd;

							hRes=psl->GetPath(szBuf, MAX_PATH, &fd, SLGP_UNCPRIORITY);
							if (hRes != S_OK) szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETSHOWMODE: 
						{
							hRes=psl->GetShowCmd(&nBuf);
							if (hRes == S_OK) wsprintf(szBuf, TEXT("%d"), nBuf);
							else szBuf[0]='\0';
						}; break;
						case SHELLLINKTYPE_GETWORKINGDIR:
						{ 
							hRes=psl->GetWorkingDirectory(szBuf, MAX_PATH);
							if (hRes != S_OK) szBuf[0]='\0';
						}; break;
					}
				}
				else
				{
					//Set
					switch(nType)
					{
						case SHELLLINKTYPE_SETARGS:
						{
							hRes=psl->SetArguments(szBuf2);
						}; break;
						case SHELLLINKTYPE_SETDESC: 
						{
							hRes=psl->SetDescription(szBuf2);
						}; break;
						case SHELLLINKTYPE_SETHOTKEY:
						{
							wHotkey=(unsigned short)myatoi(szBuf2);
							hRes=psl->SetHotkey(wHotkey);
						}; break;
						case SHELLLINKTYPE_SETICONLOC:
						{
							hRes=psl->GetIconLocation(szBuf, MAX_PATH, &nBuf);
							if (hRes == S_OK)
								hRes=psl->SetIconLocation(szBuf2, nBuf);
						}; break;
						case SHELLLINKTYPE_SETICONINDEX: 
						{
							int nBuf2;
							nBuf=myatoi(szBuf2);

							hRes=psl->GetIconLocation(szBuf, MAX_PATH, &nBuf2);
							if (hRes == S_OK)
								hRes=psl->SetIconLocation(szBuf, nBuf);
						}; break;
						case SHELLLINKTYPE_SETPATH: 
						{
							hRes=psl->SetPath(szBuf2);
						}; break;
						case SHELLLINKTYPE_SETSHOWMODE: 
						{
							nBuf=myatoi(szBuf2);
							hRes=psl->SetShowCmd(nBuf);
						}; break;
						case SHELLLINKTYPE_SETWORKINGDIR:
						{
							hRes=psl->SetWorkingDirectory(szBuf2);
						}; break;
						case SHELLLINKTYPE_SETRUNASADMIN:
						{
              IShellLinkDataList* pdl;
              hRes=psl->QueryInterface(IID_IShellLinkDataList, (void**)&pdl);
              if (hRes == S_OK)
              {
                DWORD dwFlags = 0;
							  hRes=pdl->GetFlags(&dwFlags);
                if (hRes == S_OK && (dwFlags & SLDF_RUNAS_USER) != SLDF_RUNAS_USER)
                  hRes=pdl->SetFlags(dwFlags | SLDF_RUNAS_USER);
                pdl->Release();
              }
						}; break;
					}
					if (hRes == S_OK) hRes=ppf->Save(NULL, FALSE);
					#ifdef SHELLLINK_DEBUG
					else MessageBox(hwndParent, TEXT("ERROR: Save()"), TEXT("ShellLink plug-in"), MB_OK);
					#endif
				}
			}
			#ifdef SHELLLINK_DEBUG
			else MessageBox(hwndParent, TEXT("ERROR: Load()"), TEXT("ShellLink plug-in"), MB_OK);
			#endif
		}
		#ifdef SHELLLINK_DEBUG
		else MessageBox(hwndParent, TEXT("CShellLink::Initialise, Failed in call to QueryInterface for IPersistFile, HRESULT was %x\n"), TEXT("ShellLink plug-in"), MB_OK);
		#endif

		// Cleanup:
		if (ppf) ppf->Release();
		if (psl) psl->Release();
	}
	#ifdef SHELLLINK_DEBUG
	else MessageBox(hwndParent, TEXT("ERROR: CoCreateInstance()"), TEXT("ShellLink plug-in"), MB_OK);
	#endif

	if (hRes == S_OK)
	{
		if (nType <= SHELLLINKTYPE_GETWORKINGDIR) pushstring(szBuf);
		else pushstring(TEXT("0"));
	}
	else
	{
		if (nType <= SHELLLINKTYPE_GETWORKINGDIR) pushstring(TEXT(""));
		else pushstring(TEXT("-1"));
	}

  LocalFree(szBuf);
  LocalFree(szBuf2);
}

BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
	return TRUE;
}
