Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34183564
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
38 KB
Subscribers
None
View Options
diff --git a/src/client.cc b/src/client.cc
index ccfa0ac..a9ce5f0 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -1,584 +1,573 @@
/* client.cc - gpgex assuan client implementation
Copyright (C) 2007, 2008, 2013, 2014 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 <config.h>
#endif
#include <vector>
#include <string>
#include <stdexcept>
using std::vector;
using std::string;
#include <winsock2.h>
#include <windows.h>
#include <assuan.h>
#include "main.h"
#include "registry.h"
#include "exechelp.h"
#include "client.h"
static inline char *
_gpgex_stpcpy (char *a, const char *b)
{
while (*b)
*a++ = *b++;
*a = 0;
return a;
}
#define stpcpy(a,b) _gpgex_stpcpy ((a), (b))
static const char *
default_socket_name (void)
{
static char *name;
if (!name)
{
const char *dir;
const char sockname[] = "\\S.uiserver";
dir = default_homedir ();
if (dir)
{
name = (char *)malloc (strlen (dir) + strlen (sockname) + 1);
if (name)
{
strcpy (name, dir);
strcat (name, sockname);
}
}
}
return name;
}
/* Return the name of the default UI server. This name is used to
auto start an UI server if an initial connect failed. */
static const char *
default_uiserver_cmdline (void)
{
static char *name;
if (!name)
#if ENABLE_GPA_ONLY
{
const char gpaserver[] = "bin\\launch-gpa.exe";
const char *dir;
char *p;
dir = gpgex_server::root_dir;
if (!dir)
return NULL;
name = (char*)malloc (strlen (dir) + strlen (gpaserver) + 9 + 2);
if (!name)
return NULL;
strcpy (stpcpy (stpcpy (name, dir), "\\"), gpaserver);
for (p = name; *p; p++)
if (*p == '/')
*p = '\\';
strcat (name, " --daemon");
gpgex_server::ui_server = "GPA";
}
#else /*!ENABLE_GPA_ONLY*/
{
const char *dir, **tmp;
char *uiserver, *p;
int extra_arglen = 9;
const char * server_names[] = {"kleopatra.exe",
"bin\\kleopatra.exe",
"launch-gpa.exe",
"bin\\launch-gpa.exe",
"gpa.exe",
"bin\\gpa.exe",
NULL};
dir = gpgex_server::root_dir;
if (!dir)
return NULL;
uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_2,
"UI Server");
if (!uiserver)
{
uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_3,
"UI Server");
}
if (!uiserver)
{
uiserver = strdup ("kleopatra.exe");
if (!uiserver)
return NULL;
}
if (uiserver)
{
name = (char*) malloc (strlen (dir) + strlen (uiserver) +
extra_arglen + 2);
if (!name)
return NULL;
strcpy (stpcpy (stpcpy (name, dir), "\\"), uiserver);
for (p = name; *p; p++)
if (*p == '/')
*p = '\\';
free (uiserver);
}
if (name && !access (name, F_OK))
{
/* Set through registry or default kleo */
if (strstr (name, "kleopatra.exe"))
{
gpgex_server::ui_server = "Kleopatra";
strcat (name, " --daemon");
}
else
{
gpgex_server::ui_server = "GPA";
}
return name;
}
/* Fallbacks */
for (tmp = server_names; *tmp; tmp++)
{
if (name)
{
free (name);
}
name = (char*) malloc (strlen (dir) + strlen (*tmp) + extra_arglen + 2);
if (!name)
return NULL;
strcpy (stpcpy (stpcpy (name, dir), "\\"), *tmp);
for (p = name; *p; p++)
if (*p == '/')
*p = '\\';
if (!access (name, F_OK))
{
/* Found a viable candidate */
/* Set through registry and is accessible */
if (strstr (name, "kleopatra.exe"))
{
gpgex_server::ui_server = "Kleopatra";
strcat (name, " --daemon");
}
else
{
gpgex_server::ui_server = "GPA";
}
return name;
}
}
gpgex_server::ui_server = NULL;
}
#endif /*!ENABLE_GPA_ONLY*/
return name;
}
#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
/* Percent-escape the string STR by replacing colons with '%3a'. If
EXTRA is not NULL all characters in it are also escaped. */
static char *
percent_escape (const char *str, const char *extra)
{
int i, j;
char *ptr;
if (!str)
return NULL;
for (i=j=0; str[i]; i++)
if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
j++;
ptr = (char *) malloc (i + 2 * j + 1);
i = 0;
while (*str)
{
/* FIXME: Work around a bug in Kleo. */
if (*str == ':')
{
ptr[i++] = '%';
ptr[i++] = '3';
ptr[i++] = 'a';
}
else
if (*str == '%')
{
ptr[i++] = '%';
ptr[i++] = '2';
ptr[i++] = '5';
}
else if (extra && strchr (extra, *str))
{
ptr[i++] = '%';
ptr[i++] = tohex_lower ((*str >> 4) & 15);
ptr[i++] = tohex_lower (*str & 15);
}
else
ptr[i++] = *str;
str++;
}
ptr[i] = '\0';
return ptr;
}
static string
escape (string str)
{
char *arg_esc = percent_escape (str.c_str (), "+= ");
if (arg_esc == NULL)
- throw std::bad_alloc ();
+ return std::string();
string res = arg_esc;
free (arg_esc);
return res;
}
/* Send options to the UI server and return the server's PID. */
static gpg_error_t
send_one_option (assuan_context_t ctx, const char *name, const char *value)
{
gpg_error_t err;
char buffer[1024];
if (! value || ! *value)
err = 0; /* Avoid sending empty strings. */
else
{
snprintf (buffer, sizeof (buffer), "OPTION %s=%s", name, value);
err = assuan_transact (ctx, buffer, NULL, NULL, NULL, NULL, NULL, NULL);
}
return err;
}
static gpg_error_t
getinfo_pid_cb (void *opaque, const void *buffer, size_t length)
{
pid_t *pid = (pid_t *) opaque;
*pid = (pid_t) strtoul ((char *) buffer, NULL, 10);
return 0;
}
static gpg_error_t
send_options (assuan_context_t ctx, HWND hwnd, pid_t *r_pid)
{
gpg_error_t rc = 0;
char numbuf[50];
TRACE_BEG (DEBUG_ASSUAN, "client_t::send_options", ctx);
*r_pid = (pid_t) (-1);
rc = assuan_transact (ctx, "GETINFO pid", getinfo_pid_cb, r_pid,
NULL, NULL, NULL, NULL);
if (! rc && *r_pid == (pid_t) (-1))
{
(void) TRACE_LOG ("server did not return a PID");
rc = gpg_error (GPG_ERR_ASSUAN_SERVER_FAULT);
}
if (! rc && *r_pid != (pid_t) (-1)
&& ! AllowSetForegroundWindow (*r_pid))
{
(void) TRACE_LOG ("AllowSetForegroundWindow (%u) failed");
TRACE_RES (HRESULT_FROM_WIN32 (GetLastError ()));
/* Ignore the error, though. */
}
if (! rc && hwnd)
{
/* We hope that HWND is limited to 32 bit. If not a 32 bit
UI-server would not be able to do anything with this
window-id. */
uintptr_t tmp = (uintptr_t)hwnd;
if (!(tmp & ~0xffffffff))
{
/* HWND fits into 32 bit - send it. */
snprintf (numbuf, sizeof (numbuf), "%lx", (unsigned long)tmp);
rc = send_one_option (ctx, "window-id", numbuf);
}
}
return TRACE_GPGERR (rc);
}
static gpg_error_t
uiserver_connect (assuan_context_t *ctx, HWND hwnd)
{
gpg_error_t rc;
const char *socket_name = NULL;
pid_t pid;
lock_spawn_t lock;
TRACE_BEG (DEBUG_ASSUAN, "client_t::uiserver_connect", ctx);
socket_name = default_socket_name ();
if (! socket_name || ! *socket_name)
{
(void) TRACE_LOG ("invalid socket name");
return TRACE_GPGERR (gpg_error (GPG_ERR_INV_ARG));
}
(void) TRACE_LOG1 ("socket name: %s", socket_name);
rc = assuan_new (ctx);
if (rc)
{
(void) TRACE_LOG ("could not allocate context");
return TRACE_GPGERR (rc);
}
rc = assuan_socket_connect (*ctx, socket_name, -1, 0);
if (rc)
{
int count;
(void) TRACE_LOG ("UI server not running, starting it");
/* Now try to connect again with the spawn lock taken. */
if (!(rc = gpgex_lock_spawning (&lock))
&& assuan_socket_connect (*ctx, socket_name, -1, 0))
{
rc = gpgex_spawn_detached (default_uiserver_cmdline ());
if (!rc)
{
/* Give it a bit of time to start up and try a couple of
times. */
for (count = 0; count < 10; count++)
{
Sleep (1000);
rc = assuan_socket_connect (*ctx, socket_name, -1, 0);
if (!rc)
break;
}
}
}
gpgex_unlock_spawning (&lock);
}
if (! rc)
{
if (debug_flags & DEBUG_ASSUAN)
assuan_set_log_stream (*ctx, debug_file);
rc = send_options (*ctx, hwnd, &pid);
if (rc)
{
assuan_release (*ctx);
*ctx = NULL;
}
}
return TRACE_GPGERR (rc);
}
typedef struct async_arg
{
const char *cmd;
vector<string> filenames;
HWND wid;
} async_arg_t;
static DWORD WINAPI
call_assuan_async (LPVOID arg)
{
async_arg_t *async_args = (async_arg_t *)arg;
int rc = 0;
int connect_failed = 0;
const char *cmd = async_args->cmd;
const vector<string> filenames = async_args->filenames;
assuan_context_t ctx = NULL;
string msg;
TRACE_BEG2 (DEBUG_ASSUAN, "client_t::call_assuan_async", 0,
"%s on %u files", cmd, filenames.size ());
rc = uiserver_connect (&ctx, async_args->wid);
if (rc)
{
connect_failed = 1;
goto leave;
}
- try
- {
- /* Set the input files. We don't specify the output files. */
- for (unsigned int i = 0; i < filenames.size (); i++)
- {
- msg = "FILE " + escape (filenames[i]);
-
- (void) TRACE_LOG1 ("sending cmd: %s", msg.c_str ());
-
- rc = assuan_transact (ctx, msg.c_str (),
- NULL, NULL, NULL, NULL, NULL, NULL);
- if (rc)
- goto leave;
- }
-
- /* Set the --nohup option, so that the operation continues and
- completes in the background. */
- msg = ((string) cmd) + " --nohup";
- (void) TRACE_LOG1 ("sending cmd: %s", msg.c_str ());
- rc = assuan_transact (ctx, msg.c_str (),
- NULL, NULL, NULL, NULL, NULL, NULL);
- }
- catch (std::bad_alloc)
- {
- rc = gpg_error (GPG_ERR_ENOMEM);
- }
- catch (...)
- {
- rc = gpg_error (GPG_ERR_GENERAL);
- }
+ /* Set the input files. We don't specify the output files. */
+ for (unsigned int i = 0; i < filenames.size (); i++)
+ {
+ msg = "FILE " + escape (filenames[i]);
+
+ (void) TRACE_LOG1 ("sending cmd: %s", msg.c_str ());
+
+ rc = assuan_transact (ctx, msg.c_str (),
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ goto leave;
+ }
+
+ /* Set the --nohup option, so that the operation continues and
+ completes in the background. */
+ msg = ((string) cmd) + " --nohup";
+ (void) TRACE_LOG1 ("sending cmd: %s", msg.c_str ());
+ rc = assuan_transact (ctx, msg.c_str (),
+ NULL, NULL, NULL, NULL, NULL, NULL);
/* Fall-through. */
leave:
TRACE_GPGERR (rc);
if (ctx)
assuan_release (ctx);
if (rc)
{
char buf[256];
if (connect_failed)
snprintf (buf, sizeof (buf),
_("Can not connect to the GnuPG user interface%s%s%s:\r\n%s"),
gpgex_server::ui_server? " (":"",
gpgex_server::ui_server? gpgex_server::ui_server:"",
gpgex_server::ui_server? ")":"",
gpg_strerror (rc));
else
snprintf (buf, sizeof (buf),
_("Error returned by the GnuPG user interface%s%s%s:\r\n%s"),
gpgex_server::ui_server? " (":"",
gpgex_server::ui_server? gpgex_server::ui_server:"",
gpgex_server::ui_server? ")":"",
gpg_strerror (rc));
MessageBox (async_args->wid, buf, "GpgEX", MB_ICONINFORMATION);
}
delete async_args;
return 0;
}
void
client_t::call_assuan (const char *cmd, vector<string> &filenames)
{
TRACE_BEG (DEBUG_ASSUAN, "client_t::call_assuan", cmd);
async_arg_t * args = new async_arg_t;
args->cmd = cmd;
args->filenames = filenames;
args->wid = this->window;
/* We move the call in a different thread as the Windows explorer
is blocked until our call finishes. We don't want that.
Additionally Kleopatra / Qt5 SendsMessages to the parent
window provided in wid. Qt does this with blocking calls
so Kleopatra blocks until the explorer processes more
Window Messages and we block the explorer. This is
a deadlock. */
CreateThread (NULL, 0, call_assuan_async, (LPVOID) args, 0,
NULL);
return;
}
void
client_t::decrypt_verify (vector<string> &filenames)
{
this->call_assuan ("DECRYPT_VERIFY_FILES", filenames);
}
void
client_t::verify (vector<string> &filenames)
{
this->call_assuan ("VERIFY_FILES", filenames);
}
void
client_t::decrypt (vector<string> &filenames)
{
this->call_assuan ("DECRYPT_FILES", filenames);
}
void
client_t::sign_encrypt (vector<string> &filenames)
{
this->call_assuan ("ENCRYPT_SIGN_FILES", filenames);
}
void
client_t::encrypt (vector<string> &filenames)
{
this->call_assuan ("ENCRYPT_FILES", filenames);
}
void
client_t::sign (vector<string> &filenames)
{
this->call_assuan ("SIGN_FILES", filenames);
}
void
client_t::import (vector<string> &filenames)
{
this->call_assuan ("IMPORT_FILES", filenames);
}
void
client_t::create_checksums (vector<string> &filenames)
{
this->call_assuan ("CHECKSUM_CREATE_FILES", filenames);
}
void
client_t::verify_checksums (vector<string> &filenames)
{
this->call_assuan ("CHECKSUM_VERIFY_FILES", filenames);
}
diff --git a/src/gpgex.cc b/src/gpgex.cc
index 8c973d8..84e9901 100644
--- a/src/gpgex.cc
+++ b/src/gpgex.cc
@@ -1,749 +1,731 @@
/* 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 <config.h>
#endif
#include <vector>
#include <string>
#include <stdexcept>
using std::vector;
using std::string;
#include <windows.h>
/* For the start_help() function. */
#include <exdisp.h>
#include "main.h"
#include "client.h"
#include "registry.h"
#include "gpgex.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<IShellExtInit *> (this);
else if (riid == IID_IShellExtInit)
*ppv = static_cast<IShellExtInit *> (this);
else if (riid == IID_IContextMenu)
*ppv = static_cast<IContextMenu3 *> (this);
#if 0
/* FIXME: Enable this when the functions are actually
implemented. */
else if (riid == IID_IContextMenu2)
*ppv = static_cast<IContextMenu3 *> (this);
else if (riid == IID_IContextMenu3)
*ppv = static_cast<IContextMenu3 *> (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<IUnknown *>(*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 ();
- try
+ if (pDataObj)
{
- 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)
- throw std::invalid_argument ("no filenames");
-
- try
- {
- for (i = 0; i < count; i++)
- {
- char filename[MAX_PATH];
- UINT len;
- len = DragQueryFile (drop, i,
- filename, sizeof (filename) - 1);
- if (len == 0)
- throw std::invalid_argument ("zero-length filename");
-
- /* 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);
- }
- }
- catch (...)
- {
- GlobalUnlock (medium.hGlobal);
- ReleaseStgMedium (&medium);
- throw;
- }
-
- GlobalUnlock (medium.hGlobal);
- ReleaseStgMedium (&medium);
- }
- }
- }
- catch (std::bad_alloc)
- {
- err = E_OUTOFMEMORY;
- }
- catch (std::invalid_argument &e)
- {
- (VOID) TRACE_LOG1 ("exception: E_INVALIDARG: %s", e.what ());
- err = E_INVALIDARG;
- }
- catch (...)
- {
- err = E_UNEXPECTED;
+ /* 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);
}
/* 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);
}
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 <http://www.gnu.org/licenses/>.";
/* 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;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Dec 14, 9:57 AM (19 h, 16 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d7/7c/d13cd6e152323c9f9e953b300717
Attached To
rX GpgEX
Event Timeline
Log In to Comment