diff --git a/src/debug.cpp b/src/debug.cpp
index 454de19..13a33bb 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -1,352 +1,351 @@
/* debug.cpp - Debugging / Log helpers for GpgOL
* Copyright (C) 2018 by by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL 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.
*
* GpgOL 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
* 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, see .
*/
#include "common_indep.h"
#include
#include
#include
/* The malloced name of the logfile and the logging stream. If
LOGFILE is NULL, no logging is done. */
static char *logfile;
static FILE *logfp;
#ifdef HAVE_W32_SYSTEM
/* Acquire the mutex for logging. Returns 0 on success. */
static int
lock_log (void)
{
int code = WaitForSingleObject (log_mutex, 10000);
return code != WAIT_OBJECT_0;
}
/* Release the mutex for logging. No error return is done because this
is a fatal error anyway and we have no means for proper
notification. */
static void
unlock_log (void)
{
ReleaseMutex (log_mutex);
}
#endif
const char *
get_log_file (void)
{
return logfile? logfile : "";
}
void
set_log_file (const char *name)
{
#ifdef HAVE_W32_SYSTEM
if (!lock_log ())
{
#endif
if (logfp)
{
fclose (logfp);
logfp = NULL;
}
xfree (logfile);
if (!name || *name == '\"' || !*name)
logfile = NULL;
else
logfile = xstrdup (name);
#ifdef HAVE_W32_SYSTEM
unlock_log ();
}
#endif
}
static void
do_log (const char *fmt, va_list a, int w32err, int err,
const void *buf, size_t buflen)
{
if (!logfile)
return;
#ifdef HAVE_W32_SYSTEM
if (!opt.enable_debug)
return;
if (lock_log ())
{
OutputDebugStringA ("GpgOL: Failed to log.");
return;
}
#endif
if (!strcmp (logfile, "stdout"))
{
logfp = stdout;
}
else if (!strcmp (logfile, "stderr"))
{
logfp = stderr;
}
if (!logfp)
logfp = fopen (logfile, "a+");
#ifdef HAVE_W32_SYSTEM
if (!logfp)
{
unlock_log ();
return;
}
char time_str[9];
SYSTEMTIME utc_time;
GetSystemTime (&utc_time);
if (GetTimeFormatA (LOCALE_INVARIANT,
TIME_FORCE24HOURFORMAT | LOCALE_USE_CP_ACP,
&utc_time,
"HH:mm:ss",
time_str,
9))
{
fprintf (logfp, "%s/%lu/",
time_str,
(unsigned long)GetCurrentThreadId ());
}
else
{
fprintf (logfp, "unknown/%lu/",
(unsigned long)GetCurrentThreadId ());
}
#endif
if (err == 1)
fputs ("ERROR/", logfp);
vfprintf (logfp, fmt, a);
#ifdef HAVE_W32_SYSTEM
if (w32err)
{
char tmpbuf[256];
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32err,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
tmpbuf, sizeof (tmpbuf)-1, NULL);
fputs (": ", logfp);
if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\n')
tmpbuf[strlen (tmpbuf)-1] = 0;
if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\r')
tmpbuf[strlen (tmpbuf)-1] = 0;
fprintf (logfp, "%s (%d)", tmpbuf, w32err);
}
#endif
if (buf)
{
const unsigned char *p = (const unsigned char*)buf;
for ( ; buflen; buflen--, p++)
fprintf (logfp, "%02X", *p);
putc ('\n', logfp);
}
else if ( *fmt && fmt[strlen (fmt) - 1] != '\n')
putc ('\n', logfp);
fflush (logfp);
#ifdef HAVE_W32_SYSTEM
unlock_log ();
#endif
}
const char *
log_srcname (const char *file)
{
const char *s = strrchr (file, '/');
return s? s+1:file;
}
void
log_debug (const char *fmt, ...)
{
va_list a;
va_start (a, fmt);
do_log (fmt, a, 0, 0, NULL, 0);
va_end (a);
}
void
log_error (const char *fmt, ...)
{
va_list a;
va_start (a, fmt);
do_log (fmt, a, 0, 1, NULL, 0);
va_end (a);
}
void
log_vdebug (const char *fmt, va_list a)
{
do_log (fmt, a, 0, 0, NULL, 0);
}
void
log_hexdump (const void *buf, size_t buflen, const char *fmt, ...)
{
va_list a;
va_start (a, fmt);
do_log (fmt, a, 0, 2, buf, buflen);
va_end (a);
}
#ifdef HAVE_W32_SYSTEM
void
log_debug_w32 (int w32err, const char *fmt, ...)
{
va_list a;
if (w32err == -1)
w32err = GetLastError ();
va_start (a, fmt);
do_log (fmt, a, w32err, 0, NULL, 0);
va_end (a);
}
void
log_error_w32 (int w32err, const char *fmt, ...)
{
va_list a;
if (w32err == -1)
w32err = GetLastError ();
va_start (a, fmt);
do_log (fmt, a, w32err, 1, NULL, 0);
va_end (a);
}
static void
do_log_window_info (HWND window, int level)
{
char buf[1024+1];
char name[200];
int nname;
char *pname;
DWORD pid;
if (!window)
return;
GetWindowThreadProcessId (window, &pid);
if (pid != GetCurrentProcessId ())
return;
memset (buf, 0, sizeof (buf));
GetWindowText (window, buf, sizeof (buf)-1);
nname = GetClassName (window, name, sizeof (name)-1);
if (nname)
pname = name;
else
pname = NULL;
if (level == -1)
log_debug (" parent=%p/%lu (%s) `%s'", window, (unsigned long)pid,
pname? pname:"", buf);
else
log_debug (" %*shwnd=%p/%lu (%s) `%s'", level*2, "", window,
(unsigned long)pid, pname? pname:"", buf);
}
/* Helper to log_window_hierarchy. */
static HWND
do_log_window_hierarchy (HWND parent, int level)
{
HWND child;
child = GetWindow (parent, GW_CHILD);
while (child)
{
do_log_window_info (child, level);
do_log_window_hierarchy (child, level+1);
child = GetNextWindow (child, GW_HWNDNEXT);
}
return NULL;
}
/* Print a debug message using the format string FMT followed by the
window hierarchy of WINDOW. */
void
log_window_hierarchy (HWND window, const char *fmt, ...)
{
va_list a;
va_start (a, fmt);
do_log (fmt, a, 0, 0, NULL, 0);
va_end (a);
if (window)
{
do_log_window_info (window, -1);
do_log_window_hierarchy (window, 0);
}
}
#endif
GPGRT_LOCK_DEFINE (anon_str_lock);
/* Weel ok this survives unload but we don't want races
and it makes a bit of sense to keep the strings constant. */
static std::unordered_map str_map;
const char *anonstr (const char *data)
{
static int64_t cnt;
if (opt.enable_debug & DBG_DATA)
{
return data;
}
if (!data)
{
return "gpgol_str_null";
}
if (!strlen (data))
{
return "gpgol_str_empty";
}
gpgrt_lock_lock (&anon_str_lock);
const std::string strData (data);
auto it = str_map.find (strData);
if (it == str_map.end ())
{
const auto anon = std::string ("gpgol_string_") + std::to_string (++cnt);
str_map.insert (std::make_pair (strData, anon));
it = str_map.find (strData);
}
// As the data is saved in our map we can return
// the c_str as it won't be touched as const.
gpgrt_lock_unlock (&anon_str_lock);
- TRACEPOINT;
return it->second.c_str();
}
diff --git a/src/mimedataprovider.cpp b/src/mimedataprovider.cpp
index 9f57f17..cf66ddf 100644
--- a/src/mimedataprovider.cpp
+++ b/src/mimedataprovider.cpp
@@ -1,1101 +1,1098 @@
/* mimedataprover.cpp - GpgME dataprovider for mime data
* Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL 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.
*
* GpgOL 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, see .
*/
#include "config.h"
#include "common_indep.h"
#include "xmalloc.h"
#include
#include
#include
#include "mimedataprovider.h"
#include "rfc822parse.h"
#include "rfc2047parse.h"
#include "attachment.h"
#include "cpphelp.h"
#ifndef HAVE_W32_SYSTEM
#define stricmp strcasecmp
#endif
/* The maximum length of a line we are able to process. RFC822 allows
only for 1000 bytes; thus 2000 seems to be a reasonable value. */
#define LINEBUFSIZE 2000
/* How much data is read at once in collect */
#define BUFSIZE 8192
#include
/* To keep track of the MIME message structures we use a linked list
with each item corresponding to one part. */
struct mimestruct_item_s;
typedef struct mimestruct_item_s *mimestruct_item_t;
struct mimestruct_item_s
{
mimestruct_item_t next;
unsigned int level; /* Level in the hierarchy of that part. 0
indicates the outer body. */
char *filename; /* Malloced filename or NULL. */
char *cid; /* Malloced content id or NULL. */
char *charset; /* Malloced charset or NULL. */
char content_type[1]; /* String with the content type. */
};
/* The context object we use to track information. */
struct mime_context
{
rfc822parse_t msg; /* The handle of the RFC822 parser. */
int verify_mode; /* True if we want to verify a signature. */
int nesting_level; /* Current MIME nesting level. */
int in_data; /* We are currently in data (body or attachment). */
int body_seen; /* True if we have seen a part we consider the
body of the message. */
std::shared_ptr current_attachment; /* A pointer to the current
attachment */
int collect_body; /* True if we are collcting the body */
int collect_html_body; /* True if we are collcting the html body */
int collect_crypto_data; /* True if we are collecting the signed data. */
int collect_signature; /* True if we are collecting a signature. */
int pgp_marker_checked; /* Checked if the first body line is pgp marker*/
int is_encrypted; /* True if we are working on an encrypted mail. */
int start_hashing; /* Flag used to start collecting signed data. */
int hashing_level; /* MIME level where we started hashing. */
int is_qp_encoded; /* Current part is QP encoded. */
int is_base64_encoded; /* Current part is base 64 encoded. */
int is_body; /* The current part belongs to the body. */
protocol_t protocol; /* The detected crypto protocol. */
int part_counter; /* Counts the number of processed parts. */
int any_boundary; /* Indicates whether we have seen any
boundary which means that we are actually
working on a MIME message and not just on
plain rfc822 message. */
/* A linked list describing the structure of the mime message. This
list gets build up while parsing the message. */
mimestruct_item_t mimestruct;
mimestruct_item_t *mimestruct_tail;
mimestruct_item_t mimestruct_cur;
int any_attachments_created; /* True if we created a new atatchment. */
b64_state_t base64; /* The state of the Base-64 decoder. */
gpg_error_t parser_error; /* Indicates that we encountered a error from
the parser. */
};
typedef struct mime_context *mime_context_t;
/* Print the message event EVENT. */
static void
debug_message_event (rfc822parse_event_t event)
{
const char *s;
switch (event)
{
case RFC822PARSE_OPEN: s= "Open"; break;
case RFC822PARSE_CLOSE: s= "Close"; break;
case RFC822PARSE_CANCEL: s= "Cancel"; break;
case RFC822PARSE_T2BODY: s= "T2Body"; break;
case RFC822PARSE_FINISH: s= "Finish"; break;
case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
default: s= "[unknown event]"; break;
}
log_data ("%s: rfc822 event %s\n", SRCNAME, s);
}
/* returns true if the BER encoded data in BUFFER is CMS signed data.
LENGTH gives the length of the buffer, for correct detection LENGTH
should be at least about 24 bytes. */
#if 0
static int
is_cms_signed_data (const char *buffer, size_t length)
{
TSTART;
const char *p = buffer;
size_t n = length;
tlvinfo_t ti;
if (parse_tlv (&p, &n, &ti))
{
TRETURN 0;
}
if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
&& ti.is_cons) )
TRETURN 0;
if (parse_tlv (&p, &n, &ti))
{
TRETURN 0;
}
if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_OBJECT_ID
&& !ti.is_cons && ti.length) || ti.length > n)
TRETURN 0;
if (ti.length == 9 && !memcmp (p, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x02", 9))
{
TRETURN 1;
}
TRETURN 0;
}
#endif
/* Process the transition to body event.
This means we have received the empty line indicating the body and
should now check the headers to see what to do about this part.
This is mostly a C style function because it was based on the old
c mimeparser.
*/
static int
t2body (MimeDataProvider *provider, rfc822parse_t msg)
{
TSTART;
rfc822parse_field_t field;
mime_context_t ctx = provider->mime_context ();
const char *ctmain, *ctsub;
const char *s;
size_t off;
char *p;
int is_text = 0;
int is_text_attachment = 0;
char *filename = NULL;
char *cid = NULL;
char *charset = NULL;
/* Figure out the encoding. */
ctx->is_qp_encoded = 0;
ctx->is_base64_encoded = 0;
p = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1, &off);
if (p)
{
if (!stricmp (p+off, "quoted-printable"))
ctx->is_qp_encoded = 1;
else if (!stricmp (p+off, "base64"))
{
ctx->is_base64_encoded = 1;
b64_init (&ctx->base64);
}
xfree (p);
}
/* Get the filename from the header. */
field = rfc822parse_parse_field (msg, "Content-Disposition", -1);
if (field)
{
s = rfc822parse_query_parameter (field, "filename", 0);
if (s)
filename = rfc2047_parse (s);
s = rfc822parse_query_parameter (field, NULL, 1);
/* This is a bit of a taste matter how to treat inline
attachments. Outlook does not show them inline so we
should not put it in the body either as we have
no way to show that it was actually an attachment.
For something like an inline patch it is better
to add it as an attachment instead of just putting
it in the body.
The handling in the old parser was:
if (s && strcmp (s, "inline"))
not_inline_text = 1;
*/
if (ctx->body_seen)
{
/* Some MUA's like kontact e3.5 send the body as
an inline text attachment. So if we have not
seen the body yet we treat the first text/plain
element as the body and not as an inline attachment. */
is_text_attachment = 1;
}
rfc822parse_release_field (field);
}
/* Process the Content-type and all its parameters. */
ctmain = ctsub = NULL;
field = rfc822parse_parse_field (msg, "Content-Type", -1);
if (field)
ctmain = rfc822parse_query_media_type (field, &ctsub);
if (!ctmain)
{
/* Either there is no content type field or it is faulty; in
both cases we fall back to text/plain. */
ctmain = "text";
ctsub = "plain";
}
log_data ("%s:%s: ctx=%p, ct=`%s/%s'\n",
SRCNAME, __func__, ctx, ctmain, ctsub);
s = rfc822parse_query_parameter (field, "charset", 0);
if (s)
charset = xstrdup (s);
if (!filename)
{
/* Check for Content-Type name if Content-Disposition filename
was not found */
s = rfc822parse_query_parameter (field, "name", 0);
if (s)
filename = rfc2047_parse (s);
}
/* Parse a Content Id header */
p = rfc822parse_get_field (msg, "Content-Id", -1, &off);
if (p)
{
cid = xstrdup (p+off);
xfree (p);
}
/* Update our idea of the entire MIME structure. */
{
mimestruct_item_t ms;
ms = (mimestruct_item_t) xmalloc (sizeof *ms + strlen (ctmain) + 1 + strlen (ctsub));
ctx->mimestruct_cur = ms;
*ctx->mimestruct_tail = ms;
ctx->mimestruct_tail = &ms->next;
ms->next = NULL;
strcpy (stpcpy (stpcpy (ms->content_type, ctmain), "/"), ctsub);
ms->level = ctx->nesting_level;
ms->filename = filename;
ms->cid = cid;
filename = NULL;
ms->charset = charset;
charset = NULL;
}
if (!strcmp (ctmain, "multipart"))
{
/* We don't care about the top level multipart layer but wait
until it comes to the actual parts which then will get stored
as attachments.
For now encapsulated signed or encrypted containers are not
processed in a special way as they should. Except for the
simple verify mode. */
if (!provider->signature()
&& !strcmp (ctsub, "signed")
&& (s = rfc822parse_query_parameter (field, "protocol", 0)))
{
if (!strcmp (s, "application/pgp-signature"))
ctx->protocol = PROTOCOL_OPENPGP;
else if (!strcmp (s, "application/pkcs7-signature")
|| !strcmp (s, "application/x-pkcs7-signature"))
ctx->protocol = PROTOCOL_SMIME;
else
ctx->protocol = PROTOCOL_UNKNOWN;
/* Need to start the hashing after the next boundary. */
ctx->start_hashing = 1;
}
else if (!strcmp (ctsub, "encrypted") &&
(s = rfc822parse_query_parameter (field, "protocol", 0)))
{
if (!strcmp (s, "application/pgp-encrypted"))
ctx->protocol = PROTOCOL_OPENPGP;
/* We expect an encrypted mime part. */
ctx->is_encrypted = 1;
}
}
else if (!strcmp (ctmain, "text"))
{
is_text = !strcmp (ctsub, "html")? 2:1;
}
else if (ctx->nesting_level == 1 && !provider->signature()
&& !strcmp (ctmain, "application")
&& ((ctx->protocol == PROTOCOL_OPENPGP
&& !strcmp (ctsub, "pgp-signature"))
|| (ctx->protocol == PROTOCOL_SMIME
&& (!strcmp (ctsub, "pkcs7-signature")
|| !strcmp (ctsub, "x-pkcs7-signature")))))
{
/* This is the second part of a MOSS signature. We only support
here full messages thus checking the nesting level is
sufficient. We do this only for the first signature (i.e. if
sig_data has not been set yet). We also do this only while
in verify mode because we don't want to write a full MUA. */
ctx->collect_signature = 1;
log_data ("%s:%s: Collecting signature.",
SRCNAME, __func__);
}
else if (ctx->nesting_level == 1 && ctx->is_encrypted
&& !strcmp (ctmain, "application")
&& (ctx->protocol == PROTOCOL_OPENPGP
&& !strcmp (ctsub, "octet-stream")))
{
log_data ("%s:%s: Collecting encrypted PGP data.",
SRCNAME, __func__);
ctx->collect_crypto_data = 1;
}
else /* Other type. */
{
/* Check whether this attachment is an opaque signed S/MIME
part. We use a counter to later check that there is only one
such part. */
if (!strcmp (ctmain, "application")
&& (!strcmp (ctsub, "pkcs7-mime")
|| !strcmp (ctsub, "x-pkcs7-mime")))
{
log_data ("%s:%s: Collecting crypted S/MIME data.",
SRCNAME, __func__);
ctx->collect_crypto_data = 1;
}
}
rfc822parse_release_field (field); /* (Content-type) */
ctx->in_data = 1;
log_data ("%s:%s: this body: nesting=%d partno=%d is_text=%d"
" charset=\"%s\"\n body_seen=%d is_text_attachment=%d",
SRCNAME, __func__,
ctx->nesting_level, ctx->part_counter, is_text,
ctx->mimestruct_cur->charset?ctx->mimestruct_cur->charset:"",
ctx->body_seen, is_text_attachment);
/* If this is a text part, decide whether we treat it as one
of our bodies.
*/
if ((is_text && !is_text_attachment))
{
if (is_text == 2)
{
ctx->body_seen = 2;
ctx->collect_html_body = 1;
ctx->collect_body = 0;
log_debug ("%s:%s: Collecting HTML body.",
SRCNAME, __func__);
/* We need this crutch because of one liner html
mails which would not be collected by the line
collector if they dont have a linefeed at the
end. */
provider->set_has_html_body (true);
}
else
{
log_debug ("%s:%s: Collecting text body.",
SRCNAME, __func__);
ctx->body_seen = 1;
ctx->collect_body = 1;
ctx->collect_html_body = 0;
}
}
else if (!ctx->collect_crypto_data && ctx->nesting_level >= 1)
{
/* Treat it as an attachment. */
ctx->current_attachment = provider->create_attachment();
ctx->collect_body = 0;
ctx->collect_html_body = 0;
log_data ("%s:%s: Collecting attachment.",
SRCNAME, __func__);
}
TRETURN 0;
}
static int
message_cb (void *opaque, rfc822parse_event_t event,
rfc822parse_t msg)
{
- TSTART;
int retval = 0;
MimeDataProvider *provider = static_cast (opaque);
mime_context_t ctx = provider->mime_context();
debug_message_event (event);
if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
{
/* We need to check here whether to start collecting signed data
because attachments might come without header lines and thus
we won't see the BEGIN_HEADER event. */
if (ctx->start_hashing == 1)
{
ctx->start_hashing = 2;
ctx->hashing_level = ctx->nesting_level;
ctx->collect_crypto_data = 1;
}
}
switch (event)
{
case RFC822PARSE_T2BODY:
retval = t2body (provider, msg);
break;
case RFC822PARSE_LEVEL_DOWN:
ctx->nesting_level++;
break;
case RFC822PARSE_LEVEL_UP:
if (ctx->nesting_level)
ctx->nesting_level--;
else
{
log_error ("%s: ctx=%p, invalid structure: bad nesting level\n",
SRCNAME, ctx);
ctx->parser_error = gpg_error (GPG_ERR_GENERAL);
}
break;
case RFC822PARSE_BOUNDARY:
case RFC822PARSE_LAST_BOUNDARY:
ctx->any_boundary = 1;
ctx->in_data = 0;
ctx->collect_body = 0;
if (ctx->start_hashing == 2 && ctx->hashing_level == ctx->nesting_level)
{
ctx->start_hashing = 3; /* Avoid triggering it again. */
ctx->collect_crypto_data = 0;
}
break;
case RFC822PARSE_BEGIN_HEADER:
ctx->part_counter++;
break;
default: /* Ignore all other events. */
break;
}
- TRETURN retval;
+ return retval;
}
MimeDataProvider::MimeDataProvider(bool no_headers) :
m_signature(nullptr),
m_has_html_body(false),
m_collect_everything(no_headers)
{
TSTART;
memdbg_ctor ("MimeDataProvider");
m_mime_ctx = (mime_context_t) xcalloc (1, sizeof *m_mime_ctx);
m_mime_ctx->msg = rfc822parse_open (message_cb, this);
m_mime_ctx->mimestruct_tail = &m_mime_ctx->mimestruct;
TRETURN;
}
#ifdef HAVE_W32_SYSTEM
MimeDataProvider::MimeDataProvider(LPSTREAM stream, bool no_headers):
MimeDataProvider(no_headers)
{
TSTART;
if (stream)
{
stream->AddRef ();
memdbg_addRef (stream);
}
else
{
log_error ("%s:%s called without stream ", SRCNAME, __func__);
TRETURN;
}
log_data ("%s:%s Collecting data.", SRCNAME, __func__);
collect_data (stream);
log_data ("%s:%s Data collected.", SRCNAME, __func__);
gpgol_release (stream);
TRETURN;
}
#endif
MimeDataProvider::MimeDataProvider(FILE *stream, bool no_headers):
MimeDataProvider(no_headers)
{
TSTART;
log_data ("%s:%s Collecting data from file.", SRCNAME, __func__);
collect_data (stream);
log_data ("%s:%s Data collected.", SRCNAME, __func__);
TRETURN;
}
MimeDataProvider::~MimeDataProvider()
{
TSTART;
memdbg_dtor ("MimeDataProvider");
log_debug ("%s:%s", SRCNAME, __func__);
while (m_mime_ctx->mimestruct)
{
mimestruct_item_t tmp = m_mime_ctx->mimestruct->next;
xfree (m_mime_ctx->mimestruct->filename);
xfree (m_mime_ctx->mimestruct->charset);
xfree (m_mime_ctx->mimestruct->cid);
xfree (m_mime_ctx->mimestruct);
m_mime_ctx->mimestruct = tmp;
}
rfc822parse_close (m_mime_ctx->msg);
m_mime_ctx->current_attachment = NULL;
xfree (m_mime_ctx);
if (m_signature)
{
delete m_signature;
}
TRETURN;
}
bool
MimeDataProvider::isSupported(GpgME::DataProvider::Operation op) const
{
- TSTART;
- TRETURN op == GpgME::DataProvider::Read ||
+ return op == GpgME::DataProvider::Read ||
op == GpgME::DataProvider::Seek ||
op == GpgME::DataProvider::Write ||
op == GpgME::DataProvider::Release;
}
ssize_t
MimeDataProvider::read(void *buffer, size_t size)
{
TSTART;
log_data ("%s:%s: Reading: " SIZE_T_FORMAT "Bytes",
SRCNAME, __func__, size);
ssize_t bRead = m_crypto_data.read (buffer, size);
if (opt.enable_debug & DBG_MIME_DATA && bRead)
{
std::string buf ((char *)buffer, bRead);
if (!is_binary (buf))
{
log_data ("%s:%s: Data: \n------\n%s\n------",
SRCNAME, __func__, buf.c_str());
}
else
{
log_data ("%s:%s: Hex Data: \n------\n%s\n------",
SRCNAME, __func__,
string_to_hex (buf).c_str ());
}
}
TRETURN bRead;
}
/* Split some raw data into lines and handle them accordingly.
Returns the amount of bytes not taken from the input buffer.
*/
size_t
MimeDataProvider::collect_input_lines(const char *input, size_t insize)
{
TSTART;
char linebuf[LINEBUFSIZE];
const char *s = input;
size_t pos = 0;
size_t nleft = insize;
size_t not_taken = nleft;
size_t len = 0;
/* Split the raw data into lines */
for (; nleft; nleft--, s++)
{
if (pos >= LINEBUFSIZE)
{
log_error ("%s:%s: rfc822 parser failed: line too long\n",
SRCNAME, __func__);
GpgME::Error::setSystemError (GPG_ERR_EIO);
TRETURN not_taken;
}
if (*s != '\n')
linebuf[pos++] = *s;
else
{
/* Got a complete line. Remove the last CR. */
not_taken -= pos + 1; /* Pos starts at 0 so + 1 for it */
if (pos && linebuf[pos-1] == '\r')
{
pos--;
}
log_data ("%s:%s: Parsing line=`%.*s'\n",
SRCNAME, __func__, (int)pos, linebuf);
/* Check the next state */
if (rfc822parse_insert (m_mime_ctx->msg,
(unsigned char*) linebuf,
pos))
{
log_error ("%s:%s: rfc822 parser failed: %s\n",
SRCNAME, __func__, strerror (errno));
TRETURN not_taken;
}
/* Check if the first line of the body is actually
a PGP Inline message. If so treat it as crypto data. */
if (!m_mime_ctx->pgp_marker_checked && m_mime_ctx->collect_body == 2)
{
m_mime_ctx->pgp_marker_checked = true;
if (pos >= 27 && !strncmp ("-----BEGIN PGP MESSAGE-----", linebuf, 27))
{
log_debug ("%s:%s: Found PGP Message in body.",
SRCNAME, __func__);
m_mime_ctx->collect_body = 0;
m_mime_ctx->collect_crypto_data = 1;
m_mime_ctx->start_hashing = 1;
m_collect_everything = true;
}
}
/* If we are currently in a collecting state actually
collect that line */
if (m_mime_ctx->collect_crypto_data && m_mime_ctx->start_hashing)
{
/* Save the signed data. Note that we need to delay
the CR/LF because the last line ending belongs to the
next boundary. */
if (m_mime_ctx->collect_crypto_data == 2)
{
m_crypto_data.write ("\r\n", 2);
}
log_data ("Writing raw crypto data: %.*s",
(int)pos, linebuf);
m_crypto_data.write (linebuf, pos);
m_mime_ctx->collect_crypto_data = 2;
}
if (m_mime_ctx->in_data && !m_mime_ctx->collect_signature &&
!m_mime_ctx->collect_crypto_data)
{
/* We are inside of an attachment part. Write it out. */
if (m_mime_ctx->in_data == 1) /* Skip the first line. */
m_mime_ctx->in_data = 2;
int slbrk = 0;
if (m_mime_ctx->is_qp_encoded)
len = qp_decode (linebuf, pos, &slbrk);
else if (m_mime_ctx->is_base64_encoded)
len = b64_decode (&m_mime_ctx->base64, linebuf, pos);
else
len = pos;
if (m_mime_ctx->collect_body)
{
if (m_mime_ctx->collect_body == 2)
{
m_body += std::string(linebuf, len);
if (!m_mime_ctx->is_base64_encoded && !slbrk)
{
m_body += "\r\n";
}
}
if (m_body_charset.empty())
{
m_body_charset = m_mime_ctx->mimestruct_cur->charset ?
m_mime_ctx->mimestruct_cur->charset : "";
}
m_mime_ctx->collect_body = 2;
}
else if (m_mime_ctx->collect_html_body)
{
if (m_mime_ctx->collect_html_body == 2)
{
m_html_body += std::string(linebuf, len);
if (!m_mime_ctx->is_base64_encoded && !slbrk)
{
m_html_body += "\r\n";
}
}
if (m_html_charset.empty())
{
m_html_charset = m_mime_ctx->mimestruct_cur->charset ?
m_mime_ctx->mimestruct_cur->charset : "";
}
m_mime_ctx->collect_html_body = 2;
}
else if (m_mime_ctx->current_attachment && len)
{
m_mime_ctx->current_attachment->get_data().write(linebuf, len);
if (!m_mime_ctx->is_base64_encoded && !slbrk)
{
m_mime_ctx->current_attachment->get_data().write("\r\n", 2);
}
}
else
{
log_data ("%s:%s Collecting ended / failed.",
SRCNAME, __func__);
}
}
else if (m_mime_ctx->in_data && m_mime_ctx->collect_signature)
{
/* We are inside of a signature attachment part. */
if (m_mime_ctx->collect_signature == 1) /* Skip the first line. */
m_mime_ctx->collect_signature = 2;
else
{
int slbrk = 0;
if (m_mime_ctx->is_qp_encoded)
len = qp_decode (linebuf, pos, &slbrk);
else if (m_mime_ctx->is_base64_encoded)
len = b64_decode (&m_mime_ctx->base64, linebuf, pos);
else
len = pos;
if (!m_signature)
{
m_signature = new GpgME::Data();
}
if (len)
m_signature->write(linebuf, len);
if (!m_mime_ctx->is_base64_encoded && !slbrk)
m_signature->write("\r\n", 2);
}
}
else if (m_mime_ctx->in_data && !m_mime_ctx->start_hashing)
{
/* We are inside the data. That should be the actual
ciphertext in the given encoding. */
int slbrk = 0;
if (m_mime_ctx->is_qp_encoded)
len = qp_decode (linebuf, pos, &slbrk);
else if (m_mime_ctx->is_base64_encoded)
len = b64_decode (&m_mime_ctx->base64, linebuf, pos);
else
len = pos;
log_debug ("Writing crypto data: %.*s",
(int)pos, linebuf);
if (len)
m_crypto_data.write(linebuf, len);
if (!m_mime_ctx->is_base64_encoded && !slbrk)
m_crypto_data.write("\r\n", 2);
}
/* Continue with next line. */
pos = 0;
}
}
TRETURN not_taken;
}
#ifdef HAVE_W32_SYSTEM
void
MimeDataProvider::collect_data(LPSTREAM stream)
{
TSTART;
if (!stream)
{
TRETURN;
}
HRESULT hr;
char buf[BUFSIZE];
ULONG bRead;
bool first_read = true;
bool is_pgp_message = false;
size_t allRead = 0;
while ((hr = stream->Read (buf, BUFSIZE, &bRead)) == S_OK ||
hr == S_FALSE)
{
if (!bRead)
{
log_data ("%s:%s: Input stream at EOF.",
SRCNAME, __func__);
break;
}
log_data ("%s:%s: Read %lu bytes.",
SRCNAME, __func__, bRead);
allRead += bRead;
if (first_read)
{
if (bRead > 12 && strncmp ("MIME-Version", buf, 12) == 0)
{
/* Fun! In case we have exchange or sent messages created by us
we get the mail attachment like it is before the MAPI to MIME
conversion. So it has our MIME structure. In that case
we have to expect MIME data even if the initial data check
suggests that we don't.
Checking if the content starts with MIME-Version appears
to be a robust way to check if we try to parse MIME data. */
m_collect_everything = false;
log_debug ("%s:%s: Found MIME-Version marker."
"Expecting headers even if type suggested not to.",
SRCNAME, __func__);
}
else if (bRead > 12 && !strncmp ("Content-Type:", buf, 13))
{
/* Similar as above but we messed with the order of the headers
for some s/mime mails. So also check for content type.
Want some cheese with that hack?
*/
m_collect_everything = false;
log_debug ("%s:%s: Found Content-Type header."
"Expecting headers even if type suggested not to.",
SRCNAME, __func__);
}
/* check for the PGP MESSAGE marker to see if we have it. */
if (bRead && m_collect_everything)
{
std::string tmp (buf, bRead);
std::size_t found = tmp.find ("-----BEGIN PGP MESSAGE-----");
if (found != std::string::npos)
{
log_debug ("%s:%s: found PGP Message marker,",
SRCNAME, __func__);
is_pgp_message = true;
}
}
}
first_read = false;
if (m_collect_everything)
{
/* For S/MIME, Clearsigned, PGP MESSAGES we just pass everything
on. Only the Multipart classes need parsing. And the output
of course. */
log_data ("%s:%s: Just copying data.",
SRCNAME, __func__);
m_crypto_data.write ((void*)buf, (size_t) bRead);
continue;
}
m_rawbuf += std::string (buf, bRead);
size_t not_taken = collect_input_lines (m_rawbuf.c_str(),
m_rawbuf.size());
if (not_taken == m_rawbuf.size())
{
log_error ("%s:%s: Collect failed to consume anything.\n"
"Buffer too small?",
SRCNAME, __func__);
break;
}
log_data ("%s:%s: Consumed: " SIZE_T_FORMAT " bytes",
SRCNAME, __func__, m_rawbuf.size() - not_taken);
m_rawbuf.erase (0, m_rawbuf.size() - not_taken);
}
if (is_pgp_message && allRead < (1024 * 100))
{
/* Sometimes received PGP Messsages contain extra whitespace /
newlines. To also accept such messages we fix up pgp inline
messages here. We only do this for messages which are smaller
then a hundred KByte for performance. */
log_debug ("%s:%s: Fixing up a possible broken message.",
SRCNAME, __func__);
/* Copy crypto data to string */
std::string data = m_crypto_data.toString();
m_crypto_data = GpgME::Data();
std::istringstream iss (data);
// Now parse it by line.
std::string line;
while (std::getline (iss, line))
{
trim (line);
if (line == "-----BEGIN PGP MESSAGE-----")
{
/* Finish an armor header */
line += "\n\n";
m_crypto_data.write (line.c_str (), line.size ());
continue;
}
/* Remove empty lines */
if (line.empty())
{
continue;
}
if (line.find (':') != std::string::npos)
{
log_data ("%s:%s: Removing comment '%s'.",
SRCNAME, __func__, line.c_str ());
continue;
}
line += '\n';
m_crypto_data.write (line.c_str (), line.size ());
}
}
TRETURN;
}
#endif
void
MimeDataProvider::collect_data(FILE *stream)
{
TSTART;
if (!stream)
{
TRETURN;
}
char buf[BUFSIZE];
size_t bRead;
while ((bRead = fread (buf, 1, BUFSIZE, stream)) > 0)
{
log_data ("%s:%s: Read " SIZE_T_FORMAT " bytes.",
SRCNAME, __func__, bRead);
if (m_collect_everything)
{
/* For S/MIME, Clearsigned, PGP MESSAGES we just pass everything
on. Only the Multipart classes need parsing. And the output
of course. */
log_data ("%s:%s: Making verbatim copy" SIZE_T_FORMAT " bytes.",
SRCNAME, __func__, bRead);
m_crypto_data.write ((void*)buf, bRead);
continue;
}
m_rawbuf += std::string (buf, bRead);
size_t not_taken = collect_input_lines (m_rawbuf.c_str(),
m_rawbuf.size());
if (not_taken == m_rawbuf.size())
{
log_error ("%s:%s: Collect failed to consume anything.\n"
"Buffer too small?",
SRCNAME, __func__);
TRETURN;
}
log_data ("%s:%s: Consumed: " SIZE_T_FORMAT " bytes",
SRCNAME, __func__, m_rawbuf.size() - not_taken);
m_rawbuf.erase (0, m_rawbuf.size() - not_taken);
}
TRETURN;
}
ssize_t MimeDataProvider::write(const void *buffer, size_t bufSize)
{
TSTART;
if (m_collect_everything)
{
/* Writing with collect everything one means that we are outputprovider.
In this case for inline messages we want to collect everything. */
log_data ("%s:%s: Using complete input as body " SIZE_T_FORMAT " bytes.",
SRCNAME, __func__, bufSize);
m_body += std::string ((const char *) buffer, bufSize);
TRETURN bufSize;
}
m_rawbuf += std::string ((const char*)buffer, bufSize);
size_t not_taken = collect_input_lines (m_rawbuf.c_str(),
m_rawbuf.size());
if (not_taken == m_rawbuf.size())
{
log_error ("%s:%s: Write failed to consume anything.\n"
"Buffer too small? or no newlines in text?",
SRCNAME, __func__);
TRETURN bufSize;
}
log_data ("%s:%s: Write Consumed: " SIZE_T_FORMAT " bytes",
SRCNAME, __func__, m_rawbuf.size() - not_taken);
m_rawbuf.erase (0, m_rawbuf.size() - not_taken);
TRETURN bufSize;
}
off_t
MimeDataProvider::seek(off_t offset, int whence)
{
- TSTART;
- TRETURN m_crypto_data.seek (offset, whence);
+ return m_crypto_data.seek (offset, whence);
}
GpgME::Data *
MimeDataProvider::signature() const
{
TSTART;
TRETURN m_signature;
}
std::shared_ptr
MimeDataProvider::create_attachment()
{
TSTART;
log_data ("%s:%s: Creating attachment.",
SRCNAME, __func__);
auto attach = std::shared_ptr (new Attachment());
attach->set_attach_type (ATTACHTYPE_FROMMOSS);
m_mime_ctx->any_attachments_created = 1;
/* And now for the real name. We avoid storing the name "smime.p7m"
because that one is used at several places in the mapi conversion
functions. */
if (m_mime_ctx->mimestruct_cur && m_mime_ctx->mimestruct_cur->filename)
{
if (!strcmp (m_mime_ctx->mimestruct_cur->filename, "smime.p7m"))
{
attach->set_display_name ("x-smime.p7m");
}
else
{
log_data ("%s:%s: Attachment filename: %s",
SRCNAME, __func__, m_mime_ctx->mimestruct_cur->filename);
attach->set_display_name (m_mime_ctx->mimestruct_cur->filename);
}
}
if (m_mime_ctx->mimestruct_cur && m_mime_ctx->mimestruct_cur->cid)
{
attach->set_content_id (m_mime_ctx->mimestruct_cur->cid);
}
m_attachments.push_back (attach);
TRETURN attach;
/* TODO handle encoding */
}
void MimeDataProvider::finalize ()
{
TSTART;
if (m_rawbuf.size ())
{
m_rawbuf += "\r\n";
size_t not_taken = collect_input_lines (m_rawbuf.c_str(),
m_rawbuf.size());
m_rawbuf.erase (0, m_rawbuf.size() - not_taken);
if (m_rawbuf.size ())
{
log_error ("%s:%s: Collect left data in buffer.\n",
SRCNAME, __func__);
}
}
TRETURN;
}
const std::string &MimeDataProvider::get_body ()
{
TSTART;
TRETURN m_body;
}
const std::string &MimeDataProvider::get_html_body ()
{
TSTART;
TRETURN m_html_body;
}
const std::string &MimeDataProvider::get_html_charset() const
{
TSTART;
TRETURN m_html_charset;
}
const std::string &MimeDataProvider::get_body_charset() const
{
TSTART;
TRETURN m_body_charset;
}
diff --git a/src/oomhelp.cpp b/src/oomhelp.cpp
index 851b7ba..383960c 100644
--- a/src/oomhelp.cpp
+++ b/src/oomhelp.cpp
@@ -1,2664 +1,2660 @@
/* oomhelp.cpp - Helper functions for the Outlook Object Model
* Copyright (C) 2009 g10 Code GmbH
* Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
* Copyright (C) 2018 Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL 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.
*
* GpgOL 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, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include "common.h"
#include "oomhelp.h"
#include "gpgoladdin.h"
HRESULT
gpgol_queryInterface (LPUNKNOWN pObj, REFIID riid, LPVOID FAR *ppvObj)
{
- TSTART;
HRESULT ret = pObj->QueryInterface (riid, ppvObj);
if ((opt.enable_debug & DBG_OOM_EXTRA) && *ppvObj)
{
memdbg_addRef (*ppvObj);
}
- TRETURN ret;
+ return ret;
}
HRESULT
gpgol_openProperty (LPMAPIPROP obj, ULONG ulPropTag, LPCIID lpiid,
ULONG ulInterfaceOptions, ULONG ulFlags,
LPUNKNOWN FAR * lppUnk)
{
- TSTART;
HRESULT ret = obj->OpenProperty (ulPropTag, lpiid,
ulInterfaceOptions, ulFlags,
lppUnk);
if ((opt.enable_debug & DBG_MEMORY) && *lppUnk)
{
memdbg_addRef (*lppUnk);
log_debug ("%s:%s: OpenProperty on %p prop %lx result %p",
SRCNAME, __func__, obj, ulPropTag, *lppUnk);
}
- TRETURN ret;
+ return ret;
}
/* Return a malloced string with the utf-8 encoded name of the object
or NULL if not available. */
char *
get_object_name (LPUNKNOWN obj)
{
TSTART;
HRESULT hr;
LPDISPATCH disp = NULL;
LPTYPEINFO tinfo = NULL;
BSTR bstrname;
char *name = NULL;
if (!obj)
goto leave;
/* We can't use gpgol_queryInterface here to avoid recursion */
obj->QueryInterface (IID_IDispatch, (void **)&disp);
if (!disp)
goto leave;
disp->GetTypeInfo (0, 0, &tinfo);
if (!tinfo)
{
log_debug ("%s:%s: no typeinfo found for object\n",
SRCNAME, __func__);
goto leave;
}
bstrname = NULL;
hr = tinfo->GetDocumentation (MEMBERID_NIL, &bstrname, 0, 0, 0);
if (hr || !bstrname)
log_debug ("%s:%s: GetDocumentation failed: hr=%#lx\n",
SRCNAME, __func__, hr);
if (bstrname)
{
name = wchar_to_utf8 (bstrname);
SysFreeString (bstrname);
}
leave:
if (tinfo)
tinfo->Release ();
if (disp)
disp->Release ();
TRETURN name;
}
/* Lookup the dispid of object PDISP for member NAME. Returns
DISPID_UNKNOWN on error. */
DISPID
lookup_oom_dispid (LPDISPATCH pDisp, const char *name)
{
- TSTART;
HRESULT hr;
DISPID dispid;
wchar_t *wname;
if (!pDisp || !name)
{
TRETURN DISPID_UNKNOWN; /* Error: Invalid arg. */
}
wname = utf8_to_wchar (name);
if (!wname)
{
TRETURN DISPID_UNKNOWN;/* Error: Out of memory. */
}
hr = pDisp->GetIDsOfNames (IID_NULL, &wname, 1,
LOCALE_SYSTEM_DEFAULT, &dispid);
xfree (wname);
if (hr != S_OK || dispid == DISPID_UNKNOWN)
log_debug ("%s:%s: error looking up dispid(%s)=%d: hr=0x%x\n",
SRCNAME, __func__, name, (int)dispid, (unsigned int)hr);
if (hr != S_OK)
dispid = DISPID_UNKNOWN;
- TRETURN dispid;
+ return dispid;
}
static void
init_excepinfo (EXCEPINFO *err)
{
- TSTART;
if (!err)
{
TRETURN;
}
err->wCode = 0;
err->wReserved = 0;
err->bstrSource = nullptr;
err->bstrDescription = nullptr;
err->bstrHelpFile = nullptr;
err->dwHelpContext = 0;
err->pvReserved = nullptr;
err->pfnDeferredFillIn = nullptr;
err->scode = 0;
}
void
dump_excepinfo (EXCEPINFO err)
{
log_oom ("%s:%s: Exception: \n"
" wCode: 0x%x\n"
" wReserved: 0x%x\n"
" source: %S\n"
" desc: %S\n"
" help: %S\n"
" helpCtx: 0x%x\n"
" deferredFill: %p\n"
" scode: 0x%x\n",
SRCNAME, __func__, (unsigned int) err.wCode,
(unsigned int) err.wReserved,
err.bstrSource ? err.bstrSource : L"null",
err.bstrDescription ? err.bstrDescription : L"null",
err.bstrHelpFile ? err.bstrDescription : L"null",
(unsigned int) err.dwHelpContext,
err.pfnDeferredFillIn,
(unsigned int) err.scode);
}
/* Return the OOM object's IDispatch interface described by FULLNAME.
Returns NULL if not found. PSTART is the object where the search
starts. FULLNAME is a dot delimited sequence of object names. If
an object name has a "(foo)" suffix this passes it as a parameter
to the invoke function (i.e. using (DISPATCH|PROPERTYGET)). Object
names including the optional suffix are truncated at 127 byte. */
LPDISPATCH
get_oom_object (LPDISPATCH pStart, const char *fullname)
{
TSTART;
HRESULT hr;
LPDISPATCH pObj = pStart;
LPDISPATCH pDisp = NULL;
log_oom ("%s:%s: looking for %p->`%s'",
SRCNAME, __func__, pStart, fullname);
while (pObj)
{
DISPPARAMS dispparams;
VARIANT aVariant[4];
VARIANT vtResult;
wchar_t *wname;
char name[128];
int n_parms = 0;
BSTR parmstr = NULL;
INT parmint = 0;
DISPID dispid;
char *p, *pend;
int dispmethod;
unsigned int argErr = 0;
EXCEPINFO execpinfo;
init_excepinfo (&execpinfo);
if (pDisp)
{
gpgol_release (pDisp);
pDisp = NULL;
}
if (gpgol_queryInterface (pObj, IID_IDispatch, (LPVOID*)&pDisp) != S_OK)
{
log_error ("%s:%s Object does not support IDispatch",
SRCNAME, __func__);
gpgol_release (pObj);
TRETURN NULL;
}
/* Confirmed through testing that the retval needs a release */
if (pObj != pStart)
gpgol_release (pObj);
pObj = NULL;
if (!pDisp)
{
TRETURN NULL; /* The object has no IDispatch interface. */
}
if (!*fullname)
{
if ((opt.enable_debug & DBG_MEMORY))
{
pDisp->AddRef ();
int ref = pDisp->Release ();
log_oom ("%s:%s: got %p with %i refs",
SRCNAME, __func__, pDisp, ref);
}
TRETURN pDisp; /* Ready. */
}
/* Break out the next name part. */
{
const char *dot;
size_t n;
dot = strchr (fullname, '.');
if (dot == fullname)
{
gpgol_release (pDisp);
TRETURN NULL; /* Empty name part: error. */
}
else if (dot)
n = dot - fullname;
else
n = strlen (fullname);
if (n >= sizeof name)
n = sizeof name - 1;
strncpy (name, fullname, n);
name[n] = 0;
if (dot)
fullname = dot + 1;
else
fullname += strlen (fullname);
}
if (!strncmp (name, "get_", 4) && name[4])
{
dispmethod = DISPATCH_PROPERTYGET;
memmove (name, name+4, strlen (name+4)+1);
}
else if ((p = strchr (name, '(')))
{
*p++ = 0;
pend = strchr (p, ')');
if (pend)
*pend = 0;
if (*p == ',' && p[1] != ',')
{
/* We assume this is "foo(,30007)". I.e. the frst arg
is not given and the second one is an integer. */
parmint = (int)strtol (p+1, NULL, 10);
n_parms = 4;
}
else
{
wname = utf8_to_wchar (p);
if (wname)
{
parmstr = SysAllocString (wname);
xfree (wname);
}
if (!parmstr)
{
gpgol_release (pDisp);
TRETURN NULL; /* Error: Out of memory. */
}
n_parms = 1;
}
dispmethod = DISPATCH_METHOD|DISPATCH_PROPERTYGET;
}
else
dispmethod = DISPATCH_METHOD;
/* Lookup the dispid. */
dispid = lookup_oom_dispid (pDisp, name);
if (dispid == DISPID_UNKNOWN)
{
if (parmstr)
SysFreeString (parmstr);
gpgol_release (pDisp);
TRETURN NULL; /* Name not found. */
}
/* Invoke the method. */
dispparams.rgvarg = aVariant;
dispparams.cArgs = 0;
if (n_parms)
{
if (n_parms == 4)
{
dispparams.rgvarg[0].vt = VT_ERROR;
dispparams.rgvarg[0].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[1].vt = VT_ERROR;
dispparams.rgvarg[1].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[2].vt = VT_INT;
dispparams.rgvarg[2].intVal = parmint;
dispparams.rgvarg[3].vt = VT_ERROR;
dispparams.rgvarg[3].scode = DISP_E_PARAMNOTFOUND;
dispparams.cArgs = n_parms;
}
else if (n_parms == 1 && parmstr)
{
dispparams.rgvarg[0].vt = VT_BSTR;
dispparams.rgvarg[0].bstrVal = parmstr;
dispparams.cArgs++;
}
}
dispparams.cNamedArgs = 0;
VariantInit (&vtResult);
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
dispmethod, &dispparams,
&vtResult, &execpinfo, &argErr);
if (parmstr)
SysFreeString (parmstr);
if (hr != S_OK || vtResult.vt != VT_DISPATCH)
{
log_debug ("%s:%s: failure: '%s' p=%p vt=%d hr=0x%x argErr=0x%x dispid=0x%x",
SRCNAME, __func__,
name, vtResult.pdispVal, vtResult.vt, (unsigned int)hr,
(unsigned int)argErr, (unsigned int)dispid);
dump_excepinfo (execpinfo);
VariantClear (&vtResult);
gpgol_release (pDisp);
TRETURN NULL; /* Invoke failed. */
}
pObj = vtResult.pdispVal;
memdbg_addRef (pObj);
}
gpgol_release (pDisp);
log_debug ("%s:%s: no object", SRCNAME, __func__);
TRETURN NULL;
}
/* Helper for put_oom_icon. */
static int
put_picture_or_mask (LPDISPATCH pDisp, int resource, int size, int is_mask)
{
TSTART;
HRESULT hr;
PICTDESC pdesc;
LPDISPATCH pPict;
DISPID dispid_put = DISPID_PROPERTYPUT;
UINT fuload;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[2];
/* When loading the mask we need to set the monochrome flag. We
better create a DIB section to avoid possible rendering
problems. */
fuload = LR_CREATEDIBSECTION | LR_SHARED;
if (is_mask)
fuload |= LR_MONOCHROME;
memset (&pdesc, 0, sizeof pdesc);
pdesc.cbSizeofstruct = sizeof pdesc;
pdesc.picType = PICTYPE_BITMAP;
pdesc.bmp.hbitmap = (HBITMAP) LoadImage (glob_hinst,
MAKEINTRESOURCE (resource),
IMAGE_BITMAP, size, size, fuload);
if (!pdesc.bmp.hbitmap)
{
log_error_w32 (-1, "%s:%s: LoadImage(%d) failed\n",
SRCNAME, __func__, resource);
TRETURN -1;
}
/* Wrap the image into an OLE object. */
hr = OleCreatePictureIndirect (&pdesc, IID_IPictureDisp,
TRUE, (void **) &pPict);
if (hr != S_OK || !pPict)
{
log_error ("%s:%s: OleCreatePictureIndirect failed: hr=%#lx\n",
SRCNAME, __func__, hr);
TRETURN -1;
}
/* Store to the Picture or Mask property of the CommandBarButton. */
dispid = lookup_oom_dispid (pDisp, is_mask? "Mask":"Picture");
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_DISPATCH;
dispparams.rgvarg[0].pdispVal = pPict;
dispparams.cArgs = 1;
dispparams.rgdispidNamedArgs = &dispid_put;
dispparams.cNamedArgs = 1;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dispparams,
NULL, NULL, NULL);
if (hr != S_OK)
{
log_debug ("%s:%s: Putting icon failed: %#lx", SRCNAME, __func__, hr);
TRETURN -1;
}
TRETURN 0;
}
/* Update the icon of PDISP using the bitmap with RESOURCE ID. The
function adds the system pixel size to the resource id to compute
the actual icon size. The resource id of the mask is the N+1. */
int
put_oom_icon (LPDISPATCH pDisp, int resource_id, int size)
{
TSTART;
int rc;
/* This code is only relevant for Outlook < 2010.
Ideally it should grab the system pixel size and use an
icon of the appropiate size (e.g. 32 or 64px)
*/
rc = put_picture_or_mask (pDisp, resource_id, size, 0);
if (!rc)
rc = put_picture_or_mask (pDisp, resource_id + 1, size, 1);
TRETURN rc;
}
/* Set the boolean property NAME to VALUE. */
int
put_oom_bool (LPDISPATCH pDisp, const char *name, int value)
{
TSTART;
HRESULT hr;
DISPID dispid_put = DISPID_PROPERTYPUT;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[1];
dispid = lookup_oom_dispid (pDisp, name);
if (dispid == DISPID_UNKNOWN)
{
TRETURN -1;
}
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_BOOL;
dispparams.rgvarg[0].boolVal = value? VARIANT_TRUE:VARIANT_FALSE;
dispparams.cArgs = 1;
dispparams.rgdispidNamedArgs = &dispid_put;
dispparams.cNamedArgs = 1;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dispparams,
NULL, NULL, NULL);
if (hr != S_OK)
{
log_debug ("%s:%s: Putting '%s' failed: %#lx",
SRCNAME, __func__, name, hr);
TRETURN -1;
}
TRETURN 0;
}
/* Set the property NAME to VALUE. */
int
put_oom_int (LPDISPATCH pDisp, const char *name, int value)
{
TSTART;
HRESULT hr;
DISPID dispid_put = DISPID_PROPERTYPUT;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[1];
dispid = lookup_oom_dispid (pDisp, name);
if (dispid == DISPID_UNKNOWN)
{
TRETURN -1;
}
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_INT;
dispparams.rgvarg[0].intVal = value;
dispparams.cArgs = 1;
dispparams.rgdispidNamedArgs = &dispid_put;
dispparams.cNamedArgs = 1;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dispparams,
NULL, NULL, NULL);
if (hr != S_OK)
{
log_debug ("%s:%s: Putting '%s' failed: %#lx",
SRCNAME, __func__, name, hr);
TRETURN -1;
}
TRETURN 0;
}
/* Set the property NAME to STRING. */
int
put_oom_string (LPDISPATCH pDisp, const char *name, const char *string)
{
TSTART;
HRESULT hr;
DISPID dispid_put = DISPID_PROPERTYPUT;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[1];
BSTR bstring;
EXCEPINFO execpinfo;
init_excepinfo (&execpinfo);
dispid = lookup_oom_dispid (pDisp, name);
if (dispid == DISPID_UNKNOWN)
{
TRETURN -1;
}
{
wchar_t *tmp = utf8_to_wchar (string);
bstring = tmp? SysAllocString (tmp):NULL;
xfree (tmp);
if (!bstring)
{
log_error_w32 (-1, "%s:%s: SysAllocString failed", SRCNAME, __func__);
TRETURN -1;
}
}
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_BSTR;
dispparams.rgvarg[0].bstrVal = bstring;
dispparams.cArgs = 1;
dispparams.rgdispidNamedArgs = &dispid_put;
dispparams.cNamedArgs = 1;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dispparams,
NULL, &execpinfo, NULL);
SysFreeString (bstring);
if (hr != S_OK)
{
log_debug ("%s:%s: Putting '%s' failed: %#lx",
SRCNAME, __func__, name, hr);
dump_excepinfo (execpinfo);
TRETURN -1;
}
TRETURN 0;
}
/* Set the property NAME to DISP. */
int
put_oom_disp (LPDISPATCH pDisp, const char *name, LPDISPATCH disp)
{
TSTART;
HRESULT hr;
DISPID dispid_put = DISPID_PROPERTYPUT;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[1];
EXCEPINFO execpinfo;
init_excepinfo (&execpinfo);
dispid = lookup_oom_dispid (pDisp, name);
if (dispid == DISPID_UNKNOWN)
{
TRETURN -1;
}
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_DISPATCH;
dispparams.rgvarg[0].pdispVal = disp;
dispparams.cArgs = 1;
dispparams.rgdispidNamedArgs = &dispid_put;
dispparams.cNamedArgs = 1;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUTREF, &dispparams,
NULL, &execpinfo, NULL);
if (hr != S_OK)
{
log_debug ("%s:%s: Putting '%s' failed: %#lx",
SRCNAME, __func__, name, hr);
dump_excepinfo (execpinfo);
TRETURN -1;
}
TRETURN 0;
}
/* Get the boolean property NAME of the object PDISP. Returns False if
not found or if it is not a boolean property. */
int
get_oom_bool (LPDISPATCH pDisp, const char *name)
{
TSTART;
HRESULT hr;
int result = 0;
DISPID dispid;
dispid = lookup_oom_dispid (pDisp, name);
if (dispid != DISPID_UNKNOWN)
{
DISPPARAMS dispparams = {NULL, NULL, 0, 0};
VARIANT rVariant;
VariantInit (&rVariant);
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dispparams,
&rVariant, NULL, NULL);
if (hr != S_OK)
log_debug ("%s:%s: Property '%s' not found: %#lx",
SRCNAME, __func__, name, hr);
else if (rVariant.vt != VT_BOOL)
log_debug ("%s:%s: Property `%s' is not a boolean (vt=%d)",
SRCNAME, __func__, name, rVariant.vt);
else
result = !!rVariant.boolVal;
VariantClear (&rVariant);
}
TRETURN result;
}
/* Get the integer property NAME of the object PDISP. Returns 0 if
not found or if it is not an integer property. */
int
get_oom_int (LPDISPATCH pDisp, const char *name)
{
TSTART;
HRESULT hr;
int result = 0;
DISPID dispid;
dispid = lookup_oom_dispid (pDisp, name);
if (dispid != DISPID_UNKNOWN)
{
DISPPARAMS dispparams = {NULL, NULL, 0, 0};
VARIANT rVariant;
VariantInit (&rVariant);
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dispparams,
&rVariant, NULL, NULL);
if (hr != S_OK)
log_debug ("%s:%s: Property '%s' not found: %#lx",
SRCNAME, __func__, name, hr);
else if (rVariant.vt != VT_INT && rVariant.vt != VT_I4)
log_debug ("%s:%s: Property `%s' is not an integer (vt=%d)",
SRCNAME, __func__, name, rVariant.vt);
else
result = rVariant.intVal;
VariantClear (&rVariant);
}
TRETURN result;
}
/* Get the string property NAME of the object PDISP. Returns NULL if
not found or if it is not a string property. */
char *
get_oom_string (LPDISPATCH pDisp, const char *name)
{
TSTART;
HRESULT hr;
char *result = NULL;
DISPID dispid;
dispid = lookup_oom_dispid (pDisp, name);
if (dispid != DISPID_UNKNOWN)
{
DISPPARAMS dispparams = {NULL, NULL, 0, 0};
VARIANT rVariant;
VariantInit (&rVariant);
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dispparams,
&rVariant, NULL, NULL);
if (hr != S_OK)
log_debug ("%s:%s: Property '%s' not found: %#lx",
SRCNAME, __func__, name, hr);
else if (rVariant.vt != VT_BSTR)
log_debug ("%s:%s: Property `%s' is not a string (vt=%d)",
SRCNAME, __func__, name, rVariant.vt);
else if (rVariant.bstrVal)
result = wchar_to_utf8 (rVariant.bstrVal);
VariantClear (&rVariant);
}
TRETURN result;
}
/* Get the object property NAME of the object PDISP. Returns NULL if
not found or if it is not an object perty. */
LPUNKNOWN
get_oom_iunknown (LPDISPATCH pDisp, const char *name)
{
TSTART;
HRESULT hr;
DISPID dispid;
dispid = lookup_oom_dispid (pDisp, name);
if (dispid != DISPID_UNKNOWN)
{
DISPPARAMS dispparams = {NULL, NULL, 0, 0};
VARIANT rVariant;
VariantInit (&rVariant);
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dispparams,
&rVariant, NULL, NULL);
if (hr != S_OK)
log_debug ("%s:%s: Property '%s' not found: %#lx",
SRCNAME, __func__, name, hr);
else if (rVariant.vt != VT_UNKNOWN)
log_debug ("%s:%s: Property `%s' is not of class IUnknown (vt=%d)",
SRCNAME, __func__, name, rVariant.vt);
else
{
memdbg_addRef (rVariant.punkVal);
TRETURN rVariant.punkVal;
}
VariantClear (&rVariant);
}
TRETURN NULL;
}
/* Return the control object described by the tag property with value
TAG. The object POBJ must support the FindControl method. Returns
NULL if not found. */
LPDISPATCH
get_oom_control_bytag (LPDISPATCH pDisp, const char *tag)
{
TSTART;
HRESULT hr;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[4];
VARIANT rVariant;
BSTR bstring;
LPDISPATCH result = NULL;
dispid = lookup_oom_dispid (pDisp, "FindControl");
if (dispid == DISPID_UNKNOWN)
{
log_debug ("%s:%s: Object %p has no FindControl method",
SRCNAME, __func__, pDisp);
TRETURN NULL;
}
{
wchar_t *tmp = utf8_to_wchar (tag);
bstring = tmp? SysAllocString (tmp):NULL;
xfree (tmp);
if (!bstring)
{
log_error_w32 (-1, "%s:%s: SysAllocString failed", SRCNAME, __func__);
TRETURN NULL;
}
}
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_ERROR; /* Visible */
dispparams.rgvarg[0].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[1].vt = VT_BSTR; /* Tag */
dispparams.rgvarg[1].bstrVal = bstring;
dispparams.rgvarg[2].vt = VT_ERROR; /* Id */
dispparams.rgvarg[2].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[3].vt = VT_ERROR;/* Type */
dispparams.rgvarg[3].scode = DISP_E_PARAMNOTFOUND;
dispparams.cArgs = 4;
dispparams.cNamedArgs = 0;
VariantInit (&rVariant);
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
&rVariant, NULL, NULL);
SysFreeString (bstring);
if (hr == S_OK && rVariant.vt == VT_DISPATCH && rVariant.pdispVal)
{
gpgol_queryInterface (rVariant.pdispVal, IID_IDispatch,
(LPVOID*)&result);
gpgol_release (rVariant.pdispVal);
if (!result)
log_debug ("%s:%s: Object with tag `%s' has no dispatch intf.",
SRCNAME, __func__, tag);
}
else
{
log_debug ("%s:%s: No object with tag `%s' found: vt=%d hr=%#lx",
SRCNAME, __func__, tag, rVariant.vt, hr);
VariantClear (&rVariant);
}
TRETURN result;
}
/* Add a new button to an object which supports the add method.
Returns the new object or NULL on error. */
LPDISPATCH
add_oom_button (LPDISPATCH pObj)
{
TSTART;
HRESULT hr;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[5];
VARIANT rVariant;
dispid = lookup_oom_dispid (pObj, "Add");
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_BOOL; /* Temporary */
dispparams.rgvarg[0].boolVal = VARIANT_TRUE;
dispparams.rgvarg[1].vt = VT_ERROR; /* Before */
dispparams.rgvarg[1].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[2].vt = VT_ERROR; /* Parameter */
dispparams.rgvarg[2].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[3].vt = VT_ERROR; /* Id */
dispparams.rgvarg[3].scode = DISP_E_PARAMNOTFOUND;
dispparams.rgvarg[4].vt = VT_INT; /* Type */
dispparams.rgvarg[4].intVal = MSOCONTROLBUTTON;
dispparams.cArgs = 5;
dispparams.cNamedArgs = 0;
VariantInit (&rVariant);
hr = pObj->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
&rVariant, NULL, NULL);
if (hr != S_OK || rVariant.vt != VT_DISPATCH || !rVariant.pdispVal)
{
log_error ("%s:%s: Adding Control failed: %#lx - vt=%d",
SRCNAME, __func__, hr, rVariant.vt);
VariantClear (&rVariant);
TRETURN NULL;
}
TRETURN rVariant.pdispVal;
}
/* Add a new button to an object which supports the add method.
Returns the new object or NULL on error. */
void
del_oom_button (LPDISPATCH pObj)
{
TSTART;
HRESULT hr;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant[5];
dispid = lookup_oom_dispid (pObj, "Delete");
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_BOOL; /* Temporary */
dispparams.rgvarg[0].boolVal = VARIANT_FALSE;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 0;
hr = pObj->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
NULL, NULL, NULL);
if (hr != S_OK)
log_error ("%s:%s: Deleting Control failed: %#lx",
SRCNAME, __func__, hr);
TRETURN;
}
/* Gets the current contexts HWND. Returns NULL on error */
HWND
get_oom_context_window (LPDISPATCH context)
{
TSTART;
LPOLEWINDOW actExplorer;
HWND ret = NULL;
actExplorer = (LPOLEWINDOW) get_oom_object(context,
"Application.ActiveExplorer");
if (actExplorer)
actExplorer->GetWindow (&ret);
else
{
log_debug ("%s:%s: Could not find active window",
SRCNAME, __func__);
}
gpgol_release (actExplorer);
TRETURN ret;
}
int
put_pa_variant (LPDISPATCH pDisp, const char *dasl_id, VARIANT *value)
{
TSTART;
LPDISPATCH propertyAccessor;
VARIANT cVariant[2];
VARIANT rVariant;
DISPID dispid;
DISPPARAMS dispparams;
HRESULT hr;
EXCEPINFO execpinfo;
BSTR b_property;
wchar_t *w_property;
unsigned int argErr = 0;
init_excepinfo (&execpinfo);
log_oom ("%s:%s: Looking up property: %s;",
SRCNAME, __func__, dasl_id);
propertyAccessor = get_oom_object (pDisp, "PropertyAccessor");
if (!propertyAccessor)
{
log_error ("%s:%s: Failed to look up property accessor.",
SRCNAME, __func__);
TRETURN -1;
}
dispid = lookup_oom_dispid (propertyAccessor, "SetProperty");
if (dispid == DISPID_UNKNOWN)
{
log_error ("%s:%s: could not find SetProperty DISPID",
SRCNAME, __func__);
TRETURN -1;
}
/* Prepare the parameter */
w_property = utf8_to_wchar (dasl_id);
b_property = SysAllocString (w_property);
xfree (w_property);
/* Variant 0 carries the data. */
VariantInit (&cVariant[0]);
if (VariantCopy (&cVariant[0], value))
{
log_error ("%s:%s: Falied to copy value.",
SRCNAME, __func__);
TRETURN -1;
}
/* Variant 1 is the DASL as found out by experiments. */
VariantInit (&cVariant[1]);
cVariant[1].vt = VT_BSTR;
cVariant[1].bstrVal = b_property;
dispparams.rgvarg = cVariant;
dispparams.cArgs = 2;
dispparams.cNamedArgs = 0;
VariantInit (&rVariant);
hr = propertyAccessor->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
&rVariant, &execpinfo, &argErr);
VariantClear (&cVariant[0]);
VariantClear (&cVariant[1]);
gpgol_release (propertyAccessor);
if (hr != S_OK)
{
log_debug ("%s:%s: failure: invoking SetProperty p=%p vt=%d"
" hr=0x%x argErr=0x%x",
SRCNAME, __func__,
rVariant.pdispVal, rVariant.vt, (unsigned int)hr,
(unsigned int)argErr);
VariantClear (&rVariant);
dump_excepinfo (execpinfo);
TRETURN -1;
}
VariantClear (&rVariant);
TRETURN 0;
}
int
put_pa_string (LPDISPATCH pDisp, const char *dasl_id, const char *value)
{
TSTART;
wchar_t *w_value = utf8_to_wchar (value);
BSTR b_value = SysAllocString(w_value);
xfree (w_value);
VARIANT var;
VariantInit (&var);
var.vt = VT_BSTR;
var.bstrVal = b_value;
int ret = put_pa_variant (pDisp, dasl_id, &var);
VariantClear (&var);
TRETURN ret;
}
int
put_pa_int (LPDISPATCH pDisp, const char *dasl_id, int value)
{
TSTART;
VARIANT var;
VariantInit (&var);
var.vt = VT_INT;
var.intVal = value;
int ret = put_pa_variant (pDisp, dasl_id, &var);
VariantClear (&var);
TRETURN ret;
}
/* Get a MAPI property through OOM using the PropertyAccessor
* interface and the DASL Uid. Returns -1 on error.
* Variant has to be cleared with VariantClear.
* rVariant must be a pointer to a Variant.
*/
int get_pa_variant (LPDISPATCH pDisp, const char *dasl_id, VARIANT *rVariant)
{
TSTART;
LPDISPATCH propertyAccessor;
VARIANT cVariant[1];
DISPID dispid;
DISPPARAMS dispparams;
HRESULT hr;
EXCEPINFO execpinfo;
BSTR b_property;
wchar_t *w_property;
unsigned int argErr = 0;
init_excepinfo (&execpinfo);
log_oom ("%s:%s: Looking up property: %s;",
SRCNAME, __func__, dasl_id);
propertyAccessor = get_oom_object (pDisp, "PropertyAccessor");
if (!propertyAccessor)
{
log_error ("%s:%s: Failed to look up property accessor.",
SRCNAME, __func__);
TRETURN -1;
}
dispid = lookup_oom_dispid (propertyAccessor, "GetProperty");
if (dispid == DISPID_UNKNOWN)
{
log_error ("%s:%s: could not find GetProperty DISPID",
SRCNAME, __func__);
TRETURN -1;
}
/* Prepare the parameter */
w_property = utf8_to_wchar (dasl_id);
b_property = SysAllocString (w_property);
xfree (w_property);
cVariant[0].vt = VT_BSTR;
cVariant[0].bstrVal = b_property;
dispparams.rgvarg = cVariant;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 0;
VariantInit (rVariant);
hr = propertyAccessor->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
rVariant, &execpinfo, &argErr);
SysFreeString (b_property);
gpgol_release (propertyAccessor);
if (hr != S_OK && strcmp (GPGOL_UID_DASL, dasl_id))
{
/* It often happens that mails don't have a uid by us e.g. if
they are not crypto mails or just dont have one. This is
not an error. */
log_debug ("%s:%s: error: invoking GetProperty p=%p vt=%d"
" hr=0x%x argErr=0x%x",
SRCNAME, __func__,
rVariant->pdispVal, rVariant->vt, (unsigned int)hr,
(unsigned int)argErr);
dump_excepinfo (execpinfo);
VariantClear (rVariant);
TRETURN -1;
}
TRETURN 0;
}
/* Get a property string by using the PropertyAccessor of pDisp
* Returns NULL on error or a newly allocated result. */
char *
get_pa_string (LPDISPATCH pDisp, const char *property)
{
TSTART;
VARIANT rVariant;
char *result = NULL;
if (get_pa_variant (pDisp, property, &rVariant))
{
TRETURN NULL;
}
if (rVariant.vt == VT_BSTR && rVariant.bstrVal)
{
result = wchar_to_utf8 (rVariant.bstrVal);
}
else if (rVariant.vt & VT_ARRAY && !(rVariant.vt & VT_BYREF))
{
LONG uBound, lBound;
VARTYPE vt;
char *data;
SafeArrayGetVartype(rVariant.parray, &vt);
if (SafeArrayGetUBound (rVariant.parray, 1, &uBound) != S_OK ||
SafeArrayGetLBound (rVariant.parray, 1, &lBound) != S_OK ||
vt != VT_UI1)
{
log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
VariantClear (&rVariant);
TRETURN NULL;
}
result = (char *)xmalloc (uBound - lBound + 1);
data = (char *) rVariant.parray->pvData;
memcpy (result, data + lBound, uBound - lBound);
result[uBound - lBound] = '\0';
}
else
{
log_debug ("%s:%s: Property `%s' is not a string (vt=%d)",
SRCNAME, __func__, property, rVariant.vt);
}
VariantClear (&rVariant);
TRETURN result;
}
int
get_pa_int (LPDISPATCH pDisp, const char *property, int *rInt)
{
TSTART;
VARIANT rVariant;
if (get_pa_variant (pDisp, property, &rVariant))
{
TRETURN -1;
}
if (rVariant.vt != VT_I4)
{
log_debug ("%s:%s: Property `%s' is not a int (vt=%d)",
SRCNAME, __func__, property, rVariant.vt);
TRETURN -1;
}
*rInt = rVariant.lVal;
VariantClear (&rVariant);
TRETURN 0;
}
/* Helper for exchange address lookup. */
static char *
get_recipient_addr_entry_fallbacks_ex (LPDISPATCH addr_entry)
{
TSTART;
/* Maybe check for type here? We are pretty sure that we are exchange */
/* According to MSDN Message Boards the PR_EMS_AB_PROXY_ADDRESSES_DASL
is more avilable then the SMTP Address. */
char *ret = get_pa_string (addr_entry, PR_EMS_AB_PROXY_ADDRESSES_DASL);
if (ret)
{
log_debug ("%s:%s: Found recipient through AB_PROXY: %s",
SRCNAME, __func__, anonstr (ret));
char *smtpbegin = strstr(ret, "SMTP:");
if (smtpbegin == ret)
{
ret += 5;
}
TRETURN ret;
}
else
{
log_debug ("%s:%s: Failed AB_PROXY lookup.",
SRCNAME, __func__);
}
LPDISPATCH ex_user = get_oom_object (addr_entry, "GetExchangeUser");
if (!ex_user)
{
log_debug ("%s:%s: Failed to find ExchangeUser",
SRCNAME, __func__);
TRETURN nullptr;
}
ret = get_oom_string (ex_user, "PrimarySmtpAddress");
gpgol_release (ex_user);
if (ret)
{
log_debug ("%s:%s: Found recipient through exchange user primary smtp address: %s",
SRCNAME, __func__, anonstr (ret));
TRETURN ret;
}
TRETURN nullptr;
}
/* Helper for additional fallbacks in recipient lookup */
static char *
get_recipient_addr_fallbacks (LPDISPATCH recipient)
{
TSTART;
if (!recipient)
{
TRETURN nullptr;
}
LPDISPATCH addr_entry = get_oom_object (recipient, "AddressEntry");
if (!addr_entry)
{
log_debug ("%s:%s: Failed to find AddressEntry",
SRCNAME, __func__);
TRETURN nullptr;
}
char *ret = get_recipient_addr_entry_fallbacks_ex (addr_entry);
gpgol_release (addr_entry);
TRETURN ret;
}
/* Try to resolve a recipient group and add it to the recipients vector.
Returns true on success.
*/
static bool
try_resolve_group (LPDISPATCH addrEntry,
std::vector > &ret)
{
TSTART;
/* Get the name for debugging */
std::string name;
char *cname = get_oom_string (addrEntry, "Name");
if (cname)
{
name = cname;
}
xfree (cname);
int type = get_oom_int (addrEntry, "AddressEntryUserType");
if (type != DISTRIBUTION_LIST_ADDRESS_ENTRY_TYPE)
{
log_data ("%s:%s: type of %s is %i",
SRCNAME, __func__, anonstr (name.c_str()), type);
TRETURN false;
}
LPDISPATCH members = get_oom_object (addrEntry, "Members");
addrEntry = nullptr;
if (!members)
{
TRACEPOINT;
TRETURN false;
}
int count = get_oom_int (members, "Count");
if (!count)
{
TRACEPOINT;
gpgol_release (members);
TRETURN false;
}
bool foundOne = false;
for (int i = 1; i <= count; i++)
{
auto item_str = std::string("Item(") + std::to_string (i) + ")";
auto entry = MAKE_SHARED (get_oom_object (members, item_str.c_str()));
if (!entry)
{
TRACEPOINT;
continue;
}
std::string entryName;
char *entry_name = get_oom_string (entry.get(), "Name");
if (entry_name)
{
entryName = entry_name;
xfree (entry_name);
}
int subType = get_oom_int (entry.get(), "AddressEntryUserType");
/* Resolve recursively, yeah fun. */
if (subType == DISTRIBUTION_LIST_ADDRESS_ENTRY_TYPE)
{
log_debug ("%s:%s: recursive address entry %s",
SRCNAME, __func__,
anonstr (entryName.c_str()));
if (try_resolve_group (entry.get(), ret))
{
foundOne = true;
continue;
}
}
std::pair element;
element.second = entry;
/* Resolve directly ? */
char *addrtype = get_pa_string (entry.get(), PR_ADDRTYPE_DASL);
if (addrtype && !strcmp (addrtype, "SMTP"))
{
xfree (addrtype);
char *resolved = get_pa_string (entry.get(), PR_EMAIL_ADDRESS_DASL);
if (resolved)
{
element.first = resolved;
ret.push_back (element);
foundOne = true;
continue;
}
}
xfree (addrtype);
/* Resolve through Exchange API */
char *ex_resolved = get_recipient_addr_entry_fallbacks_ex (entry.get());
if (ex_resolved)
{
element.first = ex_resolved;
ret.push_back (element);
foundOne = true;
continue;
}
log_debug ("%s:%s: failed to resolve name %s",
SRCNAME, __func__,
anonstr (entryName.c_str()));
}
gpgol_release (members);
if (!foundOne)
{
log_debug ("%s:%s: failed to resolve group %s",
SRCNAME, __func__,
anonstr (name.c_str()));
}
TRETURN foundOne;
}
/* Get the recipient mbox addresses with the addrEntry
object corresponding to the resolved address. */
std::vector >
get_oom_recipients_with_addrEntry (LPDISPATCH recipients, bool *r_err)
{
TSTART;
int recipientsCnt = get_oom_int (recipients, "Count");
std::vector > ret;
int i;
if (!recipientsCnt)
{
TRETURN ret;
}
/* Get the recipients */
for (i = 1; i <= recipientsCnt; i++)
{
char buf[16];
LPDISPATCH recipient;
snprintf (buf, sizeof (buf), "Item(%i)", i);
recipient = get_oom_object (recipients, buf);
if (!recipient)
{
/* Should be impossible */
log_error ("%s:%s: could not find Item %i;",
SRCNAME, __func__, i);
if (r_err)
{
*r_err = true;
}
break;
}
auto addrEntry = MAKE_SHARED (get_oom_object (recipient, "AddressEntry"));
if (addrEntry && try_resolve_group (addrEntry.get (), ret))
{
log_debug ("%s:%s: Resolved recipient group",
SRCNAME, __func__);
gpgol_release (recipient);
continue;
}
std::pair entry;
entry.second = addrEntry;
char *resolved = get_pa_string (recipient, PR_SMTP_ADDRESS_DASL);
if (resolved)
{
entry.first = resolved;
xfree (resolved);
gpgol_release (recipient);
ret.push_back (entry);
continue;
}
/* No PR_SMTP_ADDRESS first fallback */
resolved = get_recipient_addr_fallbacks (recipient);
if (resolved)
{
entry.first = resolved;
xfree (resolved);
gpgol_release (recipient);
ret.push_back (entry);
continue;
}
char *address = get_oom_string (recipient, "Address");
gpgol_release (recipient);
log_debug ("%s:%s: Failed to look up Address probably "
"EX addr is returned",
SRCNAME, __func__);
if (address)
{
entry.first = address;
ret.push_back (entry);
xfree (address);
}
else if (r_err)
{
*r_err = true;
}
}
TRETURN ret;
}
/* Gets the resolved smtp addresses of the recpients. */
std::vector
get_oom_recipients (LPDISPATCH recipients, bool *r_err)
{
TSTART;
std::vector ret;
for (const auto pair: get_oom_recipients_with_addrEntry (recipients, r_err))
{
ret.push_back (pair.first);
}
TRETURN ret;
}
/* Add an attachment to the outlook dispatcher disp
that has an Attachment property.
inFile is the path to the attachment. Name is the
name that should be used in outlook. */
int
add_oom_attachment (LPDISPATCH disp, const wchar_t* inFileW,
const wchar_t* displayName)
{
TSTART;
LPDISPATCH attachments = get_oom_object (disp, "Attachments");
DISPID dispid;
DISPPARAMS dispparams;
VARIANT vtResult;
VARIANT aVariant[4];
HRESULT hr;
BSTR inFileB = nullptr,
dispNameB = nullptr;
unsigned int argErr = 0;
EXCEPINFO execpinfo;
init_excepinfo (&execpinfo);
dispid = lookup_oom_dispid (attachments, "Add");
if (dispid == DISPID_UNKNOWN)
{
log_error ("%s:%s: could not find attachment dispatcher",
SRCNAME, __func__);
TRETURN -1;
}
if (inFileW)
{
inFileB = SysAllocString (inFileW);
}
if (displayName)
{
dispNameB = SysAllocString (displayName);
}
dispparams.rgvarg = aVariant;
/* Contrary to the documentation the Source is the last
parameter and not the first. Additionally DisplayName
is documented but gets ignored by Outlook since Outlook
2003 */
dispparams.rgvarg[0].vt = VT_BSTR; /* DisplayName */
dispparams.rgvarg[0].bstrVal = dispNameB;
dispparams.rgvarg[1].vt = VT_INT; /* Position */
dispparams.rgvarg[1].intVal = 1;
dispparams.rgvarg[2].vt = VT_INT; /* Type */
dispparams.rgvarg[2].intVal = 1;
dispparams.rgvarg[3].vt = VT_BSTR; /* Source */
dispparams.rgvarg[3].bstrVal = inFileB;
dispparams.cArgs = 4;
dispparams.cNamedArgs = 0;
VariantInit (&vtResult);
hr = attachments->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
&vtResult, &execpinfo, &argErr);
if (hr != S_OK)
{
log_debug ("%s:%s: error: invoking Add p=%p vt=%d hr=0x%x argErr=0x%x",
SRCNAME, __func__,
vtResult.pdispVal, vtResult.vt, (unsigned int)hr,
(unsigned int)argErr);
dump_excepinfo (execpinfo);
}
if (inFileB)
SysFreeString (inFileB);
if (dispNameB)
SysFreeString (dispNameB);
VariantClear (&vtResult);
gpgol_release (attachments);
TRETURN hr == S_OK ? 0 : -1;
}
LPDISPATCH
get_object_by_id (LPDISPATCH pDisp, REFIID id)
{
TSTART;
LPDISPATCH disp = NULL;
if (!pDisp)
{
TRETURN NULL;
}
if (gpgol_queryInterface(pDisp, id, (void **)&disp) != S_OK)
{
TRETURN NULL;
}
TRETURN disp;
}
LPDISPATCH
get_strong_reference (LPDISPATCH mail)
{
TSTART;
VARIANT var;
VariantInit (&var);
DISPPARAMS args;
VARIANT argvars[2];
VariantInit (&argvars[0]);
VariantInit (&argvars[1]);
argvars[1].vt = VT_DISPATCH;
argvars[1].pdispVal = mail;
argvars[0].vt = VT_INT;
argvars[0].intVal = 1;
args.cArgs = 2;
args.cNamedArgs = 0;
args.rgvarg = argvars;
LPDISPATCH ret = NULL;
if (!invoke_oom_method_with_parms (
GpgolAddin::get_instance()->get_application(),
"GetObjectReference", &var, &args))
{
ret = var.pdispVal;
log_oom ("%s:%s: Got strong ref %p for %p",
SRCNAME, __func__, ret, mail);
memdbg_addRef (ret);
}
else
{
log_error ("%s:%s: Failed to get strong ref.",
SRCNAME, __func__);
}
VariantClear (&var);
TRETURN ret;
}
LPMESSAGE
get_oom_message (LPDISPATCH mailitem)
{
TSTART;
LPUNKNOWN mapi_obj = get_oom_iunknown (mailitem, "MapiObject");
if (!mapi_obj)
{
log_error ("%s:%s: Failed to obtain MAPI Message.",
SRCNAME, __func__);
TRETURN NULL;
}
TRETURN (LPMESSAGE) mapi_obj;
}
static LPMESSAGE
get_oom_base_message_from_mapi (LPDISPATCH mapi_message)
{
TSTART;
HRESULT hr;
LPDISPATCH secureItem = NULL;
LPMESSAGE message = NULL;
LPMAPISECUREMESSAGE secureMessage = NULL;
secureItem = get_object_by_id (mapi_message,
IID_IMAPISecureMessage);
if (!secureItem)
{
log_error ("%s:%s: Failed to obtain SecureItem.",
SRCNAME, __func__);
TRETURN NULL;
}
secureMessage = (LPMAPISECUREMESSAGE) secureItem;
/* The call to GetBaseMessage is pretty much a jump
in the dark. So it would not be surprising to get
crashes here in the future. */
log_oom("%s:%s: About to call GetBaseMessage.",
SRCNAME, __func__);
hr = secureMessage->GetBaseMessage (&message);
memdbg_addRef (message);
gpgol_release (secureMessage);
if (hr != S_OK)
{
log_error_w32 (hr, "Failed to GetBaseMessage.");
TRETURN NULL;
}
TRETURN message;
}
LPMESSAGE
get_oom_base_message (LPDISPATCH mailitem)
{
TSTART;
LPMESSAGE mapi_message = get_oom_message (mailitem);
LPMESSAGE ret = NULL;
if (!mapi_message)
{
log_error ("%s:%s: Failed to obtain mapi_message.",
SRCNAME, __func__);
TRETURN NULL;
}
ret = get_oom_base_message_from_mapi ((LPDISPATCH)mapi_message);
gpgol_release (mapi_message);
TRETURN ret;
}
static int
invoke_oom_method_with_parms_type (LPDISPATCH pDisp, const char *name,
VARIANT *rVariant, DISPPARAMS *params,
int type)
{
TSTART;
HRESULT hr;
DISPID dispid;
dispid = lookup_oom_dispid (pDisp, name);
if (dispid != DISPID_UNKNOWN)
{
EXCEPINFO execpinfo;
init_excepinfo (&execpinfo);
DISPPARAMS dispparams = {NULL, NULL, 0, 0};
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
type, params ? params : &dispparams,
rVariant, &execpinfo, NULL);
if (hr != S_OK)
{
log_debug ("%s:%s: Method '%s' invokation failed: %#lx",
SRCNAME, __func__, name, hr);
dump_excepinfo (execpinfo);
TRETURN -1;
}
}
TRETURN 0;
}
int
invoke_oom_method_with_parms (LPDISPATCH pDisp, const char *name,
VARIANT *rVariant, DISPPARAMS *params)
{
TSTART;
TRETURN invoke_oom_method_with_parms_type (pDisp, name, rVariant, params,
DISPATCH_METHOD);
}
int
invoke_oom_method (LPDISPATCH pDisp, const char *name, VARIANT *rVariant)
{
TSTART;
TRETURN invoke_oom_method_with_parms (pDisp, name, rVariant, NULL);
}
LPMAPISESSION
get_oom_mapi_session ()
{
TSTART;
LPDISPATCH application = GpgolAddin::get_instance ()->get_application ();
LPDISPATCH oom_session = NULL;
LPMAPISESSION session = NULL;
LPUNKNOWN mapiobj = NULL;
HRESULT hr;
if (!application)
{
log_debug ("%s:%s: Not implemented for Ol < 14", SRCNAME, __func__);
TRETURN NULL;
}
oom_session = get_oom_object (application, "Session");
if (!oom_session)
{
log_error ("%s:%s: session object not found", SRCNAME, __func__);
TRETURN NULL;
}
mapiobj = get_oom_iunknown (oom_session, "MAPIOBJECT");
gpgol_release (oom_session);
if (!mapiobj)
{
log_error ("%s:%s: error getting Session.MAPIOBJECT", SRCNAME, __func__);
TRETURN NULL;
}
session = NULL;
hr = gpgol_queryInterface (mapiobj, IID_IMAPISession, (void**)&session);
gpgol_release (mapiobj);
if (hr != S_OK || !session)
{
log_error ("%s:%s: error getting IMAPISession: hr=%#lx",
SRCNAME, __func__, hr);
TRETURN NULL;
}
TRETURN session;
}
static int
create_category (LPDISPATCH categories, const char *category, int color)
{
TSTART;
VARIANT cVariant[3];
VARIANT rVariant;
DISPID dispid;
DISPPARAMS dispparams;
HRESULT hr;
EXCEPINFO execpinfo;
BSTR b_name;
wchar_t *w_name;
unsigned int argErr = 0;
init_excepinfo (&execpinfo);
if (!categories || !category)
{
TRACEPOINT;
TRETURN 1;
}
dispid = lookup_oom_dispid (categories, "Add");
if (dispid == DISPID_UNKNOWN)
{
log_error ("%s:%s: could not find Add DISPID",
SRCNAME, __func__);
TRETURN -1;
}
/* Do the string dance */
w_name = utf8_to_wchar (category);
b_name = SysAllocString (w_name);
xfree (w_name);
/* Variants are in reverse order
ShortcutKey -> 0 / Int
Color -> 1 / Int
Name -> 2 / Bstr */
VariantInit (&cVariant[2]);
cVariant[2].vt = VT_BSTR;
cVariant[2].bstrVal = b_name;
VariantInit (&cVariant[1]);
cVariant[1].vt = VT_INT;
cVariant[1].intVal = color;
VariantInit (&cVariant[0]);
cVariant[0].vt = VT_INT;
cVariant[0].intVal = 0;
dispparams.cArgs = 3;
dispparams.cNamedArgs = 0;
dispparams.rgvarg = cVariant;
hr = categories->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
&rVariant, &execpinfo, &argErr);
SysFreeString (b_name);
VariantClear (&cVariant[0]);
VariantClear (&cVariant[1]);
VariantClear (&cVariant[2]);
if (hr != S_OK)
{
log_debug ("%s:%s: error: invoking Add p=%p vt=%d"
" hr=0x%x argErr=0x%x",
SRCNAME, __func__,
rVariant.pdispVal, rVariant.vt, (unsigned int)hr,
(unsigned int)argErr);
dump_excepinfo (execpinfo);
VariantClear (&rVariant);
TRETURN -1;
}
VariantClear (&rVariant);
log_oom ("%s:%s: Created category '%s'",
SRCNAME, __func__, category);
TRETURN 0;
}
void
ensure_category_exists (LPDISPATCH application, const char *category, int color)
{
TSTART;
if (!application || !category)
{
TRACEPOINT;
TRETURN;
}
log_oom ("Ensure category exists called for %s, %i", category, color);
LPDISPATCH stores = get_oom_object (application, "Session.Stores");
if (!stores)
{
log_error ("%s:%s: No stores found.",
SRCNAME, __func__);
TRETURN;
}
auto store_count = get_oom_int (stores, "Count");
for (int n = 1; n <= store_count; n++)
{
const auto store_str = std::string("Item(") + std::to_string(n) + ")";
LPDISPATCH store = get_oom_object (stores, store_str.c_str());
if (!store)
{
TRACEPOINT;
continue;
}
LPDISPATCH categories = get_oom_object (store, "Categories");
gpgol_release (store);
if (!categories)
{
categories = get_oom_object (application, "Session.Categories");
if (!categories)
{
TRACEPOINT;
continue;
}
}
auto count = get_oom_int (categories, "Count");
bool found = false;
for (int i = 1; i <= count && !found; i++)
{
const auto item_str = std::string("Item(") + std::to_string(i) + ")";
LPDISPATCH category_obj = get_oom_object (categories, item_str.c_str());
if (!category_obj)
{
TRACEPOINT;
gpgol_release (categories);
break;
}
char *name = get_oom_string (category_obj, "Name");
if (name && !strcmp (category, name))
{
log_oom ("%s:%s: Found category '%s'",
SRCNAME, __func__, name);
found = true;
}
/* We don't check the color here as the user may change that. */
gpgol_release (category_obj);
xfree (name);
}
if (!found)
{
if (create_category (categories, category, color))
{
log_oom ("%s:%s: Found category '%s'",
SRCNAME, __func__, category);
}
}
/* Otherwise we have to create the category */
gpgol_release (categories);
}
gpgol_release (stores);
TRETURN;
}
int
add_category (LPDISPATCH mail, const char *category)
{
TSTART;
char *tmp = get_oom_string (mail, "Categories");
if (!tmp)
{
TRACEPOINT;
TRETURN 1;
}
if (strstr (tmp, category))
{
log_oom ("%s:%s: category '%s' already added.",
SRCNAME, __func__, category);
TRETURN 0;
}
std::string newstr (tmp);
xfree (tmp);
if (!newstr.empty ())
{
newstr += ", ";
}
newstr += category;
TRETURN put_oom_string (mail, "Categories", newstr.c_str ());
}
int
remove_category (LPDISPATCH mail, const char *category)
{
TSTART;
char *tmp = get_oom_string (mail, "Categories");
if (!tmp)
{
TRACEPOINT;
TRETURN 1;
}
std::string newstr (tmp);
xfree (tmp);
std::string cat (category);
size_t pos1 = newstr.find (cat);
size_t pos2 = newstr.find (std::string(", ") + cat);
if (pos1 == std::string::npos && pos2 == std::string::npos)
{
log_oom ("%s:%s: category '%s' not found.",
SRCNAME, __func__, category);
TRETURN 0;
}
size_t len = cat.size();
if (pos2)
{
len += 2;
}
newstr.erase (pos2 != std::string::npos ? pos2 : pos1, len);
log_oom ("%s:%s: removing category '%s'",
SRCNAME, __func__, category);
TRETURN put_oom_string (mail, "Categories", newstr.c_str ());
}
static char *
generate_uid ()
{
TSTART;
UUID uuid;
UuidCreate (&uuid);
unsigned char *str;
UuidToStringA (&uuid, &str);
char *ret = xstrdup ((char*)str);
RpcStringFreeA (&str);
TRETURN ret;
}
char *
get_unique_id (LPDISPATCH mail, int create, const char *uuid)
{
TSTART;
if (!mail)
{
TRETURN NULL;
}
/* Get the User Properties. */
if (!create)
{
char *uid = get_pa_string (mail, GPGOL_UID_DASL);
if (!uid)
{
log_debug ("%s:%s: No uuid found in oom for '%p'",
SRCNAME, __func__, mail);
TRETURN NULL;
}
else
{
log_debug ("%s:%s: Found uid '%s' for '%p'",
SRCNAME, __func__, uid, mail);
TRETURN uid;
}
}
char *newuid;
if (!uuid)
{
newuid = generate_uid ();
}
else
{
newuid = xstrdup (uuid);
}
int ret = put_pa_string (mail, GPGOL_UID_DASL, newuid);
if (ret)
{
log_debug ("%s:%s: failed to set uid '%s' for '%p'",
SRCNAME, __func__, newuid, mail);
xfree (newuid);
TRETURN NULL;
}
log_debug ("%s:%s: '%p' has now the uid: '%s' ",
SRCNAME, __func__, mail, newuid);
TRETURN newuid;
}
HWND
get_active_hwnd ()
{
TSTART;
LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
if (!app)
{
TRACEPOINT;
TRETURN nullptr;
}
LPDISPATCH activeWindow = get_oom_object (app, "ActiveWindow");
if (!activeWindow)
{
activeWindow = get_oom_object (app, "ActiveInspector");
if (!activeWindow)
{
activeWindow = get_oom_object (app, "ActiveExplorer");
if (!activeWindow)
{
TRACEPOINT;
TRETURN nullptr;
}
}
}
/* Both explorer and inspector have this. */
char *caption = get_oom_string (activeWindow, "Caption");
gpgol_release (activeWindow);
if (!caption)
{
TRACEPOINT;
TRETURN nullptr;
}
/* Might not be completly true for multiple explorers
on the same folder but good enugh. */
HWND hwnd = FindWindowExA(NULL, NULL, "rctrl_renwnd32",
caption);
xfree (caption);
TRETURN hwnd;
}
LPDISPATCH
create_mail ()
{
TSTART;
LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
if (!app)
{
TRACEPOINT;
TRETURN nullptr;
}
VARIANT var;
VariantInit (&var);
VARIANT argvars[1];
DISPPARAMS args;
VariantInit (&argvars[0]);
argvars[0].vt = VT_I2;
argvars[0].intVal = 0;
args.cArgs = 1;
args.cNamedArgs = 0;
args.rgvarg = argvars;
LPDISPATCH ret = nullptr;
if (invoke_oom_method_with_parms (app, "CreateItem", &var, &args))
{
log_error ("%s:%s: Failed to create mailitem.",
SRCNAME, __func__);
TRETURN ret;
}
ret = var.pdispVal;
TRETURN ret;
}
LPDISPATCH
get_account_for_mail (const char *mbox)
{
TSTART;
LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
if (!app)
{
TRACEPOINT;
TRETURN nullptr;
}
LPDISPATCH accounts = get_oom_object (app, "Session.Accounts");
if (!accounts)
{
TRACEPOINT;
TRETURN nullptr;
}
int count = get_oom_int (accounts, "Count");
for (int i = 1; i <= count; i++)
{
std::string item = std::string ("Item(") + std::to_string (i) + ")";
LPDISPATCH account = get_oom_object (accounts, item.c_str ());
if (!account)
{
TRACEPOINT;
continue;
}
char *smtpAddr = get_oom_string (account, "SmtpAddress");
if (!smtpAddr)
{
gpgol_release (account);
TRACEPOINT;
continue;
}
if (!stricmp (mbox, smtpAddr))
{
gpgol_release (accounts);
xfree (smtpAddr);
TRETURN account;
}
gpgol_release (account);
xfree (smtpAddr);
}
gpgol_release (accounts);
log_error ("%s:%s: Failed to find account for '%s'.",
SRCNAME, __func__, anonstr (mbox));
TRETURN nullptr;
}
char *
get_sender_SendUsingAccount (LPDISPATCH mailitem, bool *r_is_GSuite)
{
TSTART;
LPDISPATCH sender = get_oom_object (mailitem, "SendUsingAccount");
if (!sender)
{
TRETURN nullptr;
}
char *buf = get_oom_string (sender, "SmtpAddress");
char *dispName = get_oom_string (sender, "DisplayName");
gpgol_release (sender);
/* Check for G Suite account */
if (dispName && !strcmp ("G Suite", dispName) && r_is_GSuite)
{
*r_is_GSuite = true;
}
xfree (dispName);
if (buf && strlen (buf))
{
log_debug ("%s:%s: found sender", SRCNAME, __func__);
TRETURN buf;
}
xfree (buf);
TRETURN nullptr;
}
char *
get_sender_Sender (LPDISPATCH mailitem)
{
TSTART;
LPDISPATCH sender = get_oom_object (mailitem, "Sender");
if (!sender)
{
TRETURN nullptr;
}
char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
gpgol_release (sender);
if (buf && strlen (buf))
{
log_debug ("%s:%s Sender fallback 2", SRCNAME, __func__);
TRETURN buf;
}
xfree (buf);
/* We have a sender object but not yet an smtp address likely
exchange. Try some more propertys of the message. */
buf = get_pa_string (mailitem, PR_TAG_SENDER_SMTP_ADDRESS);
if (buf && strlen (buf))
{
log_debug ("%s:%s Sender fallback 3", SRCNAME, __func__);
TRETURN buf;
}
xfree (buf);
buf = get_pa_string (mailitem, PR_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS);
if (buf && strlen (buf))
{
log_debug ("%s:%s Sender fallback 4", SRCNAME, __func__);
TRETURN buf;
}
xfree (buf);
TRETURN nullptr;
}
char *
get_sender_CurrentUser (LPDISPATCH mailitem)
{
TSTART;
LPDISPATCH sender = get_oom_object (mailitem,
"Session.CurrentUser");
if (!sender)
{
TRETURN nullptr;
}
char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
gpgol_release (sender);
if (buf && strlen (buf))
{
log_debug ("%s:%s Sender fallback 5", SRCNAME, __func__);
TRETURN buf;
}
xfree (buf);
TRETURN nullptr;
}
char *
get_sender_SenderEMailAddress (LPDISPATCH mailitem)
{
TSTART;
char *type = get_oom_string (mailitem, "SenderEmailType");
if (type && !strcmp ("SMTP", type))
{
char *senderMail = get_oom_string (mailitem, "SenderEmailAddress");
if (senderMail)
{
log_debug ("%s:%s: Sender found", SRCNAME, __func__);
xfree (type);
TRETURN senderMail;
}
}
xfree (type);
TRETURN nullptr;
}
char *
get_sender_SentRepresentingAddress (LPDISPATCH mailitem)
{
TSTART;
char *buf = get_pa_string (mailitem,
PR_SENT_REPRESENTING_EMAIL_ADDRESS_W_DASL);
if (buf && strlen (buf))
{
log_debug ("%s:%s Found sent representing address \"%s\"",
SRCNAME, __func__, buf);
TRETURN buf;
}
xfree (buf);
TRETURN nullptr;
}
char *
get_inline_body ()
{
TSTART;
LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
if (!app)
{
TRACEPOINT;
TRETURN nullptr;
}
LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
if (!explorer)
{
TRACEPOINT;
TRETURN nullptr;
}
LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
gpgol_release (explorer);
if (!inlineResponse)
{
TRETURN nullptr;
}
char *body = get_oom_string (inlineResponse, "Body");
gpgol_release (inlineResponse);
TRETURN body;
}
int
get_ex_major_version_for_addr (const char *mbox)
{
TSTART;
LPDISPATCH account = get_account_for_mail (mbox);
if (!account)
{
TRACEPOINT;
TRETURN -1;
}
char *version_str = get_oom_string (account, "ExchangeMailboxServerVersion");
gpgol_release (account);
if (!version_str)
{
TRETURN -1;
}
long int version = strtol (version_str, nullptr, 10);
xfree (version_str);
TRETURN (int) version;
}
int
get_ol_ui_language ()
{
TSTART;
LPDISPATCH app = GpgolAddin::get_instance()->get_application();
if (!app)
{
TRACEPOINT;
TRETURN 0;
}
LPDISPATCH langSettings = get_oom_object (app, "LanguageSettings");
if (!langSettings)
{
TRACEPOINT;
TRETURN 0;
}
VARIANT var;
VariantInit (&var);
VARIANT aVariant[1];
DISPPARAMS dispparams;
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_INT;
dispparams.rgvarg[0].intVal = 2;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 0;
int ret = invoke_oom_method_with_parms_type (langSettings, "LanguageID", &var,
&dispparams,
DISPATCH_PROPERTYGET);
gpgol_release (langSettings);
if (ret)
{
TRACEPOINT;
TRETURN 0;
}
if (var.vt != VT_INT && var.vt != VT_I4)
{
TRACEPOINT;
TRETURN 0;
}
int result = var.intVal;
VariantClear (&var);
TRETURN result;
}
void
log_addins ()
{
TSTART;
LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
if (!app)
{
TRACEPOINT;
TRETURN;
}
LPDISPATCH addins = get_oom_object (app, "COMAddins");
if (!addins)
{
TRACEPOINT;
TRETURN;
}
std::string activeAddins;
int count = get_oom_int (addins, "Count");
for (int i = 1; i <= count; i++)
{
std::string item = std::string ("Item(") + std::to_string (i) + ")";
LPDISPATCH addin = get_oom_object (addins, item.c_str ());
if (!addin)
{
TRACEPOINT;
continue;
}
bool connected = get_oom_bool (addin, "Connect");
if (!connected)
{
gpgol_release (addin);
continue;
}
char *progId = get_oom_string (addin, "ProgId");
gpgol_release (addin);
if (!progId)
{
TRACEPOINT;
continue;
}
activeAddins += std::string (progId) + "\n";
xfree (progId);
}
gpgol_release (addins);
log_debug ("%s:%s:Active Addins:\n%s", SRCNAME, __func__,
activeAddins.c_str ());
TRETURN;
}
bool
is_preview_pane_visible (LPDISPATCH explorer)
{
TSTART;
if (!explorer)
{
TRACEPOINT;
TRETURN false;
}
VARIANT var;
VariantInit (&var);
VARIANT argvars[1];
DISPPARAMS args;
VariantInit (&argvars[0]);
argvars[0].vt = VT_INT;
argvars[0].intVal = 3;
args.cArgs = 1;
args.cNamedArgs = 0;
args.rgvarg = argvars;
if (invoke_oom_method_with_parms (explorer, "IsPaneVisible", &var, &args))
{
log_error ("%s:%s: Failed to check visibilty.",
SRCNAME, __func__);
TRETURN false;
}
if (var.vt != VT_BOOL)
{
TRACEPOINT;
TRETURN false;
}
TRETURN !!var.boolVal;
}
static LPDISPATCH
add_user_prop (LPDISPATCH user_props, const char *name)
{
TSTART;
if (!user_props || !name)
{
TRACEPOINT;
TRETURN nullptr;
}
wchar_t *w_name = utf8_to_wchar (name);
BSTR b_name = SysAllocString (w_name);
xfree (w_name);
/* Args:
0: DisplayFormat int OlUserPropertyType
1: AddToFolderFields Bool Should the filed be added to the folder.
2: Type int OlUserPropertyType Type of the field.
3: Name Bstr Name of the field.
Returns the added Property.
*/
VARIANT var;
VariantInit (&var);
DISPPARAMS args;
VARIANT argvars[4];
VariantInit (&argvars[0]);
VariantInit (&argvars[1]);
VariantInit (&argvars[2]);
VariantInit (&argvars[3]);
argvars[0].vt = VT_INT;
argvars[0].intVal = 1; // 1 means text.
argvars[1].vt = VT_BOOL;
argvars[1].boolVal = VARIANT_FALSE;
argvars[2].vt = VT_INT;
argvars[2].intVal = 1;
argvars[3].vt = VT_BSTR;
argvars[3].bstrVal = b_name;
args.cArgs = 4;
args.cNamedArgs = 0;
args.rgvarg = argvars;
int res = invoke_oom_method_with_parms (user_props, "Add", &var, &args);
VariantClear (&argvars[0]);
VariantClear (&argvars[1]);
VariantClear (&argvars[2]);
VariantClear (&argvars[3]);
if (res)
{
log_oom ("%s:%s: Failed to add property %s.",
SRCNAME, __func__, name);
TRETURN nullptr;
}
if (var.vt != VT_DISPATCH)
{
TRACEPOINT;
TRETURN nullptr;
}
LPDISPATCH ret = var.pdispVal;
memdbg_addRef (ret);
TRETURN ret;
}
LPDISPATCH
find_user_prop (LPDISPATCH user_props, const char *name)
{
TSTART;
if (!user_props || !name)
{
TRACEPOINT;
TRETURN nullptr;
}
VARIANT var;
VariantInit (&var);
wchar_t *w_name = utf8_to_wchar (name);
BSTR b_name = SysAllocString (w_name);
xfree (w_name);
/* Name -> 1 / Bstr
Custom 0 -> Bool True for search in custom properties. False
for builtin properties. */
DISPPARAMS args;
VARIANT argvars[2];
VariantInit (&argvars[0]);
VariantInit (&argvars[1]);
argvars[1].vt = VT_BSTR;
argvars[1].bstrVal = b_name;
argvars[0].vt = VT_BOOL;
argvars[0].boolVal = VARIANT_TRUE;
args.cArgs = 2;
args.cNamedArgs = 0;
args.rgvarg = argvars;
int res = invoke_oom_method_with_parms (user_props, "Find", &var, &args);
VariantClear (&argvars[0]);
VariantClear (&argvars[1]);
if (res)
{
log_oom ("%s:%s: Failed to find property %s.",
SRCNAME, __func__, name);
TRETURN nullptr;
}
if (var.vt != VT_DISPATCH)
{
TRACEPOINT;
TRETURN nullptr;
}
LPDISPATCH ret = var.pdispVal;
memdbg_addRef (ret);
TRETURN ret;
}
LPDISPATCH
find_or_add_text_prop (LPDISPATCH user_props, const char *name)
{
TSTART;
LPDISPATCH ret = find_user_prop (user_props, name);
if (ret)
{
TRETURN ret;
}
ret = add_user_prop (user_props, name);
TRETURN ret;
}
void
release_disp (LPDISPATCH obj)
{
TSTART;
gpgol_release (obj);
TRETURN;
}
diff --git a/src/windowmessages.cpp b/src/windowmessages.cpp
index d20655b..0b233e9 100644
--- a/src/windowmessages.cpp
+++ b/src/windowmessages.cpp
@@ -1,571 +1,570 @@
/* @file windowmessages.h
* @brief Helper class to work with the windowmessage handler thread.
*
* Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL 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.
*
* GpgOL 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, see .
*/
#include "windowmessages.h"
#include "common.h"
#include "oomhelp.h"
#include "mail.h"
#include "gpgoladdin.h"
#include "wks-helper.h"
#include "addressbook.h"
#include
#define RESPONDER_CLASS_NAME "GpgOLResponder"
/* Singleton window */
static HWND g_responder_window = NULL;
static int invalidation_blocked = 0;
LONG_PTR WINAPI
gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
- TSTART;
// log_debug ("WMG: %x", (unsigned int) message);
if (message == WM_USER + 42)
{
TSTART;
wm_ctx_t *ctx = (wm_ctx_t *) lParam;
log_debug ("%s:%s: Recieved user msg: %i",
SRCNAME, __func__, ctx->wmsg_type);
switch (ctx->wmsg_type)
{
case (PARSING_DONE):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: Parsing done for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->parsing_done();
TBREAK;
}
case (RECIPIENT_ADDED):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: Recipient add for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->locateKeys_o ();
TBREAK;
}
case (REVERT_MAIL):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: Revert mail for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->setNeedsSave (true);
/* Some magic here. Accessing any existing inline body cements
it. Otherwise updating the body through the revert also changes
the body of a inline mail. */
char *inlineBody = get_inline_body ();
xfree (inlineBody);
// Does the revert.
log_debug ("%s:%s: Revert mail. Invoking save.",
SRCNAME, __func__);
invoke_oom_method (mail->item (), "Save", NULL);
log_debug ("%s:%s: Revert mail. Save done. Updating body..",
SRCNAME, __func__);
mail->updateBody_o ();
log_debug ("%s:%s: Revert mail done.",
SRCNAME, __func__);
TBREAK;
}
case (INVALIDATE_UI):
{
if (!invalidation_blocked)
{
log_debug ("%s:%s: Invalidating UI",
SRCNAME, __func__);
gpgoladdin_invalidate_ui();
log_debug ("%s:%s: Invalidation done",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: Received invalidation msg while blocked."
" Ignoring it",
SRCNAME, __func__);
}
TBREAK;
}
case (INVALIDATE_LAST_MAIL):
{
log_debug ("%s:%s: clearing last mail",
SRCNAME, __func__);
Mail::clearLastMail ();
TBREAK;
}
case (CLOSE):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: Close for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->refCurrentItem();
Mail::closeInspector_o (mail);
TRACEPOINT;
Mail::close (mail);
log_debug ("%s:%s: Close finished.",
SRCNAME, __func__);
mail->releaseCurrentItem();
TBREAK;
}
case (CRYPTO_DONE):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: Crypto done for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
// modify the mail.
if (mail->cryptState () == Mail::NeedsUpdateInOOM)
{
// Save the Mail
log_debug ("%s:%s: Crypto done for %p updating oom.",
SRCNAME, __func__, mail);
mail->updateCryptOOM_o ();
}
if (mail->cryptState () == Mail::NeedsSecondAfterWrite)
{
invoke_oom_method (mail->item (), "Save", NULL);
log_debug ("%s:%s: Second save done for %p Invoking second send.",
SRCNAME, __func__, mail);
}
// Finaly this should pass.
if (invoke_oom_method (mail->item (), "Send", NULL))
{
log_error ("%s:%s: Send failed for %p. "
"Trying SubmitMessage instead.\n"
"This will likely crash.",
SRCNAME, __func__, mail);
/* What we do here is similar to the T3656 workaround
in mailitem-events.cpp. In our tests this works but
is unstable. So we only use it as a very very last
resort. */
auto mail_message = get_oom_base_message (mail->item());
if (!mail_message)
{
gpgol_bug (mail->getWindow (),
ERR_GET_BASE_MSG_FAILED);
TBREAK;
}
// It's important we use the _base_ message here.
mapi_save_changes (mail_message, FORCE_SAVE);
HRESULT hr = mail_message->SubmitMessage(0);
gpgol_release (mail_message);
if (hr == S_OK)
{
do_in_ui_thread_async (CLOSE, (LPVOID) mail);
}
else
{
log_error ("%s:%s: SubmitMessage Failed hr=0x%lx.",
SRCNAME, __func__, hr);
gpgol_bug (mail->getWindow (),
ERR_SEND_FALLBACK_FAILED);
}
}
else
{
mail->releaseCurrentItem ();
}
log_debug ("%s:%s: Send for %p completed.",
SRCNAME, __func__, mail);
TBREAK;
}
case (BRING_TO_FRONT):
{
HWND wnd = get_active_hwnd ();
if (wnd)
{
log_debug ("%s:%s: Bringing window %p to front.",
SRCNAME, __func__, wnd);
bring_to_front (wnd);
}
else
{
log_debug ("%s:%s: No active window found for bring to front.",
SRCNAME, __func__);
}
TBREAK;
}
case (WKS_NOTIFY):
{
WKSHelper::instance ()->notify ((const char *) ctx->data);
xfree (ctx->data);
TBREAK;
}
case (CLEAR_REPLY_FORWARD):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: Clear reply forward for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->wipe_o (true);
mail->removeAllAttachments_o ();
TBREAK;
}
case (DO_AUTO_SECURE):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: DO_AUTO_SECURE for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->setDoAutosecure_m (true);
TBREAK;
}
case (DONT_AUTO_SECURE):
{
auto mail = (Mail*) ctx->data;
if (!Mail::isValidPtr (mail))
{
log_debug ("%s:%s: DO_AUTO_SECURE for mail which is gone.",
SRCNAME, __func__);
TBREAK;
}
mail->setDoAutosecure_m (false);
TBREAK;
}
case (CONFIG_KEY_DONE):
{
log_debug ("%s:%s: Key configuration done.",
SRCNAME, __func__);
Addressbook::update_key_o (ctx->data);
TBREAK;
}
default:
log_debug ("%s:%s: Unknown msg %x",
SRCNAME, __func__, ctx->wmsg_type);
}
TRETURN 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
HWND
create_responder_window ()
{
TSTART;
size_t cls_name_len = strlen(RESPONDER_CLASS_NAME) + 1;
char cls_name[cls_name_len];
if (g_responder_window)
{
TRETURN g_responder_window;
}
/* Create Window wants a mutable string as the first parameter */
snprintf (cls_name, cls_name_len, "%s", RESPONDER_CLASS_NAME);
WNDCLASS windowClass;
windowClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
windowClass.lpfnWndProc = gpgol_window_proc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = (HINSTANCE) GetModuleHandle(NULL);
windowClass.hIcon = 0;
windowClass.hCursor = 0;
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = 0;
windowClass.lpszClassName = cls_name;
RegisterClass(&windowClass);
g_responder_window = CreateWindow (cls_name, RESPONDER_CLASS_NAME, 0, 0, 0,
0, 0, 0, (HMENU) 0,
(HINSTANCE) GetModuleHandle(NULL), 0);
TRETURN g_responder_window;
}
static int
send_msg_to_ui_thread (wm_ctx_t *ctx)
{
TSTART;
size_t cls_name_len = strlen(RESPONDER_CLASS_NAME) + 1;
char cls_name[cls_name_len];
snprintf (cls_name, cls_name_len, "%s", RESPONDER_CLASS_NAME);
HWND responder = FindWindow (cls_name, RESPONDER_CLASS_NAME);
if (!responder)
{
log_error ("%s:%s: Failed to find responder window.",
SRCNAME, __func__);
TRETURN -1;
}
SendMessage (responder, WM_USER + 42, 0, (LPARAM) ctx);
TRETURN 0;
}
int
do_in_ui_thread (gpgol_wmsg_type type, void *data)
{
TSTART;
wm_ctx_t ctx = {NULL, UNKNOWN, 0, 0};
ctx.wmsg_type = type;
ctx.data = data;
log_debug ("%s:%s: Sending message of type %i",
SRCNAME, __func__, type);
if (send_msg_to_ui_thread (&ctx))
{
TRETURN -1;
}
TRETURN ctx.err;
}
static DWORD WINAPI
do_async (LPVOID arg)
{
TSTART;
wm_ctx_t *ctx = (wm_ctx_t*) arg;
log_debug ("%s:%s: Do async with type %i after %i ms",
SRCNAME, __func__, ctx ? ctx->wmsg_type : -1,
ctx->delay);
if (ctx->delay)
{
Sleep (ctx->delay);
}
send_msg_to_ui_thread (ctx);
xfree (ctx);
TRETURN 0;
}
void
do_in_ui_thread_async (gpgol_wmsg_type type, void *data, int delay)
{
TSTART;
wm_ctx_t *ctx = (wm_ctx_t *) xcalloc (1, sizeof (wm_ctx_t));
ctx->wmsg_type = type;
ctx->data = data;
ctx->delay = delay;
CloseHandle (CreateThread (NULL, 0, do_async, (LPVOID) ctx, 0, NULL));
TRETURN;
}
LRESULT CALLBACK
gpgol_hook(int code, WPARAM wParam, LPARAM lParam)
{
/* Once we are in the close events we don't have enough
control to revert all our changes so we have to do it
with this nice little hack by catching the WM_CLOSE message
before it reaches outlook. */
LPCWPSTRUCT cwp = (LPCWPSTRUCT) lParam;
/* What we do here is that we catch all WM_CLOSE messages that
get to Outlook. Then we check if the last open Explorer
is the target of the close. In set case we start our shutdown
routine before we pass the WM_CLOSE to outlook */
switch (cwp->message)
{
case WM_CLOSE:
{
HWND lastChild = NULL;
log_debug ("%s:%s: Got WM_CLOSE",
SRCNAME, __func__);
if (!GpgolAddin::get_instance() || !GpgolAddin::get_instance ()->get_application())
{
TRACEPOINT;
TBREAK;
}
LPDISPATCH explorers = get_oom_object (GpgolAddin::get_instance ()->get_application(),
"Explorers");
if (!explorers)
{
log_error ("%s:%s: No explorers object",
SRCNAME, __func__);
TBREAK;
}
int count = get_oom_int (explorers, "Count");
if (count != 1)
{
log_debug ("%s:%s: More then one explorer. Not shutting down.",
SRCNAME, __func__);
gpgol_release (explorers);
TBREAK;
}
LPDISPATCH explorer = get_oom_object (explorers, "Item(1)");
gpgol_release (explorers);
if (!explorer)
{
TRACEPOINT;
TBREAK;
}
/* Casting to LPOLEWINDOW and calling GetWindow
succeeded in Outlook 2016 but always TRETURNed
the number 1. So we need this hack. */
char *caption = get_oom_string (explorer, "Caption");
gpgol_release (explorer);
if (!caption)
{
log_debug ("%s:%s: No caption.",
SRCNAME, __func__);
TBREAK;
}
/* rctrl_renwnd32 is the window class of outlook. */
HWND hwnd = FindWindowExA(NULL, lastChild, "rctrl_renwnd32",
caption);
xfree (caption);
lastChild = hwnd;
if (hwnd == cwp->hwnd)
{
log_debug ("%s:%s: WM_CLOSE windowmessage for explorer. "
"Shutting down.",
SRCNAME, __func__);
GpgolAddin::get_instance ()->shutdown();
TBREAK;
}
TBREAK;
}
case WM_SYSCOMMAND:
/*
This comes to often and when we are closed from the icon
we also get WM_CLOSE
if (cwp->wParam == SC_CLOSE)
{
log_debug ("%s:%s: SC_CLOSE syscommand. Closing all mails.",
SRCNAME, __func__);
GpgolAddin::get_instance ()->shutdown();
} */
break;
default:
// log_debug ("WM: %x", (unsigned int) cwp->message);
break;
}
return CallNextHookEx (NULL, code, wParam, lParam);
}
/* Create the message hook for outlook's windowmessages
we are especially interested in WM_QUIT to do cleanups
and prevent the "Item has changed" question. */
HHOOK
create_message_hook()
{
TSTART;
TRETURN SetWindowsHookEx (WH_CALLWNDPROC,
gpgol_hook,
NULL,
GetCurrentThreadId());
}
GPGRT_LOCK_DEFINE (invalidate_lock);
static bool invalidation_in_progress;
DWORD WINAPI
delayed_invalidate_ui (LPVOID minsleep)
{
TSTART;
if (invalidation_in_progress)
{
log_debug ("%s:%s: Invalidation canceled as it is in progress.",
SRCNAME, __func__);
TRETURN 0;
}
TRACEPOINT;
invalidation_in_progress = true;
gpgrt_lock_lock(&invalidate_lock);
int sleep_ms = (intptr_t)minsleep;
Sleep (sleep_ms);
int i = 0;
while (invalidation_blocked)
{
i++;
if (i % 10 == 0)
{
log_debug ("%s:%s: Waiting for invalidation.",
SRCNAME, __func__);
}
Sleep (100);
/* Do we need an abort statement here? */
}
do_in_ui_thread (INVALIDATE_UI, nullptr);
TRACEPOINT;
invalidation_in_progress = false;
gpgrt_lock_unlock(&invalidate_lock);
TRETURN 0;
}
DWORD WINAPI
close_mail (LPVOID mail)
{
TSTART;
do_in_ui_thread (CLOSE, mail);
TRETURN 0;
}
void
blockInv()
{
TSTART;
invalidation_blocked++;
log_oom ("%s:%s: Invalidation block count %i",
SRCNAME, __func__, invalidation_blocked);
TRETURN;
}
void
unblockInv()
{
TSTART;
invalidation_blocked--;
log_oom ("%s:%s: Invalidation block count %i",
SRCNAME, __func__, invalidation_blocked);
if (invalidation_blocked < 0)
{
log_error ("%s:%s: Invalidation block mismatch",
SRCNAME, __func__);
invalidation_blocked = 0;
}
TRETURN;
}