diff --git a/po/pt.po b/po/pt.po index 9486899..697ecc4 100644 --- a/po/pt.po +++ b/po/pt.po @@ -1,131 +1,130 @@ # Translation of GPGex. # Copyright (C) 2017 g10 Code GmbH # This file is distributed under the same license as the FIXME:GPGex package. # Diego Escalante Urrelo , 2008. # # msgid "" msgstr "" "Project-Id-Version: GpgEX\n" "Report-Msgid-Bugs-To: http://bugs.gnupg.org\n" "PO-Revision-Date: 2017-10-16 13:38+0100\n" "Last-Translator: Marco A.G.Pinto \n" "Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-SourceCharset: UTF-8\n" "X-Generator: Poedit 2.0.4\n" -"POT-Creation-Date: \n" #, c-format msgid "" "Can not connect to the GnuPG user interface%s%s%s:\r\n" "%s" msgstr "" "Não é possível aceder à interface de utilizador do GnuPG%s%s%s:\r\n" "%s" #, c-format msgid "" "Error returned by the GnuPG user interface%s%s%s:\r\n" "%s" msgstr "" "Erro retornado pela interface de utilizador do GnuPG%s%s%s:\r\n" "%s" msgid "Help on GpgEX" msgstr "Ajuda do GpgEX" msgid "About GpgEX" msgstr "Sobre o GpgEX" msgid "Decrypt and verify" msgstr "Desencriptar e verificar" msgid "Decrypt" msgstr "Desencriptar" msgid "Verify" msgstr "Verificar" msgid "Sign and encrypt" msgstr "Assinar e encriptar" msgid "Encrypt" msgstr "Encriptar" msgid "Sign" msgstr "Assinar" msgid "Import keys" msgstr "Importar chaves" msgid "Create checksums" msgstr "Criar checksums" msgid "Verify checksums" msgstr "Verificar checksums" msgid "More GpgEX options" msgstr "Mais opções do GpgEX" msgid "Invoke the GpgEX documentation." msgstr "Invocar a documentação do GpgEX." msgid "Show the version of GpgEX." msgstr "Mostrar a versão do GpgEX." msgid "Decrypt and verify the marked files." msgstr "Desencriptar e verificar os ficheiros marcados." msgid "Decrypt the marked files." msgstr "Desencriptar os ficheiros marcados." msgid "Verify the marked files." msgstr "Verificar os ficheiros marcados." msgid "Sign and encrypt the marked files." msgstr "Assinar e encriptar os ficheiros marcados." msgid "Encrypt the marked files." msgstr "Encriptar os ficheiros marcados." msgid "Sign the marked files." msgstr "Assinar os ficheiros marcados." msgid "Import the marked files." msgstr "Importar os ficheiros marcados." msgid "Create checksums." msgstr "Criar checksums." msgid "Verify checksums." msgstr "Verificar checksums." msgid "Show more GpgEX options." msgstr "Mostrar mais opções do GpgEX." #. TRANSLATORS: See the source for the full english text. msgid "-#GpgEXFullHelpText#-" msgstr "" "O GpgEX é um plug-in Explorer para encriptar e assinar dados.\n" "Ele usa o software GnuPG (http://www.gnupg.org).\n" "\n" "O GpgEX é software livre; podes redistribuí-lo e/ou\n" "modificá-lo nos termos da GNU Lesser General Public\n" "License, como está publicado pela Free Software Foundation; quer\n" "a versão 2.1 da Licença, ou (à tua escolha) qualquer versão posterior.\n" "\n" "O GpgEX é distribuído na esperança que possa ser útil,\n" "mas SEM QUALQUER GARANTIA; nem mesmo a garantia implícita de\n" "MERCANTIBILIDADE ou APTIDÃO PARA UMA FINALIDADE ESPECÍFICA. Vê a\n" "GNU Lesser General Public License para mais detalhes.\n" "\n" "Deves ter recebido uma cópia da GNU Lesser General Public License\n" "juntamente com este programa; se não, vê ." #, c-format msgid "This is GpgEX version %s (%s)" msgstr "Este é o GpgEX versão %s (%s)" diff --git a/src/Makefile.am b/src/Makefile.am index d7dfe7a..54329a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,57 +1,57 @@ # Makefile.am - main makefile for dialogs part of GpgEX # Copyright (C) 2005, 2007, 2013 g10 Code GmbH # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ## Process this file with automake to produce Makefile.in bin_PROGRAMS = gpgex EXTRA_DIST = versioninfo.rc.in gpgex.manifest.in \ - GNU.GnuPG.Gcc64Support.manifest gnupg.ico + GNU.GnuPG.Gcc64Support.manifest gnupg.ico \ + gpgex_logo.svg standalone.svg EXEEXT = .dll AM_CFLAGS = $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) -shared AM_CXXFLAGS = $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) -shared -ICONS = icon-key-12.bmp icon-key-16.bmp \ - icon-lock-12.bmp icon-lock-16.bmp +ICONS = icon-16.png nodist_gpgex_SOURCES = versioninfo.rc gpgex.manifest gpgex_SOURCES = \ gpgex.def \ registry.h registry.c \ exechelp.h exechelp.c \ gpgex-class.h gpgex-class.cc \ gpgex-factory.h gpgex-factory.cc \ - bitmaps.h bitmaps.cc \ gpgex.h gpgex.cc \ client.h client.cc \ main.h debug.h main.cc \ + resource.h \ $(ICONS) libgpg-error.a: ln -sf $$($(GPG_ERROR_CONFIG) --prefix)/lib/libgpg-error.a . libassuan.a: ln -sf $$($(LIBASSUAN_CONFIG) --prefix)/lib/libassuan.a . clean-local: rm -f libgpg-error.a libassuan.a #gpgex_LDADD = $(srcdir)/gpgex.def \ # -L . -lshell32 -lcomdlg32 -ladvapi32 gpgex_LDFLAGS = -static-libgcc -static-libstdc++ # We need -loleaut32 for start_help() in gpgex.cc. gpgex_LDADD = $(srcdir)/gpgex.def -L . \ - -lshell32 -lgdi32 -lole32 -luuid \ + -lshell32 -lgdi32 -lole32 -luuid -lgdiplus \ ./libassuan.a ./libgpg-error.a -lws2_32 -loleaut32 .rc.o: $(WINDRES) -I $(srcdir) -I . `test -f '$<' || echo '$(srcdir)/'`$< $@ diff --git a/src/bitmaps.cc b/src/bitmaps.cc deleted file mode 100644 index 4928412..0000000 --- a/src/bitmaps.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* bitmaps.cc - gpgex bitmap implementation - Copyright (C) 2007 g10 Code GmbH - - This file is part of GpgEX. - - GpgEX is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - GpgEX is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ - -#if HAVE_CONFIG_H -#include -#endif - -#include -#include - -using std::string; - -#include - -#include "main.h" - -#include "bitmaps.h" - - -/* The size of the icons. */ -int gpgex_bitmaps_t::size; - -/* The available bitmap sizes in ascending order. */ -int gpgex_bitmaps_t::available_sizes[] = { 12, 16 }; - - -/* The global singleton object. */ -class gpgex_bitmaps_t gpgex_bitmaps; - - -gpgex_bitmaps_t::gpgex_bitmaps_t (void) -{ - /* Note about bitmaps: The required size is given by - GetSystemMetrics and can vary depending on the display size. A - typical value is 12x12. The color depth should be 8 bits. The - upper left corner pixel color is replaced by transparent - automatically. */ - int width = GetSystemMetrics (SM_CXMENUCHECK); - int height = GetSystemMetrics (SM_CYMENUCHECK); - - /* All our images are square, so take the minimum and look for the - biggest available size that fits in there. */ - int max_size = (width < height) ? width : height; - - for (unsigned int i = 0; i < (sizeof (this->available_sizes) - / sizeof (this->available_sizes[0])); i++) - if (max_size >= this->available_sizes[i]) - this->size = this->available_sizes[i]; - else - break; - - (void) TRACE3 (DEBUG_INIT, "gpgex_bitmaps_t::gpgex_bitmaps_t", this, - "GetSystemMetrics: %ix%i (using %i)", width, height, - this->size); -} - - -/* Load the bitmap with name NAME. */ -HBITMAP gpgex_bitmaps_t::load_bitmap (string name) -{ - HBITMAP bmap; - std::ostringstream out; - - out << name << "_" << this->size; - bmap = LoadBitmap (gpgex_server::instance, out.str().c_str()); - if (bmap == NULL) - (void) TRACE2 (DEBUG_INIT, "gpgex_bitmaps_t::load_bitmap", this, - "LoadImage %s failed: ec=%x", - out.str().c_str(), GetLastError ()); - else - (void) TRACE1 (DEBUG_INIT, "gpgex_bitmaps_t::load_bitmap", this, - "loaded image %s", out.str().c_str()); - - /* FIXME: Create cache of images. */ - return bmap; -} - diff --git a/src/gpgex.cc b/src/gpgex.cc index 84e9901..bdac2d0 100644 --- a/src/gpgex.cc +++ b/src/gpgex.cc @@ -1,731 +1,853 @@ /* gpgex.cc - gpgex implementation Copyright (C) 2007, 2013 g10 Code GmbH This file is part of GpgEX. GpgEX is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GpgEX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H #include #endif #include #include #include +#include using std::vector; using std::string; #include +#include +#include +#include /* For the start_help() function. */ #include #include "main.h" #include "client.h" #include "registry.h" #include "gpgex.h" +#include "resource.h" + /* For context menus. */ #define ID_CMD_HELP 0 #define ID_CMD_DECRYPT_VERIFY 1 #define ID_CMD_DECRYPT 2 #define ID_CMD_VERIFY 3 #define ID_CMD_SIGN_ENCRYPT 4 #define ID_CMD_ENCRYPT 5 #define ID_CMD_SIGN 6 #define ID_CMD_IMPORT 7 #define ID_CMD_CREATE_CHECKSUMS 8 #define ID_CMD_VERIFY_CHECKSUMS 9 #define ID_CMD_POPUP 10 #define ID_CMD_ABOUT 11 #define ID_CMD_MAX 11 #define ID_CMD_STR_HELP _("Help on GpgEX") #define ID_CMD_STR_ABOUT _("About GpgEX") #define ID_CMD_STR_DECRYPT_VERIFY _("Decrypt and verify") #define ID_CMD_STR_DECRYPT _("Decrypt") #define ID_CMD_STR_VERIFY _("Verify") #define ID_CMD_STR_SIGN_ENCRYPT _("Sign and encrypt") #define ID_CMD_STR_ENCRYPT _("Encrypt") #define ID_CMD_STR_SIGN _("Sign") #define ID_CMD_STR_IMPORT _("Import keys") #define ID_CMD_STR_CREATE_CHECKSUMS _("Create checksums") #define ID_CMD_STR_VERIFY_CHECKSUMS _("Verify checksums") /* Reset the instance between operations. */ void gpgex_t::reset (void) { this->filenames.clear (); this->all_files_gpg = TRUE; } STDMETHODIMP gpgex_t::QueryInterface (REFIID riid, void **ppv) { #define _TRACE_BEG12(a,b,c,d,e,f) TRACE_BEG12(a,b,c,d,e,f) _TRACE_BEG12 (DEBUG_INIT, "gpgex_t::QueryInterface", this, "riid=" GUID_FMT ", ppv=%p", GUID_ARG (riid), ppv); if (ppv == NULL) return TRACE_RES (E_INVALIDARG); /* Be nice to broken software. */ *ppv = NULL; /* The static casts ensure that the virtual function table layout of the returned object is correct. We can not cast to IUnknown because that base class is ambiguous (because it is not virtual), so we pick one of the derived classes instead. */ if (riid == IID_IUnknown) *ppv = static_cast (this); else if (riid == IID_IShellExtInit) *ppv = static_cast (this); else if (riid == IID_IContextMenu) *ppv = static_cast (this); #if 0 /* FIXME: Enable this when the functions are actually implemented. */ else if (riid == IID_IContextMenu2) *ppv = static_cast (this); else if (riid == IID_IContextMenu3) *ppv = static_cast (this); #endif else return TRACE_RES (E_NOINTERFACE); /* We have to acquire a reference to the returned object. We lost the type information, but we know that all object classes inherit from IUnknown, which is good enough. */ reinterpret_cast(*ppv)->AddRef (); return TRACE_RES (S_OK); } STDMETHODIMP_(ULONG) gpgex_t::AddRef (void) { (void) TRACE1 (DEBUG_INIT, "gpgex_t::AddRef", this, "new_refcount=%i", this->refcount + 1); return InterlockedIncrement (&this->refcount); } STDMETHODIMP_(ULONG) gpgex_t::Release (void) { LONG count; (void) TRACE1 (DEBUG_INIT, "gpgex_t::Release", this, "new_refcount=%i", this->refcount - 1); count = InterlockedDecrement (&this->refcount); if (count == 0) delete this; return count; } /* IShellExtInit methods. */ STDMETHODIMP gpgex_t::Initialize (LPCITEMIDLIST pIDFolder, IDataObject *pDataObj, HKEY hRegKey) { HRESULT err = S_OK; TRACE_BEG3 (DEBUG_INIT, "gpgex_t::Initialize", this, "pIDFolder=%p, pDataObj=%p, hRegKey=%p", pIDFolder, pDataObj, hRegKey); /* This function is called for the Shortcut (context menu), Drag-and-Drop, and Property Sheet extensions. */ this->reset (); if (pDataObj) { /* The data object contains a drop item which we extract. */ FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium; UINT count; if (SUCCEEDED (pDataObj->GetData (&fe, &medium))) { HDROP drop = (HDROP) GlobalLock (medium.hGlobal); unsigned int i; /* Now that we have the drop item, we can extract the file names. */ count = DragQueryFile (drop, (UINT) -1, NULL, 0); if (count == 0) { err = E_INVALIDARG; } if (!err) { for (i = 0; i < count; i++) { char filename[MAX_PATH]; UINT len; len = DragQueryFile (drop, i, filename, sizeof (filename) - 1); if (len == 0) { err = E_INVALIDARG; break; } /* Take a look at the ending. */ char *ending = strrchr (filename, '.'); if (ending) { BOOL gpg = false; ending++; if (! strcasecmp (ending, "gpg") || ! strcasecmp (ending, "pgp") || ! strcasecmp (ending, "asc") || ! strcasecmp (ending, "sig") || ! strcasecmp (ending, "pem") || ! strcasecmp (ending, "p7m") || ! strcasecmp (ending, "p7s") ) gpg = true; if (gpg == false) this->all_files_gpg = FALSE; } else this->all_files_gpg = FALSE; this->filenames.push_back (filename); } GlobalUnlock (medium.hGlobal); ReleaseStgMedium (&medium); } } } if (err != S_OK) this->reset (); return TRACE_RES (err); } +static HBITMAP +getBitmap (int id) +{ + TRACE_BEG0 (DEBUG_CONTEXT_MENU, __func__, nullptr, "get bitmap"); + PICTDESC pdesc; + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + Gdiplus::Bitmap* pbitmap; + ULONG_PTR gdiplusToken; + HRSRC hResource; + DWORD imageSize; + const void* pResourceData; + HGLOBAL hBuffer; + + memset (&pdesc, 0, sizeof pdesc); + pdesc.cbSizeofstruct = sizeof pdesc; + pdesc.picType = PICTYPE_BITMAP; + + /* Initialize GDI */ + gdiplusStartupInput.DebugEventCallback = NULL; + gdiplusStartupInput.SuppressBackgroundThread = FALSE; + gdiplusStartupInput.SuppressExternalCodecs = FALSE; + gdiplusStartupInput.GdiplusVersion = 1; + GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL); + + /* Get the image from the resource file */ + hResource = FindResource (gpgex_server::instance, MAKEINTRESOURCE(id), RT_RCDATA); + if (!hResource) + { + TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "Failed to find id: %i", + id); + return nullptr; + } + + imageSize = SizeofResource (gpgex_server::instance, hResource); + if (!imageSize) + { + TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i", + __LINE__); + return nullptr; + } + + pResourceData = LockResource (LoadResource (gpgex_server::instance, hResource)); + + if (!pResourceData) + { + TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i", + __LINE__); + return nullptr; + } + + hBuffer = GlobalAlloc (GMEM_MOVEABLE, imageSize); + + if (hBuffer) + { + void* pBuffer = GlobalLock (hBuffer); + if (pBuffer) + { + IStream* pStream = NULL; + CopyMemory (pBuffer, pResourceData, imageSize); + + if (CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK) + { + pbitmap = Gdiplus::Bitmap::FromStream (pStream); + pStream->Release(); + if (!pbitmap || pbitmap->GetHBITMAP (0, &pdesc.bmp.hbitmap)) + { + TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i", + __LINE__); + return nullptr; + } + } + } + GlobalUnlock (pBuffer); + } + GlobalFree (hBuffer); + + Gdiplus::GdiplusShutdown (gdiplusToken); + + return pdesc.bmp.hbitmap; +} + +static HBITMAP +getBitmapCached (int id) +{ + static std::map s_id_map; + + const auto it = s_id_map.find (id); + if (it == s_id_map.end ()) + { + const HBITMAP icon = getBitmap (id); + s_id_map.insert (std::make_pair (id, icon)); + return icon; + } + return it->second; +} + +static bool +setupContextMenuIcon (int id, HMENU hMenu, UINT indexMenu) +{ + TRACE_BEG2 (DEBUG_CONTEXT_MENU, __func__, nullptr, "Start. menu: %p index %u", + hMenu, indexMenu); + int width = GetSystemMetrics (SM_CXMENUCHECK); + int height = GetSystemMetrics (SM_CYMENUCHECK); + + TRACE2 (DEBUG_CONTEXT_MENU, __func__, nullptr, "width %i height %i", + width, height); + + HBITMAP bmp = getBitmapCached (id); + + if (!bmp) + { + TRACE1 (DEBUG_CONTEXT_MENU, __func__, nullptr, "WTF: %i", + __LINE__); + return false; + } + + return SetMenuItemBitmaps (hMenu, indexMenu - 1, MF_BYPOSITION, + bmp, bmp); +} + /* IContextMenu methods. */ /* The argument HMENU contains the context menu, and INDEXMENU points to the first index where we can add items. IDCMDFIRST and IDCMDLAST is the range of command ID values which we can use. */ STDMETHODIMP gpgex_t::QueryContextMenu (HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { BOOL res; TRACE_BEG5 (DEBUG_CONTEXT_MENU, "gpgex_t::QueryContextMenu", this, "hMenu=%p, indexMenu=%u, idCmdFirst=%u, idCmdLast=%u, uFlags=%x", hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); /* FIXME: Do something if idCmdLast - idCmdFirst + 1 is not big enough. */ /* If the flags include CMF_DEFAULTONLY then nothing should be done. */ if (uFlags & CMF_DEFAULTONLY) return TRACE_RES (MAKE_HRESULT (SEVERITY_SUCCESS, FACILITY_NULL, 0)); res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); if (! res) return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ())); /* First we add the file-specific menus. */ if (this->all_files_gpg) { res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_DECRYPT_VERIFY, ID_CMD_STR_DECRYPT_VERIFY); if (! res) return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ())); } else { /* FIXME: Check error. */ res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_SIGN_ENCRYPT, ID_CMD_STR_SIGN_ENCRYPT); if (! res) return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ())); } /* Now generate and add the generic command popup menu. */ HMENU popup; UINT idx = 0; /* FIXME: Check error. */ popup = CreatePopupMenu (); if (popup == NULL) return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ())); MENUITEMINFO mii = { sizeof (MENUITEMINFO) }; mii.fMask = MIIM_SUBMENU | MIIM_STRING | MIIM_ID; mii.wID = idCmdFirst + ID_CMD_POPUP; mii.hSubMenu = popup; mii.dwTypeData = (CHAR *) _("More GpgEX options"); res = InsertMenuItem (hMenu, indexMenu++, TRUE, &mii); if (!res) { DWORD last_error = GetLastError (); DestroyMenu (popup); return TRACE_RES (HRESULT_FROM_WIN32 (last_error)); } - if (this->key_bitmap) - { - // indexMenu - 1!!! - res = SetMenuItemBitmaps (hMenu, indexMenu - 1, MF_BYPOSITION, - this->key_bitmap, this->key_bitmap); - } + res = setupContextMenuIcon (IDI_ICON_16, hMenu, indexMenu); + if (res) res = InsertMenu (hMenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); if (! res) return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ())); res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_DECRYPT, ID_CMD_STR_DECRYPT); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_VERIFY, ID_CMD_STR_VERIFY); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_DECRYPT_VERIFY, ID_CMD_STR_DECRYPT_VERIFY); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_ENCRYPT, ID_CMD_STR_ENCRYPT); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_SIGN, ID_CMD_STR_SIGN); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_SIGN_ENCRYPT, ID_CMD_STR_SIGN_ENCRYPT); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_IMPORT, ID_CMD_STR_IMPORT); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_CREATE_CHECKSUMS, ID_CMD_STR_CREATE_CHECKSUMS); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_VERIFY_CHECKSUMS, ID_CMD_STR_VERIFY_CHECKSUMS); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_HELP, ID_CMD_STR_HELP); if (res) res = InsertMenu (popup, idx++, MF_BYPOSITION | MF_STRING, idCmdFirst + ID_CMD_ABOUT, ID_CMD_STR_ABOUT); if (! res) return TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ())); /* We should return a HRESULT that indicates success and the offset to the next free command ID after the last one we used, relative to idCmdFirst. In other words: max_used - idCmdFirst + 1. */ return TRACE_RES (MAKE_HRESULT (SEVERITY_SUCCESS, FACILITY_NULL, ID_CMD_MAX + 1)); } /* Get a verb or help text for the command IDCOMMAND (which is the offset to IDCMDFIRST of QueryContextMenu, ie zero based). UFLAGS has GCS_HELPTEXT set if the help-text is requested (otherwise a verb is requested). If UFLAGS has the GCS_UNICODE bit set, we need to return a wide character string. */ STDMETHODIMP gpgex_t::GetCommandString (UINT_PTR idCommand, UINT uFlags, LPUINT lpReserved, LPSTR pszName, UINT uMaxNameLen) { const char *txt; TRACE_BEG5 (DEBUG_CONTEXT_MENU, "gpgex_t::GetCommandString", this, "idCommand=%u, uFlags=%x, lpReserved=%lu, pszName=%p, " "uMaxNameLen=%u", (unsigned int)(idCommand & 0xffffffff), uFlags, lpReserved, pszName, uMaxNameLen); if (! (uFlags & GCS_HELPTEXT)) return TRACE_RES (E_INVALIDARG); if (idCommand > ID_CMD_MAX) return TRACE_RES (E_INVALIDARG); switch (idCommand) { case ID_CMD_HELP: txt = _("Invoke the GpgEX documentation."); break; case ID_CMD_ABOUT: txt = _("Show the version of GpgEX."); break; case ID_CMD_DECRYPT_VERIFY: txt = _("Decrypt and verify the marked files."); break; case ID_CMD_DECRYPT: txt = _("Decrypt the marked files."); break; case ID_CMD_VERIFY: txt = _("Verify the marked files."); break; case ID_CMD_SIGN_ENCRYPT: txt = _("Sign and encrypt the marked files."); break; case ID_CMD_ENCRYPT: txt = _("Encrypt the marked files."); break; case ID_CMD_SIGN: txt = _("Sign the marked files."); break; case ID_CMD_IMPORT: txt = _("Import the marked files."); break; case ID_CMD_CREATE_CHECKSUMS: txt = _("Create checksums."); break; case ID_CMD_VERIFY_CHECKSUMS: txt = _("Verify checksums."); break; case ID_CMD_POPUP: txt = _("Show more GpgEX options."); break; default: return TRACE_RES (E_INVALIDARG); } if (uFlags & GCS_UNICODE) { /* FIXME: Convert to unicode. */ lstrcpynW ((LPWSTR) pszName, L"(Unicode help not available yet)", uMaxNameLen); } else lstrcpynA (pszName, txt, uMaxNameLen); return TRACE_RES (S_OK); } /* Return the lang name. This is either "xx" or "xx_YY". On error "en" is returned. */ static const char * get_lang_name (void) { static char *name; const char *s; char *p; int count = 0; if (!name) { s = gettext_localename (); if (!s) s = "en"; else if (!strcmp (s, "C") || !strcmp (s, "POSIX")) s = "en"; name = strdup (s); if (!name) return "en"; for (p = name; *p; p++) { if (*p == '.' || *p == '@' || *p == '/' /*(safeguard)*/) *p = 0; else if (*p == '_') { if (count++) *p = 0; /* Also cut at a underscore in the territory. */ } } } return name; } /* According to MSDN using ShellExecute may be problematic when using within the Shell. Thus we call Internet explorer directly. It is anyway only used for local files. */ static void start_help (HWND hwnd) { HRESULT res; CLSID clsid; LPUNKNOWN browser = NULL; IWebBrowser2 *web = NULL; CLSIDFromProgID (OLESTR ("InternetExplorer.Application"), &clsid); res = CoCreateInstance (clsid, NULL, CLSCTX_SERVER, IID_IUnknown, (void **) &browser); if (! SUCCEEDED (res)) { MessageBox (hwnd, "Can not open browser", "GpgEX", MB_ICONINFORMATION); return; } browser->QueryInterface (IID_IWebBrowser2, (void **) &web); browser->Release (); /* FIXME: Pick a good configuration. */ // Only for IE7? // web->put_Resizable (VARIANT_TRUE); // web->put_ToolBar (FALSE); // web->put_AddressBar (VARIANT_FALSE); // web->put_MenuBar (VARIANT_FALSE); // web->put_StatusBar (VARIANT_FALSE); // width, height web->put_Visible (VARIANT_TRUE); wchar_t *wurl; { #define URLSIZE 512 char url[URLSIZE]; const char *lang = get_lang_name (); snprintf (url, URLSIZE, "file:///%s\\share\\doc\\gpgex\\gpgex-%s.html", gpgex_server::root_dir, lang); url[URLSIZE - 1] = '\0'; wurl = utf8_to_wchar (url); /* We need to test whether we need to fall back to the generic lang id. */ if (wurl && strchr (lang, '_') && _waccess (wurl+8, 0)) { snprintf (url, URLSIZE, "file:///%s\\share\\doc\\gpgex\\gpgex-%.2s.html", gpgex_server::root_dir, lang); url[URLSIZE - 1] = '\0'; free (wurl); wurl = utf8_to_wchar (url); } /* If the help file does not exists fall back to the english version. */ if (wurl && _waccess (wurl+8, 0)) { snprintf (url, URLSIZE, "file:///%s\\share\\doc\\gpgex\\gpgex-en.html", gpgex_server::root_dir); url[URLSIZE - 1] = '\0'; free (wurl); wurl = utf8_to_wchar (url); } } if (wurl) { BSTR burl = SysAllocString ((const OLECHAR *)wurl); VARIANT vars[4]; memset (vars, 0, sizeof (vars)); res = web->Navigate (burl, vars, vars + 1, vars + 2, vars + 3); SysFreeString (burl); free (wurl); if (!SUCCEEDED (res)) { web->Release (); return; } } /* Do more stuff. */ web->Release (); } /* Show the version informatione etc. */ static void show_about (HWND hwnd) { const char cpynotice[] = "Copyright (C) 2013 g10 Code GmbH"; const char en_notice[] = "GpgEX is an Explorer plugin for data encryption and signing\n" "It uses the GnuPG software (http://www.gnupg.org).\n" "\n" "GpgEX is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU Lesser General Public\n" "License as published by the Free Software Foundation; either\n" "version 2.1 of the License, or (at your option) any later version.\n" "\n" "GpgEX is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU Lesser General Public License for more details.\n" "\n" "You should have received a copy of the GNU Lesser General Public " "License\n" "along with this program; if not, see ."; /* TRANSLATORS: See the source for the full english text. */ const char notice_key[] = N_("-#GpgEXFullHelpText#-"); const char *notice; char header[300]; char *buffer; size_t nbuffer; snprintf (header, sizeof header, _("This is GpgEX version %s (%s)"), PACKAGE_VERSION, #ifdef HAVE_W64_SYSTEM "64 bit" #else "32 bit" #endif ); notice = _(notice_key); if (!strcmp (notice, notice_key)) notice = en_notice; nbuffer = strlen (header) + strlen (cpynotice) + strlen (notice) + 20; buffer = (char*)malloc (nbuffer); if (buffer) { snprintf (buffer, nbuffer, "%s\n%s\n\n%s\n", header, cpynotice, notice); MessageBox (hwnd, buffer, "GpgEx", MB_OK); free (buffer); } else MessageBox (hwnd, header, "GpgEx", MB_OK); } STDMETHODIMP gpgex_t::InvokeCommand (LPCMINVOKECOMMANDINFO lpcmi) { TRACE_BEG1 (DEBUG_CONTEXT_MENU, "gpgex_t::InvokeCommand", this, "lpcmi=%p", lpcmi); /* If lpVerb really points to a string, ignore this function call and bail out. */ if (HIWORD (lpcmi->lpVerb) != 0) return TRACE_RES (E_INVALIDARG); client_t client (lpcmi->hwnd); /* Get the command index, which is the offset to IDCMDFIRST of QueryContextMenu, ie zero based). */ switch (LOWORD (lpcmi->lpVerb)) { case ID_CMD_HELP: start_help (lpcmi->hwnd); break; case ID_CMD_ABOUT: show_about (lpcmi->hwnd); break; case ID_CMD_DECRYPT_VERIFY: client.decrypt_verify (this->filenames); break; case ID_CMD_DECRYPT: client.decrypt (this->filenames); break; case ID_CMD_VERIFY: client.verify (this->filenames); break; case ID_CMD_SIGN_ENCRYPT: client.sign_encrypt (this->filenames); break; case ID_CMD_ENCRYPT: client.encrypt (this->filenames); break; case ID_CMD_SIGN: client.sign (this->filenames); break; case ID_CMD_IMPORT: client.import (this->filenames); break; case ID_CMD_CREATE_CHECKSUMS: client.create_checksums (this->filenames); break; case ID_CMD_VERIFY_CHECKSUMS: client.verify_checksums (this->filenames); break; default: return TRACE_RES (E_INVALIDARG); break; } return TRACE_RES (S_OK); } /* IContextMenu2 methods. */ STDMETHODIMP gpgex_t::HandleMenuMsg (UINT uMsg, WPARAM wParam, LPARAM lParam) { /* FIXME */ return S_OK; } /* IContextMenu3 methods. */ STDMETHODIMP gpgex_t::HandleMenuMsg2 (UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) { /* FIXME */ return S_OK; } diff --git a/src/gpgex.h b/src/gpgex.h index 6402dce..9f5e5e0 100644 --- a/src/gpgex.h +++ b/src/gpgex.h @@ -1,150 +1,140 @@ /* gpgex.h - gpgex prototypes Copyright (C) 2007 g10 Code GmbH This file is part of GpgEX. GpgEX is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GpgEX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GPGEX_H #define GPGEX_H #include #include using std::vector; using std::string; #include #include -#include "bitmaps.h" - /* Our shell extension interface. We use multiple inheritance to achieve polymorphy. NOTE 1: By this we save some effort, but we can only provide one implementation for each virtual function signature. The overlap in the IUnknown interface does not matter, in fact it is a plus that we only have to implement it once. For other functions, it might be more of a problem. If this needs to be avoided, one can derive intermediate classes which inherit only one of the overlapping classes and contain a implementations for the overlapping method that call a purely virtual function of different, unambiguous names. For example, if there is bar::foo and baz::foo, classes mybar : public bar and mybaz : public baz can be defined with mybar::foo calling mybar::bar_foo and mybaz::foo calling mybaz::baz_foo. Then the final class can inherit mybar and mybaz and implement the virtual functions bar_foo and baz_foo, leading to the desired result. NOTE 2: It is not obvious why this approach works at all! Ignorance is bliss, I guess, because the multiple-inheritance approach is documented in many places, but rarely it is explained why it works. The naive explanation is that there is a virtual function table for each base class, and we can just use the address of the pointer to that table as our COM object pointer. However, what is missing from this description is that now the THIS pointer is incorrect, and needs to be adjusted by subtracting the offset of the base class inside the object when a function implementation is invoked (which exists in the derived class and overrides the abstract base class). Recent compilers seem to implement this by replacing the function pointer in the VTBL with an "adjustor thunk" which subtracts this offset and jumps to the actual function implementation (see the C++ ABI for GCC http://www.codesourcery.com/cxx-abi/abi.html and http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnarvc/html/jangrayhood.asp for MSVC++). But this is not the only possible implementation: Other compilers use a displacement field in each VTBL entry. The displacement field is often zero, and in this case an adjustor thunk is a good optimization. However, if a displacement field is used by the compiler, it changes the VTBL layout in memory and makes it non-compliant with COM! So let's all be happy that modern compilers agree on using adjustor thunks and cross our fingers, as there is no possible way we can even sanely check if the compiler complies. And you thought C++ was easy? */ class gpgex_t : public IShellExtInit, public IContextMenu3 { private: /* Per-object reference count. */ LONG refcount; /* Support for IShellExtInit. */ vector filenames; /* TRUE if all files in filenames are directly related to GPG. */ BOOL all_files_gpg; - /* Support for the context menu. */ - HBITMAP key_bitmap; - public: /* Constructors and destructors. For these, we update the global component reference counter. */ gpgex_t (void) : refcount (0) { TRACE_BEG (DEBUG_INIT, "gpgex_t::gpgex_t", this); gpgex_server::add_ref (); - this->key_bitmap = gpgex_bitmaps.load_bitmap ("Lock"); - (void) TRACE_SUC (); } ~gpgex_t (void) { TRACE_BEG (DEBUG_INIT, "gpgex_t::~gpgex_t", this); - if (this->key_bitmap != NULL) - DeleteObject (this->key_bitmap); - gpgex_server::release (); (void) TRACE_SUC (); } /* Reset the instance between operations. */ void reset (void); public: /* IUnknown methods. */ STDMETHODIMP QueryInterface (REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef (void); STDMETHODIMP_(ULONG) Release (void); /* IShellExtInit methods. */ STDMETHODIMP Initialize (LPCITEMIDLIST pIDFolder, IDataObject *pDataObj, HKEY hRegKey); /* IContextMenu methods. */ STDMETHODIMP QueryContextMenu (HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); STDMETHODIMP GetCommandString (UINT_PTR idCommand, UINT uFlags, LPUINT lpReserved, LPSTR pszName, UINT uMaxNameLen); STDMETHODIMP InvokeCommand (LPCMINVOKECOMMANDINFO lpcmi); /* IContextMenu2 methods. */ STDMETHODIMP HandleMenuMsg (UINT uMsg, WPARAM wParam, LPARAM lParam); /* IContextMenu3 methods. */ STDMETHODIMP HandleMenuMsg2 (UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult); }; #endif /* ! GPGEX_H */ diff --git a/src/gpgex_logo.svg b/src/gpgex_logo.svg new file mode 100644 index 0000000..149047f --- /dev/null +++ b/src/gpgex_logo.svg @@ -0,0 +1,191 @@ + + + + + + image/svg+xml + + GpgEX finnished Logo + + + + + + + + + + GpgEX finnished Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/icon-16.png b/src/icon-16.png new file mode 100644 index 0000000..4c59692 Binary files /dev/null and b/src/icon-16.png differ diff --git a/src/icon-key-12.bmp b/src/icon-key-12.bmp deleted file mode 100644 index d61baac..0000000 Binary files a/src/icon-key-12.bmp and /dev/null differ diff --git a/src/icon-key-16.bmp b/src/icon-key-16.bmp deleted file mode 100644 index f2888b7..0000000 Binary files a/src/icon-key-16.bmp and /dev/null differ diff --git a/src/icon-lock-12.bmp b/src/icon-lock-12.bmp deleted file mode 100644 index dec900d..0000000 Binary files a/src/icon-lock-12.bmp and /dev/null differ diff --git a/src/icon-lock-16.bmp b/src/icon-lock-16.bmp deleted file mode 100644 index ac458a3..0000000 Binary files a/src/icon-lock-16.bmp and /dev/null differ diff --git a/src/bitmaps.h b/src/resource.h similarity index 55% rename from src/bitmaps.h rename to src/resource.h index 40115a9..d9e36a6 100644 --- a/src/bitmaps.h +++ b/src/resource.h @@ -1,52 +1,26 @@ -/* bitmaps.h - gpgex bitmap prototypes - Copyright (C) 2007 g10 Code GmbH +/* resource.h - resource ids + Copyright (C) 2018 Intevation GmbH This file is part of GpgEX. GpgEX is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GpgEX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef GPGEX_BITMAPS_H -#define GPGEX_BITMAPS_H +#ifndef RESOURCE_H +#define RESOURCE_H -#include +#define IDI_ICON_16 0x1000 -using std::string; - -#include - - -/* The class used to load bitmap resources. */ -class gpgex_bitmaps_t -{ - /* The icon size used. */ - static int size; - - /* The available sizes. */ - static int available_sizes[]; - - public: - /* Constructor. */ - gpgex_bitmaps_t (void); - - /* Load the bitmap with name NAME. */ - HBITMAP load_bitmap (string name); -}; - - -/* The global singleton object. */ -extern gpgex_bitmaps_t gpgex_bitmaps; - -#endif /* ! GPGEX_BITMAPS_H */ +#endif // RESOURCE_H diff --git a/src/standalone.svg b/src/standalone.svg new file mode 100644 index 0000000..c30e24a --- /dev/null +++ b/src/standalone.svg @@ -0,0 +1,100 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/versioninfo.rc.in b/src/versioninfo.rc.in index 6528dc8..6454bfd 100644 --- a/src/versioninfo.rc.in +++ b/src/versioninfo.rc.in @@ -1,81 +1,80 @@ /* versioninfo.rc.in -*- c -*- * Copyright (C) 2005, 2007, 2009, 2013 g10 Code GmbH * * This file is free software; as a special exception the author gives * unlimited permission to copy and/or distribute it, with or without * modifications, as long as this notice is preserved. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* This file is processed by configure to create versioninfo.rc */ #line __LINE__ "versioninfo.rc.in" +#include "resource.h" + #include 1 ICON "./gnupg.ico" 1 VERSIONINFO FILEVERSION @BUILD_FILEVERSION@ PRODUCTVERSION @BUILD_FILEVERSION@ FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/ #else FILEFLAGS 0x00L #endif FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */ FILETYPE 0x2L /* VFT_DLL (0x2) */ FILESUBTYPE 0x0L /* VFT2_UNKNOWN */ BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" /* US English (0409), Unicode (04b0) */ BEGIN VALUE "FileDescription", "GpgEX - GnuPG shell extensions\0" VALUE "InternalName", "gpgex\0" VALUE "OriginalFilename", "gpgex.dll\0" VALUE "ProductName", "GpgEX\0" VALUE "ProductVersion", "@VERSION@\0" VALUE "CompanyName", "g10 Code GmbH\0" VALUE "FileVersion", "@VERSION@\0" VALUE "LegalCopyright", "Copyright \xa9 2013 g10 Code GmbH\r\n \ This program is free software; you can redistribute it and/or modify it \ under the terms of the GNU Lesser General Public License as published by \ the Free Software Foundation; either version 2.1 of the License, \ or (at your option) any later version.\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END /* Fixme: It was not possible to convince Windows to use the manifest. * Thus we resort to static linking of gpgex with the helper * libraries. The idea was to put have this directory layout * * bin/gpgex.dll * bin/GNU.GnuPG.Gcc64Support/GNU.GnuPG.Gcc64Support.manifest * bin/GNU.GnuPG.Gcc64Support/libstdc++-6.dll * bin/GNU.GnuPG.Gcc64Support/libgcc_s_sjlj-1.dll * * to cope with the 64 bit and 32 bit versions of the gcc support * libraries. It works with exe files but not with DLLs loaded. Any * hints to fix this are welcome. */ /* ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "gpgex.manifest" */ /* * Our bitmaps. */ -Key_12 BITMAP "icon-key-12.bmp" -Key_16 BITMAP "icon-key-16.bmp" -Lock_12 BITMAP "icon-lock-12.bmp" -Lock_16 BITMAP "icon-lock-16.bmp" +IDI_ICON_16 RCDATA "icon-16.png"