diff --git a/src/gpga/CMakeLists.txt b/src/gpga/CMakeLists.txt index 1e8e8c3..3502ad1 100644 --- a/src/gpga/CMakeLists.txt +++ b/src/gpga/CMakeLists.txt @@ -1,28 +1,29 @@ # Copyright (C) 2018 Andre Heinecke # # This file is Free Software under the GNU GPL (v>=2) # and comes with ABSOLUTELY NO WARRANTY! # See LICENSE.txt for details. set(EXECUTABLE_NAME "gpga") set(EXECUTABLE_SRC flange.cpp ../util/debug.cpp ../util/w32-util.cpp gpga.def + gpgshellfolder.cpp ) add_library (${EXECUTABLE_NAME} ${EXECUTABLE_SRC} ) target_link_libraries(${EXECUTABLE_NAME} PRIVATE Gpgmepp ) set_target_properties(${EXECUTABLE_NAME} PROPERTIES LINK_FLAGS "-municode") set_target_properties(${EXECUTABLE_NAME} PROPERTIES PREFIX "") install(TARGETS ${EXECUTABLE_NAME} DESTINATION bin) diff --git a/src/gpga/flange.cpp b/src/gpga/flange.cpp index b77d6e5..15c5144 100644 --- a/src/gpga/flange.cpp +++ b/src/gpga/flange.cpp @@ -1,271 +1,277 @@ /* Copyright (C) 2018 by Andre Heinecke * * This file is Free Software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include "flange.h" #include "debug.h" #include "comhelp.h" #include "w32-util.h" +#include "gpgshellfolder.h" #include "shlobj.h" /** @file Code to flange to the Windows explorer. * * This file contains the Windows and COM API requirements * to properly work as an explorer extension. */ #ifndef INITGUID /* Include every header that defines a GUID below this macro. Otherwise the GUID's will only be declared and not defined. */ #define INITGUID #endif #include #define MY_CLSID_STR "CCD955E4-5C16-4A33-AFDA-A8947A94946C" DEFINE_GUID(MY_CLSID, 0xCCD955E4, 0x5C16, 0x4A33, 0xAF, 0xDA, 0xA8, 0x94, 0x7A, 0x94, 0x94, 0x6C); #define MY_PROGID "GpgArchive" static HINSTANCE s_hinst; static FlangeFactory *s_factory; static void init_logging() { static bool wasDone; if (!wasDone) { gpgrt_log_set_sink("c:\\tmp\\gpga.txt", nullptr, -1); wasDone = true; } } /* Here we go */ STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv) { init_logging (); - log_debug ("Get class object for clsid'" GUID_FMT "'", - GUID_ARG(riid)); if (!ppv) { return E_POINTER; } *ppv = nullptr; if (rclsid == MY_CLSID) { return FlangeFactory::instance()->QueryInterface(riid, ppv); } + log_debug ("Get class object for nonimp clsid'" GUID_FMT "'", + GUID_ARG(riid)); + return CLASS_E_CLASSNOTAVAILABLE; } STDMETHODIMP FlangeFactory::LockServer (BOOL lock) { if (lock) { AddRef(); } else { Release(); } return S_OK; } -STDMETHODIMP FlangeFactory::CreateInstance (LPUNKNOWN unknown, +STDMETHODIMP FlangeFactory::CreateInstance (LPUNKNOWN, REFIID riid, LPVOID* ppvObj) { - log_debug ("CreateInstance with id '" GUID_FMT "'", - GUID_ARG(riid)); + if (riid == IID_IShellFolder) { + auto folder = new GpgShellFolder(); + return folder->QueryInterface (riid, ppvObj); + } + log_debug ("CreateInstance with unimplemented id '" GUID_FMT "'", + GUID_ARG(riid)); return E_NOINTERFACE; } STDMETHODIMP FlangeFactory::QueryInterface (REFIID riid, void **ppv) { - log_debug ("QueryInterface:'" GUID_FMT "'", - GUID_ARG(riid)); - if (!ppv) { return E_POINTER; } *ppv = nullptr; /* The static casts ensure that the virtual function table layout of the returned object is correct. */ - if (riid == IID_IUnknown) + if (riid == IID_IUnknown) { *ppv = static_cast (this); - else if (riid == IID_IClassFactory) + } else if (riid == IID_IClassFactory) { *ppv = static_cast (this); - else + } else { + log_debug("QueryInterface with unknwon interface: '" GUID_FMT "'", + GUID_ARG(riid)); return E_NOINTERFACE; + } reinterpret_cast(*ppv)->AddRef (); return S_OK; } FlangeFactory *FlangeFactory::instance() { if (!s_factory) { s_factory = new FlangeFactory(); } return s_factory; } STDMETHODIMP_(ULONG) FlangeFactory::Release() { ULONG lCount = --m_lRef; if (!lCount) { delete this; } return lCount; } FlangeFactory::~FlangeFactory() { s_factory = nullptr; } STDAPI DllCanUnloadNow() { return s_factory == nullptr; } STDAPI DllUnregisterServer() { HKEY root_key; if (W32::isElevated()) { root_key = HKEY_LOCAL_MACHINE; } else { root_key = HKEY_CURRENT_USER; } log_debug("Un Registering shell extension."); RegDeleteKeyA(root_key, "Software\\Classes\\.gpga"); RegDeleteKeyA(root_key, "Software\\Classes\\GPGArchive"); RegDeleteKeyA(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}"); } STDAPI DllRegisterServer() { const char *root_key; const auto gpg4windir = W32::getGpg4winDir(); init_logging (); log_debug ("Debug"); if (gpg4windir.empty()) { STRANGEPOINT; return FALSE; } char module_path[MAX_PATH + 1]; if (!s_hinst || GetModuleFileNameA (s_hinst, module_path, MAX_PATH) == MAX_PATH) { STRANGEPOINT; return FALSE; } if (W32::isElevated()) { root_key = "HKEY_LOCAL_MACHINE"; } else { root_key = "HKEY_CURRENT_USER"; } log_debug("Registering shell extension."); /* Tar file is: {2B3256E4-49AA-11D3-8229-0050AE509054} */ /* Register File extension */ if (!W32::writeRegStr(root_key, "Software\\Classes\\.gpga", nullptr, MY_PROGID)) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\GPGArchive", nullptr, "GnuPG Archive Extension")) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\GPGArchive", "FriendlyTypeName", "GnuPG Archive")) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\GPGArchive\\ShellEx", nullptr, "")) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\GPGArchive\\CLSID", nullptr, "{" MY_CLSID_STR "}")) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\GPGArchive\\CLSID", nullptr, "{" MY_CLSID_STR "}")) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}", nullptr, "GnuPG Archive Extension")) { STRANGEPOINT; return FALSE; } std::string iconPath = gpg4windir + "\\share\\gpg4win\\file-ext.ico"; if (!W32::writeRegStr(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}", "Default Icon", iconPath.c_str())) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}\\InprocServer32", nullptr, module_path)) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}\\InprocServer32", "ThreadingModel", "Apartment")) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}\\ProgID", nullptr, MY_PROGID)) { STRANGEPOINT; return FALSE; } if (!W32::writeRegStr(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}\\Implemented Categories\\" "{00021490-0000-0000-C000-000000000046}", nullptr, "Browsable Shell Extension")) { STRANGEPOINT; return FALSE; } /* See: https://msdn.microsoft.com/en-us/library/windows/desktop/cc144093(v=vs.85).aspx */ if (!W32::writeRegDword(root_key, "Software\\Classes\\CLSID\\{" MY_CLSID_STR "}\\ShellFolder", "Attributes", SFGAO_FOLDER | SFGAO_CANRENAME | SFGAO_CANDELETE)) { STRANGEPOINT; return FALSE; } /* Notify the shell about the change. */ SHChangeNotify (SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); } STDAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) { s_hinst = hinst; // Do init of GPGME init_logging (); } else if (reason == DLL_PROCESS_DETACH) { log_debug ("Detached"); } return TRUE; } diff --git a/src/gpga/gpgshellfolder.cpp b/src/gpga/gpgshellfolder.cpp new file mode 100644 index 0000000..17caeb7 --- /dev/null +++ b/src/gpga/gpgshellfolder.cpp @@ -0,0 +1,146 @@ +/* Copyright (C) 2018 by Andre Heinecke + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ + +#include "gpgshellfolder.h" +#include "debug.h" + +GpgShellFolder::~GpgShellFolder() +{ +} + +STDMETHODIMP GpgShellFolder::QueryInterface (REFIID riid, void **ppv) +{ + if (!ppv) { + return E_POINTER; + } + + *ppv = nullptr; + + /* The static casts ensure that the virtual function table + layout of the returned object is correct. */ + if (riid == IID_IUnknown) { + *ppv = static_cast (this); + } else if (riid == IID_IShellFolder) { + *ppv = static_cast (this); + } else { + log_debug("QueryInterface with unknwon interface: '" GUID_FMT "'", + GUID_ARG(riid)); + return E_NOINTERFACE; + } + + reinterpret_cast(*ppv)->AddRef (); + + return S_OK; +} + +ULONG STDMETHODCALLTYPE GpgShellFolder::Release() +{ + ULONG lCount = --m_lRef; + if (!lCount) { + delete this; + } + return lCount; +} + +HRESULT STDMETHODCALLTYPE GpgShellFolder::ParseDisplayName(HWND hwnd, + IBindCtx *pbc, + LPWSTR pszDisplayName, + ULONG *pchEaten, + PIDLIST_RELATIVE *ppidl, + ULONG *pdwAttributes) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::EnumObjects(HWND hwnd, + SHCONTF grfFlags, + IEnumIDList **ppenumIDList) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::BindToObject(PCUIDLIST_RELATIVE pidl, + IBindCtx *pbc, + REFIID riid, + void **ppv) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, + IBindCtx *pbc, + REFIID riid, + void **ppv) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::CompareIDs(LPARAM lParam, + PCUIDLIST_RELATIVE pidl1, + PCUIDLIST_RELATIVE pidl2) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::CreateViewObject(HWND hwndOwner, + REFIID riid, + void **ppv) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::GetAttributesOf(UINT cidl, + PCUITEMID_CHILD_ARRAY apidl, + SFGAOF *rgfInOut) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::GetUIObjectOf(HWND hwndOwner, + UINT cidl, + PCUITEMID_CHILD_ARRAY apidl, + REFIID riid, + UINT *rgfReserved, + void **ppv) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, + SHGDNF uFlags, + STRRET *pName) +{ + TRACEPOINT; + return S_OK; +} + + +HRESULT STDMETHODCALLTYPE GpgShellFolder::SetNameOf(HWND hwnd, + PCUITEMID_CHILD pidl, + LPCWSTR pszName, + SHGDNF uFlags, + PITEMID_CHILD *ppidlOut) +{ + TRACEPOINT; + return S_OK; +} diff --git a/src/gpga/gpgshellfolder.h b/src/gpga/gpgshellfolder.h new file mode 100644 index 0000000..4e3ad22 --- /dev/null +++ b/src/gpga/gpgshellfolder.h @@ -0,0 +1,77 @@ +#ifndef GPGSHELLFOLDER_H +#define GPGSHELLFOLDER_H +/* Copyright (C) 2018 by Andre Heinecke + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ +#include +#include + +class GpgShellFolder: public IShellFolder +{ +public: + GpgShellFolder(): m_lRef(0) {} + + virtual ~GpgShellFolder(); + + /* IUnknown */ + STDMETHODIMP QueryInterface (REFIID riid, LPVOID* ppvObj); + STDMETHODIMP_(ULONG) AddRef() { ++m_lRef; return m_lRef; }; + STDMETHODIMP_(ULONG) Release(); + + /* IShellFolder*/ + HRESULT STDMETHODCALLTYPE ParseDisplayName(HWND hwnd, + IBindCtx *pbc, + LPWSTR pszDisplayName, + ULONG *pchEaten, + PIDLIST_RELATIVE *ppidl, + ULONG *pdwAttributes); + + HRESULT STDMETHODCALLTYPE EnumObjects(HWND hwnd, + SHCONTF grfFlags, + IEnumIDList **ppenumIDList); + + HRESULT STDMETHODCALLTYPE BindToObject(PCUIDLIST_RELATIVE pidl, + IBindCtx *pbc, + REFIID riid, + void **ppv); + + HRESULT STDMETHODCALLTYPE BindToStorage(PCUIDLIST_RELATIVE pidl, + IBindCtx *pbc, + REFIID riid, + void **ppv); + + HRESULT STDMETHODCALLTYPE CompareIDs(LPARAM lParam, + PCUIDLIST_RELATIVE pidl1, + PCUIDLIST_RELATIVE pidl2); + + HRESULT STDMETHODCALLTYPE CreateViewObject(HWND hwndOwner, + REFIID riid, + void **ppv); + + HRESULT STDMETHODCALLTYPE GetAttributesOf(UINT cidl, + PCUITEMID_CHILD_ARRAY apidl, + SFGAOF *rgfInOut); + + HRESULT STDMETHODCALLTYPE GetUIObjectOf(HWND hwndOwner, + UINT cidl, + PCUITEMID_CHILD_ARRAY apidl, + REFIID riid, + UINT *rgfReserved, + void **ppv); + + HRESULT STDMETHODCALLTYPE GetDisplayNameOf(PCUITEMID_CHILD pidl, + SHGDNF uFlags, + STRRET *pName); + + HRESULT STDMETHODCALLTYPE SetNameOf(HWND hwnd, + PCUITEMID_CHILD pidl, + LPCWSTR pszName, + SHGDNF uFlags, + PITEMID_CHILD *ppidlOut); +private: + ULONG m_lRef; +}; +#endif // GPGSHELLFOLDER_H diff --git a/src/util/debug.h b/src/util/debug.h index e68fca4..5d6257a 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -1,29 +1,31 @@ #ifndef GPG4WIN_DEBUG_H #define GPG4WIN_DEBUG_H /* Copyright (C) 2018 by Andre Heinecke * * This file is Free Software under the GNU GPL (v>=2) * and comes with ABSOLUTELY NO WARRANTY! * See LICENSE.txt for details. */ #include const char *log_srcname (const char *s); #define SRCNAME log_srcname (__FILE__) #define log_debug(format, ...) \ gpgrt_log_debug("%s:%s: " format, SRCNAME, __func__, ##__VA_ARGS__) #define STRANGEPOINT log_debug ("%s:%s:%d:UNEXPECTED", \ __LINE__); +#define TRACEPOINT log_debug ("%s:%s:%d:TRACE"); + #define GUID_FMT "{%08lX-%04hX-%04hX-%02hX%02hX-%02hX%02hX%02hX%02hX%02hX%02hX}" #define GUID_ARG(x) (x).Data1, (x).Data2, (x).Data3, (x).Data4[0], \ (x).Data4[1], (x).Data4[2], (x).Data4[3], (x).Data4[4], \ (x).Data4[5], (x).Data4[6], (x).Data4[7] #endif // GPG4WIN_DEBUG_H