Page MenuHome GnuPG

No OneTemporary

diff --git a/src/ChangeLog b/src/ChangeLog
index bc62192..bd72721 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,1841 +1,1855 @@
+2008-03-07 Werner Koch <wk@g10code.com>
+
+ * engine.c (struct engine_filter_s): Add field ADD_EXTRA_LF.
+ (engine_request_exra_lf): New.
+ (engine_wait): Implement that.
+ * mimeparser.c (mime_decrypt): Add arg SIMPLE_PGP and call
+ engine_request_exra_lf.
+ (struct mime_context): Add field NO_MAIL_HEADER.
+ (message_cb): Implement it.
+ * message.cpp (message_decrypt): Set that flag for old style PGP.
+
+ * common.h (DBG_COMMANDS, debug_commands): New.
+ * ext-commands.cpp: Use it.
+
2008-03-06 Werner Koch <wk@g10code.com>
* mimemaker.c (do_mime_sign): Figure out the protocol to use.
* engine.c (engine_sign_start): Add new args SENDER and R_PROTOCOL.
* engine-assuan.c (op_assuan_sign): Ditto. Send SENDER command.
2008-03-05 Werner Koch <wk@g10code.com>
* main.c (read_options): Insert the debug registry key.
(write_options): More debug output.
2008-02-28 Werner Koch <wk@g10code.com>
* olflange-dlgs.cpp (GPGOptionsDlgProc): Fix initial setting of
openpgp and smime state. I wish C would haved used := for
assignments.
2008-02-26 Werner Koch <wk@g10code.com>
* common.c (qp_decode): Add arg S_LBRK.
* mimeparser.c (plaintext_handler, ciphertext_handler): Handle
soft line breaks.
* mapihelp.cpp (mapi_change_message_class): Handle opaque S/MIME
messages without an smime-type parameter.
2008-02-25 Werner Koch <wk@g10code.com>
* message.cpp (message_verify): Show message boxes for non-signed
messages.
(message_decrypt): Likewise.
2008-02-19 Marcus Brinkmann <marcus@g10code.de>
* engine-assuan.c (get_uiserver_name): Change default uiserver
path and remove work-around.
2008-02-18 Werner Koch <wk@g10code.com>
* message.cpp (pgp_body_to_attachment): New.
(message_decrypt): Use it.
(message_wipe_body_cruft): Also wipe already processed PGP
encrypted messages. Factor common code out to ...
(do_wipe_body): .. new.
* mapihelp.h (ATTACHTYPE_PGPBODY): New.
2008-02-15 Werner Koch <wk@g10code.com>
* olflange-dlgs.cpp: Remove code for IDC_ENCRYPT_WITH_STANDARD_KEY
and IDC_ENCRYPT_TO.
* dialogs.rc: Ditto.
2008-02-13 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_gpgolcharset_tag, mapi_get_gpgol_charset)
(mapi_set_gpgol_charset): New.
(mapi_get_gpgol_body_attachment): Transcode from Latin-1.
* mimeparser.c (start_attachment): Set the charset property.
(struct mime_context): Remove is_utf8 field.
2008-02-11 Werner Koch <wk@g10code.com>
* common.h (tlvinfo_t): New.
* common.c (parse_tlv): New. Based on code from libksba.
* mapihelp.cpp (has_smime_filename): New.
(is_really_cms_encrypted): New.
(mapi_change_message_class): Use this here to work around a
CryptoEx bug.
2008-02-08 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_change_message_class): Improve detecion of
CryptoEx messages.
2008-02-07 Werner Koch <wk@g10code.com>
* engine.c (engine_verify_start): Enable opaque signature for the
assuan backend.
* engine-assuan.c (op_assuan_verify): New arg OUTDATA. Add
support for opaque signatures.
* mimeparser.c (mime_verify_opaque): New.
* message.cpp (message_verify): Handle opaque signed S/MIME.
* message.cpp (message_wipe_body_cruft): Delete only encrypted
messages.
2008-02-06 Werner Koch <wk@g10code.com>
* mimeparser.c (mime_decrypt): New arg IS_RFC822.
* message.cpp (message_decrypt): Add code to see whether to use
the new arg.
2008-02-01 Werner Koch <wk@g10code.com>
* mimeparser.c (ciphertext_handler, ciphermessage_cb)
(ciphermessage_t2body): New.
(mime_decrypt): Use an rfc822 parser to pass the message to the
engine.
* mapihelp.cpp (mapi_get_attach_as_stream): Add arg R_ATTACH.
(mapi_set_attach_hidden): New.
2008-01-31 Werner Koch <wk@g10code.com>
* message.cpp (message_verify): Check that the body attachment is
available before shortcutting the verification.
* user-events.cpp (OnSelectionChange): Change SMIME message
class.
* mapihelp.cpp (mapi_change_message_class): Add arg
SYNC_OVERRIDE. Changed all callers to pass false.
(mapi_test_sig_status): Take care of sent messages.
(mapi_get_gpgol_body_attachment): Change args to allow use as
testing fucntion too. Adjusted caller.
2008-01-29 Werner Koch <wk@g10code.com>
* mimemaker.c (do_mime_sign): Set CTE for SMIME.
(delete_all_attachments): Remove extra semicolon accidently
inserted with revision 916.
2008-01-18 Marcus Brinkmann <marcus@g10code.de>
* mimeparser.c (mime_verify): New variable sig_len, and pass it on
to engine_verify_start.
* engine.h (engine_verify_start): Add new argument sig_len.
* engine.c (engine_verify_start): Add new argument sig_len and
pass it on to op_assuan_verify and op_gpgme_verify.
* engine-assuan.h (op_asssuan_verify): Add new argument sig_len.
* engine-gpgme.c (op_gpgme_verify): New argument sig_len and use
it instead of string length of signature.
* engine-assuan.c (op_assuan_verify): Likewise.
2008-01-11 Werner Koch <wk@g10code.com>
* mimemaker.c (finalize_message): Add args PROTOCOL and ENCRYPT
and use them to set the override message class.
(mime_sign, mime_encrypt, mime_sign_encrypt): Pass this info via
the new args.
(do_mime_sign): Set micalg to sha1 for CMS.
* message.cpp (message_decrypt): Add hack fro seldgenerated messages.
2008-01-10 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_gpgolmsgclass_tag, mapi_set_gpgol_msg_class):
New.
(mapi_change_message_class, mapi_get_message_type): Try override
first.
* message.cpp (message_incoming_handler): Remove arg MSGTYE and
let the function retrieve it. Changed all callers. Retry after a
sucessful message class change.
* olflange.cpp (getMsgtype): Remove. The caching might lead to
problems and makes it all more complex. Changed all callers to
use mapi_get_message_type.
* mimemaker.c (create_top_encryption_header) <SMIME>: Write empty
line after header.
2008-01-09 Werner Koch <wk@g10code.com>
* mimeparser.c (finish_saved_body): New.
(finish_attachment): Keep the body attachment open.
(mime_decrypt, mime_verify): Close the saved body data.
(t2body): Continue body attachments.
* message.cpp (message_verify): Save changes.
* mapihelp.cpp (mapi_change_message_class): Handle case of
PGP/MIME signed with IPM.Note. Save only of really needed, use
FORCE_SAVE and keep it open for read and write.
2008-01-07 Marcus Brinkmann <marcus@g10code.de>
* engine-assuan.c (replace_dollar_s): Remove obsolete function.
(get_quoted_socket_name): Remove obsolete function.
(get_uiserver_name): Invoke GUI server with --daemon. Fix buglet
in assignment.
(destroy_command): Change return type to void to suppress compiler
warning.
2008-01-04 Werner Koch <wk@g10code.com>
* engine-assuan.c (send_options): Call AllowSetForegroundWindow.
* main.c (read_options): Allo other values than 1 for enableDebug.
* common.h (DBG_IOWORKER, DBG_IOWORKER_EXTRA, DBG_FILTER)
(DBG_FILTER): New.
* engine.c (debug_filter): Turn it into a macro.
(debug_filter_extra): New macro, used instead of checking the
value of debug_filter.
* engine-assuan.c (debug_ioworker, debug_ioworker_extra): new.
Use them all over the file to enable debugging.
* engine.c (engine_encrypt_start): Add arg HWND.
(engine_sign_start, engine_decrypt_start, engine_verify_start)
(engine_start_keymanager): Ditto.
* mimemaker.c (mime_encrypt, mime_sign_encrypt, mime_sign)
(do_mime_sign): Add arg HWND and pass it to the engine.
* mimeparser.c (mime_verify, mime_decrypt): Pass HWND.
* message.cpp (message_sign, sign_encrypt): Pass HWND.
(message_incoming_handler, message_verify, message_decrypt): Add
arg HWND and pass it on.
* message-events.cpp (OnRead): Pass HWND to message function.
* ext-commands.cpp (DoCommand): Ditto.
2008-01-03 Werner Koch <wk@g10code.com>
* mimemaker.c (mime_sign_encrypt): Fix result test of do_mime_sign.
(write_tempsign_attachment): Remove.
(do_mime_sign): Change last ark to a sink_t.
(mime_sign_encrypt): Rework to use a temporary stream instead of a
temporary attachment.
(create_mapi_attachment): Remove arg TEMPSIGN.
2007-12-18 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_msgcls_from_pgp_lines): Limit check to the
first 1 k and stop testing after the first PGP armor line.
(mapi_get_message_type): Return MSGTYPE_SMIME.
(mapi_change_message_class): Take care of CryptoEx signatures.
* mapihelp.h (MSGTYPE_SMIME): New.
* message.cpp (message_incoming_handler): Check message class for
unknown and unchecked messages. Take care MSGTYPE_SMIME.
* ext-commands.cpp (DoCommand): Add debug command change message class.
2007-12-07 Werner Koch <wk@g10code.com>
* ext-commands.cpp (InstallCommands): Removed toolbar button fro
decrypt as this is not anymore needed. Fixes bug#860.
(QueryHelpText): Ditto.
2007-11-12 Werner Koch <wk@g10code.com>
* olflange.h (class GpgolExt): Rename m_gpgSelectSmime to
m_protoSelection.
* message-events.cpp (OnWriteComplete): Use it accordingly.
* main.c (write_options, read_options): Load and save it.
* dialogs.rc: Add new check box for OpenPGP default protocol.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Adjust for above chnages.
2007-11-09 Werner Koch <wk@g10code.com>
* main.c (read_options): New option ENABLE_DEBUG to be enabled
only using the Registry.
(read_options): Show warning for certain option combinations.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Hide the Advanced options
button unless in debug mode.
* mapihelp.cpp (get_gpgollastdecrypted_tag): New.
(mapi_test_last_decrypted): New.
(mapi_has_last_decrypted): new.
* mimeparser.c (finish_message): Update the Last Decrypted property.
* message.cpp (message_decrypt): Use it here.
(message_wipe_body_cruft): New.
* main.c (do_log_window_hierarchy): Factor some code out to ..
(do_log_window_info): .. this.
(log_window_hierarchy): Log parent window info.
(get_64bit_session_marker): New.
(initialize_session_key): Init session marker.
* Makefile.am (gpgol_SOURCES): Remove item-events.cpp
* olflange.cpp (GpgolExt): Disable the GpgOLItemEvents as they can
only be used with the undocumented ECF file.
2007-10-29 Werner Koch <wk@g10code.com>
* mimemaker.c (create_top_signing_header): Add arg FIRST.
(mime_sign): Factor allmost all code out to ..
(do_mime_sign): .. new function.
(create_mapi_attachment): Add arg TEMPSIGN.
(delete_all_attachments): Adjust for that.
(mime_encrypt): Factor some code out to ..
(create_top_encryption_header): .. new.
(write_tempsign_attachment): New.
(mime_sign_encrypt): Implement.
2007-10-22 Werner Koch <wk@g10code.com>
* engine-assuan.c (connect_uiserver): Try to start the server.
(get_uiserver_name, replace_dollar_s, get_quoted_socket_name): New.
* main.c (REGKEY): Remove.
* common.h (GNUPG_REGKEY): New.
* common.c (default_homedir): Use it in place of a hard coded one.
(get_locale_dir): Ditto.
(gpgol_spawn_detached): New.
2007-10-18 Werner Koch <wk@g10code.com>
* common.c (get_system_check_bitmap): New.
* decrypt.bmp, encrypt.bmp, sign.bmp, key_mana.bmp: Change
background color to pink and voila Outlook presents them
transparent.
* logo.bmp: Cleaned up to use just one color.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Do not use LoadImage.
2007-10-16 Werner Koch <wk@g10code.com>
* myexchext.h (EECMDID_): Add a few more of these constants.
* ext-commands.cpp (check_menu): New.
(toolbar_add_menu): Rename to ..
(add_menu): .. this.
(toolbar_from_tbe): Remove.
(add_toolbar): New.
(InstallCommands): Use new toolbar helper.
(QueryButtonInfo): Changed to use new toolbar helper.
(~GpgolExtCommands): New.
* engine.c (engine_encrypt_start): Add arg R_PROTOCOL.
* engine-assuan.c (op_assuan_encrypt): Add arg R_USED_PROTOCOL and
ask the server for it.
* mimemaker.c (sink_encryption_write_b64): New.
(mime_encrypt): Add S/MIME support.
2007-10-15 Werner Koch <wk@g10code.com>
* engine-assuan.c (op_assuan_start_keymanager): New.
* engine.c (engine_start_keymanager): New.
* ext-commands.cpp (DoCommand): Call it.
2007-10-12 Werner Koch <wk@g10code.com>
* gpgol-rsrcs.rc: Remove.
* dialogs.rc: Renamed from olflange-rsrcs.rc.
* dialogs.h: Rename for olflange-ids.h. Changed all includers.
* Makefile.am: Adjust accordingly.
* verify-dialog.c (verify_dialog_box): Do not distinguish
languages.
(verify_dlg_set_labels): New.
(verify_dlg_proc): Call it.
* passphrase-dialog.c (passphrase_callback_box): Do not
distinguish languages.
(decrypt_key_dlg_set_labels): New.
(decrypt_key_dlg_proc): Call it.
(decrypt_key_ext_dlg_set_labels): New.
(decrypt_key_ext_dlg_proc): Call it.
* recipient-dialog.c (recipient_dialog_box): Do not distinguish
languages.
(recipient_dialog_box2): Ditto.
(recipient_dlg_set_labels): New.
(recipient_dlg_proc): Call it.
2007-10-11 Werner Koch <wk@g10code.com>
* ext-commands.cpp (toolbar_add_menu): USe "@" to indicate a
separator and "" to ski the entry. Changed callers.
* common.h (struct): Remove AUTO_SIGN_ATTACH. Add ENABLE_SMIME.
* main.c (read_options, write_options): Save the new option.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Implement new option and
print warning.
* mapihelp.cpp (mapi_change_message_class): Take care of enable_smime.
* olflange-ids.h: Renumbered.
* olflange-rsrcs.rc: Keep only the english dialog and changed all
text to dummy texts.
* olflange-dlgs.cpp (set_labels): New.
(GPGOptionsDlgProc): Call it.
* property-sheets.cpp (GetPages): Do not distinguish languages.
* config-dialog.c (config_dialog_box): Ditto.
(config_dlg_set_labels): New.
* gpgol-rsrcs.rc (IDD_OPT): Move to olflange-rsrcs.rc and rename
to IDD_EXT_OPTIONS.
(IDD_OPT_DE): Remove.
2007-10-10 Werner Koch <wk@g10code.com>
* main.c (read_options): Remove saveDecryptedAttachment. Add
smimeDefault.
2007-10-08 Werner Koch <wk@g10code.com>
* main.c (do_log): Remove trailing LF from w32 error message and
also print the numeric error code.
2007-09-25 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Link against libassuan.
* util.h (DIM, DIMof): New.
* engine.c (filter_gpgme_read_cb): Implement nonblock feature.
(filter_gpgme_write_cb): Ditto.
2007-09-24 Werner Koch <wk@g10code.com>
* common.c (standard_homedir, default_homedir): New.
(w32_shgetfolderpath): Make static.
2007-09-21 Werner Koch <wk@g10code.com>
* mimeparser.c (build_mimeinfo): New.
(finish_message): Store the mime structure.
* mapihelp.cpp (mapi_get_message_class): New.
(mapi_get_sig_status): New.
(get_gpgolprotectiv_tag, get_gpgolsigstatus_tag)
(get_gpgolattachtype_tag): Factored code out to ...
(create_gpgol_tag): .. New.
(get_gpgolmimeinfo_tag): New.
(mapi_get_mime_info): New.
2007-09-20 Werner Koch <wk@g10code.com>
* user-events.cpp, user-events.h: New.
* olflange.h (class GpgolExt): Add member for it.
* olflange.cpp (QueryInterface): Hook it in.
2007-09-19 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_has_sig_status): Return true if any sig
status is present.
(mapi_test_sig_status): New. Take semantics of the former
mapi_has_sig_status. Changed all callers.
(mapi_change_message_class): Set sign status to n/a for other
message classed.
* mimemaker.c (finalize_message): Mark created messages.
(write_buffer_for_cb): Rename from write_buffer_voidarg and return
number of bytes written.
(collect_signature): Return number of bytes written.
* mapihelp.h (struct mapi_attach_item_s): New member
PRIVATE_MAPITABLE.
* mapihelp.cpp (mapi_create_attach_table)
(mapi_release_attach_table): Keep the mapi table oben and put
PR_ATATCH_NUM into the MAPIPOS member.
(mapi_get_gpgol_body_attachment): Use PR_ATATCH_NUM.
* mimeparser.c (finish_message): New.
(mime_decrypt, mime_verify): Call it.
2007-09-17 Werner Koch <wk@g10code.com>
* olflange.cpp: Print gpgme version.
2007-09-14 Werner Koch <wk@g10code.com>
* engine-gpgme.c: Rewrote most of it.
2007-09-13 Werner Koch <wk@g10code.com>
* common.c (xrealloc): New.
2007-09-11 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_encrypt_data): New.
2007-09-08 Werner Koch <wk@g10code.com>
* engine.c: New.
* engine.h: Rewrite. Factor existing stuff out to ..
* engine-gpgme.h: .. new.
* engine-assuan.h, engine-assuan.c: New.
2007-09-07 Werner Koch <wk@g10code.com>
* common.c (qp_decode): Handle softe line breaks.
2007-09-06 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_get_body): New.
* mimeparser.c (protocol_t): Move to .. common.h.
2007-09-05 Werner Koch <wk@g10code.com>
* mimemaker.c, mimemaker.h: New.
2007-08-31 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_set_header): New.
* message.cpp (pgp_mime_from_clearsigned): New.
(message_verify): Use it.
* main.c: Call srand ().
* util.h (trailing_ws_p): New.
* common.c (generate_boundary): New.
2007-08-30 Werner Koch <wk@g10code.com>
* message-events.h (class GpgolMessageEvents): Rename M_IS_SMIME
to M_PROCESSED.
* message-events.cpp (OnRead, OnReadComplete): Ditto.
(OnReadComplete): Remove preview decryption stuff.
(OnWriteComplete): Remove GpgMsg based code.
* ext-commands.cpp (DoCommand): Ditto
(InstallCommands): Do not init the watcher.
* olflange.cpp (GpgolExt): Do not init the watcher.
(GpgolExt): Do not clanup the watcher.
* main.c (DllMain): Ditto.
* gpgmsg.cpp, gpgmsg.hh: Remove.
* watcher.cpp: Remove
* pgpmime.c, pgpmime.h: Remove.
* mapihelp.h (MSGTYPE_GPGOL_CLEAR_SIGNED)
(MSGTYPE_GPGOL_PGP_MESSAGE): New.
* mapihelp.cpp (mapi_get_message_type): Map them.
(get_msgcls_from_pgp_lines): New.
(mapi_change_message_class): Detect old old style PGP messages.
(mapi_get_body_as_stream): New.
* message.cpp (message_verify, message_decrypt): Handle them.
* message-events.cpp (OnRead): Ditto.
2007-08-29 Werner Koch <wk@g10code.com>
* ext-commands.h (class GpgolExtCommands): Add members
M_NCMDKEYMANAGER and M_NCMDDECRYPT.
* ext-commands.cpp (GpgolExtCommands): Initialize it.
(InstallCommands): Use them here instead of reusing another variable.
(DoCommand, Help, QueryHelpText, QueryButtonInfo): Restructured
for better readibility.
2007-08-21 Werner Koch <wk@g10code.com>
* w32-gettext.c (SUBLANG_BENGALI_BANGLADESH): Fix to 2 as per MSDN.
(SUBLANG_PUNJABI_PAKISTAN): Remove as it is not in MSDN.
(SUBLANG_ROMANIAN_MOLDOVA): Remove as it is not in MSDN.
(SUBLANG_ROMANIAN_ROMANIA): Change to value 1 as per MSDN.
2007-08-13 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (gpgol_SOURCES): Add common.h.
2007-08-06 Werner Koch <wk@g10code.com>
Lots of changes to support S/MIME and to revamp most of the old
code. More changes to follow. The list of changes below is not
complete as it does not identfy all newly written code.
* mimeparser.c: New. Based on pgpmime.c
* display.cpp (update_display): Removed unused arg MSG.
(update_display): Set the body to an empty string even if it is
not HTML.
* olflange.cpp (Install): Print OL version only once.
* verify-dialog.c (verify_dialog_box): Add arg PROTOCOL.
(load_sigbox): Ditto.
(verify_dlg_proc): Changed title acccording to used protocol.
* Makefile.am (gpgol_LDADD): Add libadvapi due to our use of
CryptGenRandom.
* main.c (get_crypt_random, initialize_session_key): New.
(DllMain): Initialize the session key.
(get_128bit_session_key, create_initialization_vector): New.
* serpent.c: New. Taken from libgcrypt 1.3.0 and stripped down to
fit our needs. Add CFB encryption API.
* serpent.h: New.
* mapihelp.cpp (mapi_mark_moss_attach): New.
* mymapitags.h (PR_ATTACHMENT_HIDDEN): New.
* display.cpp (open_inspector): New.
* mapihelp.cpp (mapi_get_binary_prop): New.
* engine-gpgme.c (op_verify_detached_sig_gpgme): Add arg
PROTOCOL. Changed all callers to pass the OPENPGP protocol.
* common.c (b64_decode): Renamed from base64_decode and use new
type for the context.
(b64_init): New.
* pgpmime.c (qp_decode, base64_decode): Move to common.c and make
public.
* common.h (STRICT): Remove.
* intern.h: Rename to common.h.
2007-07-20 Werner Koch <wk@g10code.com>
* myexchext.h (IOutlookExtItemEvents.): New.
* item-events.h, item-events.cpp: New.
* olflange.cpp (GpgolExt, QueryInterface): Hook it in.
2007-07-19 Werner Koch <wk@g10code.com>
* attached-file-events.cpp: Renamed from attach.c.
* attached-file-events.h: Renamed from attach.h. Removed some
inlines to the impl file.
* Makefile.am (gpgol_LDADD): Add libole32.
2007-07-18 Werner Koch <wk@g10code.com>
* mapihelp.c (log_mapi_property): Support STRIGN8 and UNICODE.
* myexchext.h (IExchExtUserEvents, IExchExtSessionEvents): New
declarations.
* session-events.cpp, session-events.h: New.
* olflange.cpp (GpgolExt, QueryInterface): Hook session-events in.
* olflange.cpp (DllRegisterServer): Register only for interfaces
we really use.
(ext_context_name): New. Factored out from several places.
(Install): Initialize for Session context.
Renamed all CGPGExchExt* classes to Gpgol*.
* olflange.h, olflange.cpp: Factor most code out to ..
* ext-commands.cpp, ext-commands.h, message-events.cpp
* message-events.h, property-sheets.cpp, property-sheets.h
* ol-ext-callback.cpp, ol-ext-callback.h: .. New.
2007-07-17 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Add ws2_32.
* main.c (log_window_hierarchy, do_log_window_hierarchy): New.
* olflange.cpp (show_window_hierarchy): Replace by
log_window_hierarchy.
(g_initdll): Make static.
(InstallCommands): Factor some code out to ..
(toolbar_from_tbe, toolbar_add_menu): .. new.
* mapihelp.c, mapihelp.h: New.
* olflange.cpp (show_mapi_property): Factor out to mapihelp.c
and rename to log_mapi_property.
2007-04-10 Werner Koch <wk@g10code.com>
* display.cpp (find_message_window): Add arg LEVEL for debugging.
Ignore MsoCommand* Windows. Fixes bug 735.
2006-10-14 Timo Schulz <ts@g10code.de>
* recipient-dialog.c (lv_get_item_param): New.
(copy_item): Use it here to copy the opaque param.
(recipient_dlg_proc): And here to avoid the hidden column.
(initialize_rsetbox): Localize column names.
* olflange.cpp (get_outlook_property): Free returned BSTR.
(InstallCommands): Likewise.
2009-09-06 Timo Schulz <ts@g10code.de>
* recipient-dialog.c (recipient_dialog2): Do not free
key array here.
2008-08-21 Timo Schulz <ts@g10code.de>
* engine-gpgme.c (op_lookup_keys): Only add useable keys
and add all invalid keys to unknown.
* recipient-dialog.c (copy_item): Rewritten.
(initialize_keybox): Add comment to clarify use of fnd_keys.
(recipient_dialog_box): Simplified.
(find_item): Support partial search.
2006-08-19 Timo Schulz <ts@g10code.de>
* olflange-rsrcs.rc: Correct some dialog sizes.
* passphrase-dialog.c (decrypt_key_dlg_proc): Automatically
select the secret key if only one is available.
* config-dialog.c (GPGOptionsDlgProc): Passphrase cache
time is now requested in minutes but still internally
stored as seconds.
2006-08-15 Timo Schulz <ts@g10code.de>
* decrypt.bmp, encrypt.bmp: Restore format.
* olflange.cpp (OnWriteComplete): Correct exit code handling.
* recipient-dialog.c (initialize_rsetbox): Correct column width.
(recipient_dlg_proc): Do not show the cancel error any longer.
* passphrase-dialog.c (decrypt_key_dlg_proc): Likewise.
(decrypt_key_ext_dlg_proc): Ditto.
* olgpgcore.def: Deleted unused file.
2006-06-14 Timo Schulz <ts@g10code.com>
* gpgol-rscs.rc (IDD_OPT): The English version of the dialog
has no log file item. Add it.
2006-05-22 Timo Schulz <ts@g10code.com>
* verify-dialog.c (load_sigbox): A sigsum of 0 also indicates
a valid (good) signature.
2006-04-25 Werner Koch <wk@g10code.com>
* xmalloc.h: New. Moved prototypes from util.h
* w32-gettext.h: Include it.
* common.c (utf8_to_wincp): Removed and replaced all callers by
utf8_to_native.
* common.c (wchar_to_utf8, utf8_to_wchar): Moved to ..
* w32-gettext.c: .. here.
(utf8_to_native): Make sure that we always return
a string and never NULL.
(native_to_utf8): New.
(native_to_wchar): New.
* gpgmsg.cpp (decrypt): Use native_to_utf8 for i18n strings
expected to be utf-8.
* pgpmime.c (pgpmime_decrypt, pgpmime_verify): Ditto.
2006-04-24 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (decrypt): New arg INFO_ONLY.
* olflange.cpp (OnReadComplete): Add code to call decrypt but with
INFO_ONLY if preview decryption has not been requested.
* main.c (read_options): New compatibility option no_preview_info.
* gpgmsg.cpp (getRecipients): Don't add the default key here.
(encrypt_and_sign): But do it here.
* engine-gpgme.c (op_get_one_key): New.
2006-04-22 Timo Schulz <ts@g10code.com>
* common.c (utf8_to_wincp): Corrected utf8 decoding.
* passphrase-dialog.c (load_recipbox): Likewise.
* olflange-dlg.cpp (GPGOptionsDlgProc): Activate the
'confirm' button when the dialog state has been changed.
* olflange-rsrcs.rc (IDD_GPG_OPTIONS_DE): Change description.
2006-03-28 Werner Koch <wk@g10code.com>
* olflange-rsrcs.rc (IDD_GPG_OPTIONS_DE): Add new control box.
(IDD_GPG_OPTIONS): Updated to match German version.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Ditto.
* gpgmsg.cpp (decrypt): Implemented PREFER_HTML option.
(get_long_attach_data): New.
2006-03-27 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_verify_detached_sig_gpgme): New.
* pgpmime.c (pgpmime_verify): New. First take on a PGP/MIME
signature verification.
* gpgmsg.cpp (is_pgpmime): Renamed to is_pgpmime_enc.
(class GpgMsgImpl): Niew members media_type. media_subtype and
ct_protocol.
(get_msg_content_type): New.
(decrypt): Show a warning for PGP/MIME signed messages.
2006-03-26 Werner Koch <wk@g10code.com>
* intern.h: New option PREFER_HTML.
* main.c (write_options, read_options): Ditto.
2006-03-20 Werner Koch <wk@g10code.com>
* olflange.cpp (Install): Also check major part of build version.
2006-03-17 Timo Schulz <ts@g10code.com>
* w32-gettext.c (utf8_to_native): Make it global.
* config-dialog.c (get_open_file_name): Make sure the selected
file really exists.
* passphrase-dialog.c (decrypt_dlg_proc): UTF8 conversion.
(passphrase_callback_box): Likewise.
* recipient-dialog.c (recipient_dlg_proc): Likewise.
* verify-dialog.c (load_akalist): Likewise.
(load_sigbox): Likewise.
* common.c (utf8_to_wincp): New.
2006-03-15 Werner Koch <wk@g10code.com>
* olflange.cpp (Install): Print gpgol version for debugging.
2006-03-14 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_dlg_proc): When used
as a signing key selection dialog, use a different title.
* gpgol-rsrcs.rc: Use German titles for German dialog versions.
2006-02-23 Werner Koch <wk@g10code.com>
* main.c (read_options): Set default caching time to 10 minutes.
2006-01-16 Werner Koch <wk@g10code.com>
* verify-dialog.c (load_sigbox): Give a hint in case of a bad
signature.
* gpgol-rsrcs.rc (IDD_ENC_DE): Add an informational header.
2005-12-07 Werner Koch <wk@g10code.com>
* olflange.cpp (Install): Check the version and print a warning.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Simplified the default
key code.
* config-dialog.c (store_config_value): Create key if it does not
exists.
(load_config_value_ext): Removed.
2005-12-06 Werner Koch <wk@g10code.com>
* config-dialog.c (start_key_manager): Don't pass the options to
access.
2005-12-06 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (getRecipients): Add the default key to the list of
recipients.
* recipient-dialog.c (recipient_dlg_proc): Add the already found
keys to the selected ones.
* olflange.cpp (OnWriteComplete): Need to disable the deleting of
HTML bodys.
2005-12-05 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Add -loleaut32.
* engine-gpgme.c (op_verify_detached_sig_mem): New.
* olflange.cpp (OnWriteComplete): Pass HTML flag to sign call.
(put_outlook_property): Need to use a BSTR for the sake of putting
HTMLBody.
* gpgmsg.cpp (sign): Add arg WANT_HTML.
(free_attach_info): New. Use it in the destructor.
(createHtmlAttachment): New.
(encrypt_and_sign, sign): Use it here.
(writeAttestation): Don't write an empty attestation.
2005-12-02 Werner Koch <wk@g10code.com>
* verify-dialog.c (verify_dialog_box): Actually allow for German
dialog.
* recipient-dialog.c (recipient_dialog_box)
(recipient_dialog_box2): Ditto.
* passphrase-dialog.c (signer_dialog_box)
(passphrase_callback_box): Ditto.
* intern.h (struct): New field PREVIEW_DECRYPT. Use it instead os
the old compatibility flags.
* main.c (write_options, read_options): Store/load preview decrypt.
* config-dialog.c (config_dlg_proc): Removed homedir and gpgbinary
options as they are deprecated. Put logfile entry here.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Remove logfile entry. Add
preview-decrypt checkbox.
* olflange.cpp (InstallCommands): Remove experimental preview
command.
* w32-gettext.c (gettext_localename): New.
* config-dialog.c (config_dialog_box): Use it here to match the
gettext behaviour.
(GetPages): Ditto.
2005-12-01 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_decrypt_stream_to_gpgme, decrypt_stream)
(op_decrypt): Add arg PREVIEW_MODE.
* pgpmime.c (pgpmime_decrypt): New arg PREVIEW_MODE.
(struct pgpmime_context): New field PREVIEW.
(message_cb, plaintext_handler): Handle preview mode.
* gpgmsg.cpp (class GpgMsgImpl): Renamed SILENT to PREVIEW.
(setSilent): Renamed to ..
(setPreview): .. this.
(decrypt): Handle preview mode. Display a string while decrypting
PGP/MIME messages.
* display.cpp (update_display): New arg TEXT.
* gpgmsg.cpp (class GpgMsgImpl): Removed BODY_PLAIN and BODY.
(getDisplayText): Removed.
(loadBody): Changes to return the allocated body.
(getOrigText): Removed.
(getMessageType): Rewritten to take the body text as argument.
(decrypt): Pass plaintext directly to update_display. Free
plaintext.
(sign, encrypt_and_sign): Likewise.
* olflange.cpp (OnWriteComplete): Always delete PR_BODY on error.
2005-11-30 Werner Koch <wk@g10code.com>
* gpgmsg.cpp: Made more strings translatable.
* olflange.cpp: Replaced all LoadStrings by gettext calls.
* olflange-ids.h: Removed the IDS_ constants.
* olflange-rsrcs.rc: Removed the stringtables.
* common.c (get_root_key, read_w32_registry_string): New. Taken
for libgpg-error.
* main.c (i18n_init): New.
(DllMain): Call it.
(get_locale_dir, drop_locale_dir): New.
* w32-gettext.c, w32-gettext.h: New. Taken form libgpg-error.
Slightly modified due to the fact that gpgol is W32-only.
* util.h (_, N_): Define standard i18n macros.
* display.cpp (set_message_body): Do not delete a RTF property.
* util.h (SRCNAME): New. Changed all __FILE__ to this.
* main.c (log_srcname): New.
2005-11-20 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (loadBody): For HTML try to read the HTMLBody from
the OOM as a last resort.
* olflange.cpp (get_outlook_property): New.
(put_outlook_property_int): New.
2005-11-15 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Remove -lintl for now.
* olflange.cpp (OnWriteComplete): Make sure that we don't sent out
unencrypted stuff on error.
* display.cpp (set_message_body): Add arg IS_HTML.
(update_display): Ditto.
* gpgmsg.cpp (loadBody): New arg WANT_HTML.
(getOrigText): Ditto.
* olflange.h (class CGPGExchExtMessageEvents): Add M_WANT_HTML.
* olflange.cpp (OnWrite): Set it.
(OnWriteComplete): Pass its value to the encrypt functions.
2005-11-10 Werner Koch <wk@g10code.com>
* config-dialog.c (start_key_manager): Changed invocation of
default keymanager.
2005-10-21 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (libgpgme.a, libgpgme.a): New targets.
(gpgol_DEPENDENCIES): Add libgpgme.a and libgpg-error.a.
(clean-local): Likewise.
(gpgol_LDADD): Link to these local versions statically.
2005-10-20 Marcus Brinkmann <marcus@g10code.de>
* mapi32.def: New file.
* Makefile.am (gpgol_DEPENDENCIES): New variable.
(libmapi32.a): New target.
(gpgol_LDADD): Replace mapi32.dll with "-L . -lmapi32".
(clean-local): New target.
2005-10-19 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (sign, encrypt_and_sign): Don't set the body first to
empty. If this is really required we should do this in
set_message_body.
(sign): Save changes. Set content type to text/plain.
(encrypt_and_sign): Save changes also for empty bodies.
2005-10-06 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (writeAttestation): Use gpgme_free for BUFFER.
* engine-gpgme.c (data_to_file): Ditto.
2005-10-06 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (gpgol_DEPENDENCIES): New variable.
(libmapi32.a): New target.
(gpgol_LDADD): Replace mapi32.dll with "-L . -lmapi32".
* Makefile.am (gpgol_LDADD): Prefix gpgol.def and mapi32.dll with
$(srcdir).
* Makefile.am (.rc.o): Invoke windres with "-I .".
* Makefile.am (.rc.o): Invoke windres with -I $(srcdir).
* Makefile.am (gpgol_SOURCES): Add util.h.
2005-09-29 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (encrypt_and_sign): Pass signing key to encryption
function.
* passphrase-dialog.c (signer_dialog_box): New arg encrypting.
* gpgmsg.cpp (encrypt_and_sign): Set content type.
* engine-gpgme.c (op_lookup_keys): Fixed multiple key detection.
2005-09-28 Werner Koch <wk@g10code.com>
* olflange.cpp (DoCommand): Catch close command and resend to
avoid the "save changes?".
* display.cpp (update_display): Take care of utf-8 issues.
* common.c (latin1_to_utf8): New.
* pgpmime.c (latin1_data_write): New.
(plaintext_handler): Use it here.
(message_cb): Detect utf-8 encoding.
* main.c (read_options): Make sure that compat flags are always
properly initialized.
* display.cpp (find_message_window): First try to find the window
by class name.
* common.c (wchar_to_utf8_2): New.
2005-09-27 Werner Koch <wk@g10code.com>
* pgpmime.c (pgpmime_decrypt): Pass a pseduo filename to the
decryption function.
* verify-dialog.c (load_sigbox): Get key direct from gpgme.
* passphrase-dialog.c (load_secbox, load_recipbox): Reworked.
(decrypt_key_dlg_proc, decrypt_key_ext_dlg_proc): Reworked.
(count_keys, release_keyarray): New.
(signer_dialog_box, passphrase_callback_box): Adjusted to above
changes.
* engine-gpgme.c (op_deinit): Remove keycache cleanup.
* Makefile.am (gpgol_SOURCES): Removed keycache.c, keycache.h.
* keycache.c, keycache.h: Removed.
* recipient-dialog.c (recipient_dialog_box)
(recipient_dialog_box2): Rewritten and changed interface.
(load_rsetbox): Removed keycache stuff and rewrote to make use of
the keyarray.
(copy_item, initialize_keybox, recipient_dlg_proc): Ditto.
(keycache_to_key_array): Removed.
* engine-gpgme.c (op_lookup_keys): Rewritten, changed interface.
* gpgmsg.cpp (count_recipients): Renamed to ..
(count_strings): .. this.
(count_keys): New.
(free_recipient_array): Renamed to ..
(free_string_array): .. this.
(encrypt_and_sign): Adjusted for changes in op_lookup_keys and
recipient_dialog_box2.
2005-09-26 Werner Koch <wk@g10code.com>
* passphrase-dialog.c (get_pubkey_algo_str): Add DSA and old Elgamal.
* gpgmsg.cpp (gatherAttachmentInfo): Ignore attachments we can't
open.
* main.c (write_options): Print message on error. Rearranged to
make use of a table for all options.
2005-09-23 Werner Koch <wk@g10code.com>
* recipient-dialog.c (recipient_dlg_proc): Removed
IDC_ENC_OPTARMOR stuff; it was not used.
(load_rsetbox): Fixed detection of encryption capability.
* gpgol-ids.h, gpgol-rsrcs.rc: Ditto.
2005-09-22 Werner Koch <wk@g10code.com>
* engine-gpgme.c (decrypt_stream): Use gpgme_op_decrypt_verify.
* gpgmsg.cpp (gatherAttachmentInfo): Ignore attestations when
checking for pgp/mime.
* pgpmime.c (pgpmime_decrypt): Added arg HWND.
(message_cb, plaintext_handler): Write attachments.
* pgpmime.c (base64_decode): New.
* rfc822parse.c (parse_field): Treat Content-Disposition special.
* gpgmsg.cpp (get_save_filename): Moved to ..
* common.c (get_save_filename): .. here.
2005-09-20 Timo Schulz <ts@g10code.com>
* attach.c: New.
* attach.h: New.
* olflange.cpp (CGPGExchExt): Allocate class for
attached file events.
(QueryInterface): Return interface pointer for
attached file events.
* passphrase-dialog.c (load_secbox): Use new GPGME
interface.
(get_pubkey_algo_str): New.
* verify-dialog.c (load_sigbox): Likewise.
* recipient-dialog.c (load_recipbox): Likewise.
2005-09-19 Werner Koch <wk@g10code.com>
* msgcache.c (flush_if_needed): New.
(msgcache_put): use it.
* intern.h (opt): New compatibility flags AUTO_DECRYPT and
NO_ATTESTATION.
* olflange.cpp (InstallCommands): Use watcher stuff only when this
option has been enabled.
* gpgmsg.cpp (decrypt): Take care of NO_ATTESTATION.
* main.c (DllMain): Removed debug output; this should not be
used before initialization!
* watcher.cpp: Include config.h. Removed weird line endings.
* gpgmsg.cpp (encrypt_and_sign): Call SaveChanges.
2005-09-15 Timo Schulz <ts@g10code.com>
* util.h: Provider watcher prototypes.
* watcher.cpp (watcher_init_hook): New.
(watcher_free_hook): New.
(watcher_set_callback_ctx): New.
(cbt_proc): New.
* display.cpp (find_message_window): Removed GpgMsg param.
Changed all callers.
2005-09-15 Werner Koch <wk@g10code.com>
* olflange.cpp (OnWriteComplete): Take care of EEME_FAILED.
(OnWrite): Check that we are encrypting only plain text messages.
* myexchext.h: Add flags used by OnReadComplete.
2005-09-14 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (writeAttestation): Add a content type.
(gatherAttachmentInfo): Detect whether we already have an attestation.
(decrypt): Don't create duplicate attestations.
(writeAttestation): Translate LF to CRLF.
2005-09-13 Werner Koch <wk@g10code.com>
* pgpmime.c (pgpmime_decrypt): New arg ATTESTATION.
* engine-gpgme.c (add_verify_attestation): New.
(op_decrypt, op_verify_detached_sig, op_verify_detached_sig)
(op_verify_detached_sig): Add new arg ATTESTATION. Changed all
callers.
(at_sig_summary, at_sig_validity, add_verify_attestation): New.
The code has been taken and modified from Mutt's crypt-gpgme.c and
entirely been writen by g10 Code.
(at_fingerprint): Ditto.
* gpgmsg.cpp (class GpgMsgImpl): New member ATTESTATION. Use it
in all calls to the functions above.
* gpgmsg.cpp (decryptAttachment, decrypt): Save plaintext back
into the MAPI if desired.
2004-09-08 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (lod_recipbox): Use gpgme directly
to resolve the keyids to userids.
* usermap.c, usermap.h: Removed.
* HashTable.cpp, HashTable.h: Removed.
2005-09-07 Timo Schulz <ts@g10code.com>
* common.c: Removed unused code.
* intern.h: Likewise.
2005-09-07 Timo Schulz <ts@g10code.com>
* olflange.cpp (ul_release): New. Wrapper with error checking
around UlRelease.
(InstallCommands): Separate test for type and if string is empty.
2005-09-06 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_decrypt_stream): Factored most code out to ..
(decrypt_stream): .. new.
(op_decrypt_stream_to_buffer): Simplified accordingly. Fixed
possible buffer overflow when trying to make it a string.
(op_decrypt_stream_to_gpgme): New.
* pgpmime.c, pgpmime.h: New.
* rfc822parse.h, rfc822parse.c: New. Taken from GnuPG 1.9.
2005-09-06 Timo Schulz <ts@g10code.com>
* config-dialog.c (get_open_file_name): Correctly terminated filter.
New parameter for the title. Changed all callers.
(get_folder): Likewise.
2005-09-01 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (get_pgp_armor_type): New.
(gatherAttachmentInfo): Enhanced to detect text/plain pgp.
(verify): Removed.
(decrypt): Do clearsig verification here.
* gpgmsg.cpp (decrypt): Kludge to workaround OL not updating a
window.
* msgcache.c (msgcache_get_from_mapi): New.
* display.cpp (find_message_window): Don't use the GpgMsg function
to to the string compare.
* gpgmsg.cpp (matchesString): Removed.
* msgcache.c (msgcache_set_active): Removed.
(msgcache_get): Rewritten.
(msgcache_put): Now uses PR_CONVERSATION_INDEX as key. Update
existing entries.
* olflange.cpp (OnRead): Removed msgcache_set_active.
(InstallCommands): Now uses the ConversationIndex to match the
reply with the message.
2005-08-31 Werner Koch <wk@g10code.com>
* olflange.cpp (DllRegisterServer): Define a CLSID and set the
ThreadingModel.
2005-08-30 Werner Koch <wk@g10code.com>
Renamed from "outlgpg" to "gpgol".
More or less finished this major rewrite.
2005-08-26 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp, MapiGPGME.h: Removed.
* display.cpp, display.h: New.
2005-08-23 Werner Koch <wk@g10code.com>
* msgcache.c, msgcache.h: New.
2005-08-22 Werner Koch <wk@g10code.com>
* olflange.cpp: Major cleanups and partly rewrites.
2005-08-18 Werner Koch <wk@g10code.com>
* gpgmsg.cpp, gpgmsg.hh: New.
2005-08-17 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (setMessageAccess): Removed as it was only used at
one place.
(setRTFBody): Use the above code here directly..
(setWindow, setMessage): Removed. We can't use that because there
is only one instance of this class.
(decrypt): Add args HWND and MSG. Changed caller.
(getBody): Changed to ..
(get_body): .. plain function and add new arg MSG. Changed all
callers.
(isHtmlMessage): Likewise changed to ..
(is_html_message): .. plain function and add new arg MSG.
(doCmd): Removed.
* common.c (utf8_to_wchar): New.
* MapiGPGME.cpp (passphraseCallback): Removed.
(getPassphrase, clearPassphrase, storePassphrase): Removed.
(add_html_line_endings): Rewritten.
* engine-gpgme.c (op_sign_encrypt_start): Removed because it is
not used anywhere.
(op_sign): Renamed to ..
(do_sign): .. this and made local.
2005-08-16 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (signAttachment): Simplified.
* engine-gpgme.c (op_sign_file): Add arg TTL.
(op_sign_file_ext): Removed.
(op_sign_file_next): Renamed to ..
(do_sign_file): .. this and made local.
(do_sign_file): Updated to use new passphrase callback
semantics.
(op_decrypt_file): Ditto.
(free_recipients): Need to use gpgme_key_release and not just
free.
* engine-gpgme.c (do_decrypt): Factored some code out to ..
(update_passphrase_cache): .. new.
(op_sign_encrypt_file): Updated to use new passphrase callback
semantics.
* MapiGPGME.cpp (getBody): Properly distinguish property types.
(delete_buf): Removed macro. We now use malloc for the body
string. Changed other places to use delete directly for clarity.
(fail_if_null): Removed. Replaced by direct tests and a call to
out_of_core.
(setDefaultKey): Now use malloc/free instead of new/delete.
Changed at other places too.
(getDefaultKey): Changed to return a const char *.
* common.c (wchar_to_utf8): New.
(out_of_core): Made global and call abort after displaying the
message box.
2005-08-14 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (log_debug_w32): New.
(do_log): New arg W32ERR. Make sure to print a trailing LF.
* passphrase-dialog.c (passphrase_callback_box): Revamped.
(free_decrypt_key): Wipe out a passphrase. Remove superfluous
variable clearing.
* util.h (wipememory2, wipememory): New.
(wipestring): New.
* engine-gpgme.c (op_decrypt): Renamed to ..
(do_decrypt): .. this and made local.
(clear_error_if_cancel): Removed as we inlined the code.
(do_decrypt): Cleaned up.
* main.c (DllMain): Initialize passcaching subsystem.
* passcache.c, passcache.h: New.
* intern.h: Include it here.
* util.h: New.
* olflange.cpp (DllRegisterServer): Remove key for the old
versions of this plugin.
Merged olgpgmain.dll and olgpgcore.dll into outlgpg.dll.
* Makefile.am: Renamed target to outlgpg. Added required files.
* olflange.cpp, olflange.h: Renamed from GPGExch.cpp and
GPGExch.h. Removed all the MFC cruft as it is not required - it
was only used to get hands on the hInstance of the DLL; chnaged
that to use the glob_hinst which gets set by DllMain.
* outlgpg.def: New.
* olflange-def.h: New.
* olflange-dlgs.cpp: Renamed from GPGOptionsDlgs.cpp.
* olflange-ids.h: Renamed from ../olflange/resource.h
* olflange-rsrcs.rc: Renamed from ..olflange/olgpgmaindlgs.rc and
stripped off unneedded stuff.
* olgpgcoredlgs.rc: Renamed to ..
* outlgpg-rsrcs.rc: .. this and stripped of cruft.
* olgpgcoredlgs.h: Renamed to ..
* outlgpg-ids.h: .. this.
* versioninfo.rc.in: New.
2005-08-12 Timo Schulz <ts@g10code.com>
* config-dialog.c (sotre_extension_value, load_extension_value):
Adjust registry key.
2005-08-12 Werner Koch <wk@g10code.com>
* intern.h: Moved keycache prototypes to keycache.h.
* MapiGPGME.cpp (lock_log, unlock_log): New.
* engine-gpgme.c (op_init): Check GPGME version.
* main.c (outlook_gpg_get_version): Removed as it is now derived
from config.h.
(DllMain): Initializes gpgme and mapigpgme.
* MapiGPGME.cpp (initialize_mapi_gpgme): New.
* config-dialog.c (store_extension_value, load_extension_value):
Changed key to "OutlGPG".
* MapiGPGME.h (class MapiGPGME): New methods versionString and
showVersion. Breaks ABI but it doesn't matter as we are also
going to change the name of the project.
* Makefile.am: Renamed target to olgpgcore.
* resource.h: Renamed to ...
* olgpgcoredlgs.h: .. and removed cruft from generator.
* gpgmedlgs.rc: Renamed to ..
* olgpgcoredlgs.rc: ... and removed cruft.
* libgpgmedlgs.def: Renamed to ..
* olgpgcore.def: .. this.
2005-08-11 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (log_debug): New. Rewrote the whole log stuff.
It is not anymore per instance.
(logDebug): New version with va_list arg.
* MapiGPGME.cpp (passphraseCallback): Use gpgme_error_t becuase
C++ enforces enum types.
* engine-gpgme.c (op_lookup_keys): s/id/names/. id is a reved
word Obj-C and it is good style not to use it in plain C code.
(op_sign_file_next): Use gpgme_passphrase_cb_t in declaration.
(op_decrypt_next): Ditto.
* MapiGPGME.cpp (count_recipients): Renamed from countRecipients
method, made local and changed both callers.
(log_key_info): Changed output format. New arg PREFIX. Changed
callers.
(add_html_line_endings): Renamed from addHtmlLineEndings method
and made local.
(logDebug): Open in text mode. Removed all superfluous "\r" from
callers.
2005-08-09 Timo Schulz <ts@g10code.com>
* main.c (outlook_gpg_get_version): New.
Use same version as the Outlook GPG plugin.
* MapiGPGME.cpp [!__MINGW32__]: Changed sequence of include files.
2005-08-09 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (userid_from_key, keyid_from_key): New. Changed
all calls to the deprecated gpgme_key_get_string_attr function by
these.
* MapiGPGME.h, MapiGPGME.cpp: Splitted into interace and
implementation.
* HashTable.h (class HashTable): No need to dll export anything.
2005-08-08 Werner Koch <wk@g10code.com>
* common.c (w32_shgetfolderpath): Added.
* config-dialog.c (load_config_value_ext): use it here.
2005-07-21 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (decrypt): Only return if no valid PGP
data was found and the message has no attachments.
* engine-gpgme.c (op_encrypt): Use --textmode to fix
problems when the recipient OS has different line endings.
For example Win32->Linux.
2005-07-20 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (addHtmlLineEndings): New.
(encrypt):Use it here.
(signEncrypt): Likewise. Free memory in case of errors.
(decrypt): Free memory in case of the verify procedure.
Issue a warning when the text of the mail could not be
updated.
(isMessageEncrypted): New.
(countAttachments): Check for null pointers.
(clearPassphrase): Likewise.
* config-dialog.c (store_config_value): Support '%val%' input.
* verify-dialog.c (load_akalist): Return the number of user-ids
which were added.
* passphrase-dialog.c (load_secbox): Make sure we really start
to add the item data at the begin.
* intern.h: Fixed GCC compiler problem.
2005-07-19 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (encrypt): Handle cancel.
(encryptAttachments): If no attachments exist, close the table.
(decryptAttachments): Likewise.
(signAttachments): Likewise.
(isHtmlBody): New.
(isHtmlMessage): New.
(setBody): Html support.
(encrypt): Figure out if message is html and encode the right body.
(signEncrypt): Likewise.
(verify): Always use the non-html body for GPG input.
(decrypt): Likewise.
* engine-gpgme.c (recipient_dialog_box2): Set cancel flag.
(op_sign_start): Handle cancel.
(recipient_dlg_proc): Make sure there is at least one selected
key. Disable armor checkbox.
2005-07-19 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (~MapiGPGME): After releasing the
memory, set all pointers to NULL. It seems that NT5
bases operating systems are more pedantic with such
issues than 9X based systems.
(freeUnknownKeys): Likewise.
(freeRecipients): Likewise.
(encrypt): Outlook 2003 returns a body string which
is not NULL but \0 with a length of 0. Handle it.
(sign): Likewise.
(signEncrypt): Likewise.
* engine-gpgme.c (free_recipients): New.
(op_encrypt_start): Handle cancel and free memory.
2005-07-18 Timo Schulz <ts@g10code.com>
* gpgmedlgs.rc: Native language support for German.
2005-07-14 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp: Add some missing MAPI constants.
(passphrase_callback_box): Repair cancel button.
(decrypt): If the user cancels the operation, do not
alter the message text.
* config-dialog.c (expand_path): New. From WinPT.
(load_config_value): Support for REG_EXPAND_SZ.
Suggested by Sebastian.
2005-07-13 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (sign): Ignore empty bodies.
(signEncrypt): Likewise. Free recipient memory.
(encrypt): Modify code so it really works.
(attachPublicKey): New.
* engine-gpgme.c (op_export_keys): New.
2005-07-12 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (displayError): New.
(writeOptions): Use it here. Simplify the code.
(signAttachments): New.
(sign): Sign attachments. Noted by Ralf.
(processAttachment): Support for sign-only.
2005-07-08 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (setEnableLogging): New.
(getEnableLogging): New.
(logDebug): Move all logging code to this function.
(readOptions): Automatically enable logging if the
'LogFile' registry entry is valid.
(prepareLogging): New.
(readOptions): Properly handle ""-strings.
* config-dialog.c (does_file_exist): Allow to have
parameters like '--keymanager' and cut them off before
checking the existence.
(start_key_manager): Free memory.
(config_dlg_proc): Initialize pointer to 'NULL'.
(SHFree): New. Special function to handle shell memory.
(get_folder): Free memory.
(does_folder_exist): New.
2005-07-06 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp: s/ATTR_/ATT_.
(saveDecryptedAttachment): Cut off the prefix.
(clearConfig): New.
(clearObject): New.
(MapiGPGME): Use it here.
Use ATT_PREFIX instead of a hardcoded string.
* engine-gpgme.c
(op_sign_file_next): New.
(op_sign_file): Call op_sign_file2.
(op_sign_file_ext): New.
2005-07-05 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (readOptions, writeOptions):
Support for the new 'auto sign attachment' flag.
(signAttachment): New.
(setSignAttachments): New.
(getSignAttachments): New.
* engine-gpgme.c (op_sign_file): Enable armor.
2005-07-03 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Implement
the decryption part and only use it if the 'save
decrypted attachment' flag is set.
(saveDecryptedAttachment): New.
(writeOptions): Save 'decrypt attachment' flag.
(readOptions): Load it here.
(cleanupTempFiles): Check handle.
(encrypt): If the message has no body skip the
procedure.
(op_decrypt_file): Set recipient callback.
(decryptAttachments): We do not alter the attachment
so there is no need to release it again.
Replace all 'free' with 'xfree'.
2005-07-01 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Check that
the file has a valid PGP extension before we try
to decrypt the attachment.
(checkAttachmentExtension): Check if the file
extension is a vliad PGP extension.
2005-06-30 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Use a unique
temp name to make a cleanup at the end easier.
(cleanupTempFiles): Delete possible left over
temp files.
(~MapiGPGME): Use it here.
2005-06-22 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Close attachment
before we delete it.
(streamOnFile): Directly use the attachment.
(streamFromFile): Likewise.
(closeAttachment): Renamed to..
(releaseAttachment): ..this.
2005-06-21 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (getMessageType): Support all types.
(streamOnFile): More straight forward now.
(encryptAttachments): Get the attachment table first.
(getAttachPathname): New.
(processAttachment): Add new parameter for the position
of the attachment.
(streamFromFile): New.
(generateTempname): New.
* engine-gpgme.c (op_sign_encrypt_file): New.
2005-06-17 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (MapiGPGME): Initialize all attachment
components to zero. Thanks to Sebstian for pointing this out.
2005-06-16 Timo Schulz <ts@g10code.com>
* engine-gpgme.c (check_encrypt_result): New. Check if
the encrypt procedure returned some invalid recipients.
(op_encrypt): Use it here.
(op_encrypt_file): Likewise.
(op_sign_encrypt): Likewise.
* missing.h: Removed unused constants.
* verify-dialog.c (load_sigbox): Handle v3 RSA keys.
* passphrase-dialog.c (load_secbox): Make sure the index
from the first loop matche the second. Which means skip
all invalid keys also.
2005-06-13 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (writeOptions): New Option 'defaultKey'.
(readOptions): Likewise. Force overwrite 'addDefaultKey'.
2005-06-07 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_key_dlg_proc): Add a
reference to the key object so it will be still valid
after the dialog is destroyed.
* MapiGPGME.cpp (freeUnknownKeys): Do not try to free
the context if no keys are available. Fixed a segv.
(signEncrypt): The new code now gets a persistent pointer
to the key.
* engine-gpgme.c (op_sign_encrypt): Load the right
dialog to request the passphrase.
2005-06-05 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (load_recipbox): Check ctx if null.
(passphrase_callback_box): Different dialogs for sign
and decrypt.
(signer_dialog_box): Do not zero the context too early.
* keycache.c (enum_gpg_seckeys): Also reload if ctx
is NULL.
* MapiGPGME.cpp (signEncrypt): Return if the user
cancelled the signer selection dialog.
* recipient-dialog.c (recipient_dlg_proc): Check 'Text Mode'
button.
* keycache.c (enum_gpg_keys, enum_gpg_seckeys): Fully
reset the keycache initializing it again. Thanks to Ralf.
2005-06-04 Timo Schulz <ts@g10code.com>
* verify-dialog.c (load_sigbox): Only issue a warning
if the key exists but is not valid.
Get pkalgo from the signature.
Fixed format string problem s/%d/%s.
* config-dialog.c (load_config_value): Close reg handle.
(config_dlg_proc): Show error if the values could not
be written to the registry.
2005-06-03 Timo Schulz <ts@g10code.com>
* mapuser.c (new_usermap): New.
(free_usermap): New.
* engine-gpgme.c (op_decrypt): Return 'No_Seckey' if
appropriate and not just 'Decrypt_Failed'.
Set the gpgme_ctx_t object in the callback to allow
to list the 'encrypt_to' entries.
* passphrase-dialog.c (decrypt_key_dlg_proc): Make sure
we only warn when there is a valid callback context.
(load_secbox): New parameter 'ctlid'. Changed all callers.
(load_recipbox): New. Use usermap to lookup user-id's.
(decrypt_key_ext_dlg_proc): New dialog procedure for
the callback mode.
* HashTable.cpp (HashTable_new): New. C-interface.
(HashTable_free): Likewise.
(HashTable_get): Likewise.
(HashTable_put): Likewise.
(HashTable_get_i): Likewise.
(HashTable_size): Likewise.
2005-05-29 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_key_dlg_proc): Warning
if the user cancels the signing process.
Make the passphrase field invisible if the key is used
in selection mode.
(recipient_dlg_proc): Likewise.
(signer_dialog_box): Return -1 if the user cancelled the
dialog.
* engine-gpgme.c (op_sign): Set flags to '1' to indicate
signing process.
* config-dialog.c (does_file_exist): Use appropriate
length for xmalloc. Noted by Sebastian.
2005-05-24 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (setXHeader): New.
(getXHeader): New.
* engine-gpgme.c (op_sign_file): Implemented.
2005-05-22 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (createAttachment): New.
(deleteAttachment): New.
(encryptAttachments): New.
(encrypt): Also encrypt the attachments if possible.
(signEncrypt): Likewise.
(setEncodingFormat): New.
(getEncodingFormat): New.
(readOptions, writeOptions): Store encoding format.
(MapiGPGME): The default encoding is 'CLASSIC'.
2005-05-21 Timo Schulz <ts@g10code.com>
* HashTable.h: Export functions.
* MapiGPGME.cpp (freeAttachments): New.
(getAttachments): New.
(openAttachment): New.
(closeAttachment): New.
(processAttachment): New.
(hasAttachments): New.
(countAttachments): New.
(doCmdFile): New. Can handle files.
(doCmdAttach): New. Can handle attachment action types.
(signEncrypt): Release locusr key.
* engine-gpgme.c (op_sign_file): New. Dummy.
(op_sign_encrypt_file): New. Dummy.
2005-05-11 Timo Schulz <ts@g10code.com>
* common.c (cache_item_new): New.
(cache_item_free): New.
* engine-gpgme.c (op_decrypt_start_ext): Return an cache item and
not just the passphrase. Changed all caller.
* MapiGPGME.cpp (passphraseCallback): Support caching.
(decrypt): Likewise.
(storePassphrase): Likewise.
(getPassphrase): Likewise.
* HashTable.cpp: New.
2005-05-10 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_key_dlg_proc): Reset 'hide state'.
Show some text when the user entered a wrong passphrase.
* MapiGPGME.cpp (findMessageWindow): New.
(setRTFBody): New.
(decrypt): Just change the window text, not the MAPI object.
Call verify() if this is a clearsigned message.
(getMessageType): New.
(getAttachmentExtension): New.
(verify): Extract text and set it.
* engine-gpgme.c (op_decrypt): Spawn verify dialog if the text
was also signed.
(op_decrypt_file): New.
* verify-dialog.c (load_sigbox): Avoid key cache and give more
information.
* passphrase-dialog.c (decrypt_key_dlg_proc): Handle 'x' clicks.
* logging.c (log_debug): Disable it for release versions.
2005-05-08 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (readOptions): Handle logfile.
(writeOptions): Likewise.
(storePassphrase): New.
(clearPassphrase): New.
(passphraseCallback): New. Needs to be static...
(decrypt): Store passphrase if requested.
(~MapiGPGME): Free memory.
(streamOnFile): New.
(getAttachMethod): New.
(getAttachFilename): New.
(setAttachMethod): New.
(getAttachFilename): New.
(getMessageHasAttachments): New.
(getMessageFlags): New.
* engine-gpgme.c (op_decrypt): New. Factoured out
code from...
(op_decrypt_start): ..here. Now call op_decrypt
with standard parameters.
(op_decrypt_next): Allow to use a pre-defined
passphrase callback. Needed for caching.
2005-05-02 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (fail_if_null, delete_buf): New
(doCmd): New.
(setBody): If the body to set is empty, do nothing.
(freeKeyArray): New.
(readOptions): New.
(writeOptions): New.
Implement all getters and setters for the config code.
* keycache.c: Now that we use a DLL, we create a
shared data segment for static data.
(load_keycache_objects): New.
2005-05-01 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (rtfSync): New.
* logging.c (log_debug): New.
* common.c (xfree): New.
* engine-gpgme.c (op_lookup_keys): Corrected offsets.
2005-04-29 Timo Schulz <ts@g10code.com>
* engine-gpgme.c (op_encrypt_file): New.
2005-04-27 Timo Schulz <ts@g10code.com>
* config-dialog.c (config_dialog_box): Add dialog
item to select a GUI key manager.
Check that the entered files really exist.
(config_dlg_proc): Likewise.
(does_file_exist): New.
2005-04-24 Timo Schulz <ts@g10code.com>
* main.c (DllMain): New. With a static library it was not
possible to have a separate resource file. Thus we use a
DLL now which contains all needed dialogs.
* common.c (set_global_hinstance): New.
* libgpgmedlgs.def: New.
2005-04-18 Timo Schulz <ts@g10code.com>
* recipient-dialog.c (recipient_dialog_box2): New
way to show pre-selected keys.
(copy_item): New paramenter for the pos.
(find_item): Do not select the item.
* gpgmedlgs.rc (IDD_ENC): New label to describe
the listbox.
2005-04-15 Timo Schulz <ts@g10code.com>
* common.c (xmalloc, xcalloc, xstrdup): New.
(out_of_core): New.
* recipient-dialog.c (initialize_keybox): New.
(find_item): New.
* MapiGPGME.cpp (freeUnknownKeys): New.
(signEncrypt): Show dialog to select a key if no
default key was set.
* Replace all std-c alloc functions with x equivalents.
2005-04-13 Timo Schulz <ts@g10code.com>
* engine-gpgme.c (do_init): Alloc keycache objects.
(do_deinit): Cleanup the mess.
(op_lookup_keys): New. Allow to find keys via the email.
* GPGME.cpp (MapiGPGME): New. MAPI interface.
2005-04-07 Timo Schulz <ts@g10code.com>
* verify-dialog.c (load_akalist): New.
(load_sigbox): Handle bad signatures.
* keycache.c (enum_gpg_keys): Allow to reset the
enum context.
* config-dialog.c (get_open_file_name): Use MAX_PATH.
(get_folder): Likewise.
* recipient-dialog.c (load_rsetbox): Modify code to
add the last keycache item.
* passphrase-dialog.c (load_secbox): Likewise.
diff --git a/src/common.h b/src/common.h
index a3170bb..15b96ab 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,226 +1,230 @@
/* common.h - Common declarations for GpgOL
* Copyright (C) 2004 Timo Schulz
* Copyright (C) 2005, 2006, 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifndef GPGOL_COMMON_H
#define GPGOL_COMMON_H
#include <gpgme.h>
#include "util.h"
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
/* The Registry key used by GnuPg and closley related software. */
#define GNUPG_REGKEY "Software\\GNU\\GnuPG"
/* Identifiers for the protocol. We use different one than those use
by gpgme. FIXME: We might want to define an unknown protocol to
non-null and define such a value also in gpgme. */
typedef enum
{
PROTOCOL_UNKNOWN = 0,
PROTOCOL_OPENPGP = 1000,
PROTOCOL_SMIME = 1001
}
protocol_t;
/* Possible options for the recipient dialog. */
enum
{
OPT_FLAG_TEXT = 2,
OPT_FLAG_FORCE = 4,
OPT_FLAG_CANCEL = 8
};
typedef enum
{
GPG_FMT_NONE = 0, /* do not encrypt attachments */
GPG_FMT_CLASSIC = 1, /* encrypt attachments without any encoding */
GPG_FMT_PGP_PEF = 2 /* use the PGP partioned encoding format (PEF) */
}
gpgol_format_t;
/* Type of a message. */
typedef enum
{
OPENPGP_NONE = 0,
OPENPGP_MSG,
OPENPGP_SIG,
OPENPGP_CLEARSIG,
OPENPGP_PUBKEY, /* Note, that this type is only partly supported */
OPENPGP_SECKEY /* Note, that this type is only partly supported */
}
openpgp_t;
extern HINSTANCE glob_hinst;
extern UINT this_dll;
/* Passphrase callback structure. */
struct passphrase_cb_s
{
gpgme_key_t signer;
gpgme_ctx_t ctx;
char keyid[16+1];
char *user_id;
char *pass;
int opts;
int ttl; /* TTL of the passphrase. */
unsigned int decrypt_cmd:1; /* 1 = show decrypt dialog, otherwise secret key
selection. */
unsigned int hide_pwd:1;
unsigned int last_was_bad:1;
};
/* Global options - initialized to default by main.c. */
#ifdef __cplusplus
extern
#endif
struct
{
int enable_debug; /* Enable extra debug options. Values
larger than 1 increases the debug log
verbosity. */
int enable_smime; /* Enable S/MIME support. */
int passwd_ttl; /* Time in seconds the passphrase is stored. */
protocol_t default_protocol;/* The default protocol. */
int encrypt_default; /* Encrypt by default. */
int sign_default; /* Sign by default. */
int enc_format; /* Encryption format for attachments. */
char *default_key; /* The key we want to always encrypt to. */
int enable_default_key; /* Enable the use of DEFAULT_KEY. */
int preview_decrypt; /* Decrypt in preview window. */
int prefer_html; /* Prefer html in html/text alternatives. */
/* The compatibility flags. */
struct
{
unsigned int no_msgcache:1;
unsigned int no_pgpmime:1;
unsigned int no_oom_write:1; /* Don't write using Outlooks object model. */
unsigned int no_preview_info:1; /* No preview info about PGP/MIME. */
unsigned int old_reply_hack: 1; /* See gpgmsg.cpp:decrypt. */
unsigned int auto_decrypt: 1; /* Try to decrypt when clicked. */
unsigned int no_attestation: 1; /* Don't create an attestation. */
} compat;
} opt;
/* The state object used by b64_decode. */
struct b64_state_s
{
int idx;
unsigned char val;
int stop_seen;
int invalid_encoding;
};
typedef struct b64_state_s b64_state_t;
/* Bit values used for extra log file verbosity. Value 1 is reserved
to enable debug menu options. */
#define DBG_IOWORKER 2
#define DBG_IOWORKER_EXTRA 4
#define DBG_FILTER 8
#define DBG_FILTER_EXTRA 16
#define DBG_MEMORY 32
+#define DBG_COMMANDS 64
+
+/* Macros to used in conditionals to enabel debug output. */
+#define debug_commands (opt.enable_debug & DBG_COMMANDS)
/* Type and constants used with parse_tlv. */
struct tlvinfo_s
{
int cls; /* The class of the tag. */
int tag; /* The tag. */
int is_cons; /* True if it is a constructed object. */
int is_ndef; /* True if the object has an indefinite length. */
size_t length; /* The length of the value. */
size_t nhdr; /* The number of octets in the header (tag,length). */
};
typedef struct tlvinfo_s tlvinfo_t;
#define MY_ASN_CLASS_UNIVERSAL 0
#define MY_ASN_CLASS_APPLICATION 1
#define MY_ASN_CLASS_CONTEXT 2
#define MY_ASN_CLASS_PRIVATE 3
#define MY_ASN_TAG_OBJECT_ID 6
#define MY_ASN_TAG_SEQUENCE 16
/*-- common.c --*/
void set_global_hinstance (HINSTANCE hinst);
void center_window (HWND childwnd, HWND style);
HBITMAP get_system_check_bitmap (int checked);
char *get_save_filename (HWND root, const char *srcname);
char *utf8_to_wincp (const char *string);
const char *default_homedir (void);
size_t qp_decode (char *buffer, size_t length, int *r_slbrk);
void b64_init (b64_state_t *state);
size_t b64_decode (b64_state_t *state, char *buffer, size_t length);
/* The length of the boundary - the buffer needs to be allocated one
byte larger. */
#define BOUNDARYSIZE 20
char *generate_boundary (char *buffer);
int gpgol_spawn_detached (const char *cmdline);
int parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti);
/*-- recipient-dialog.c --*/
unsigned int recipient_dialog_box (gpgme_key_t **ret_rset);
unsigned int recipient_dialog_box2 (gpgme_key_t *fnd, char **unknown,
gpgme_key_t **ret_rset);
/*-- passphrase-dialog.c --*/
int signer_dialog_box (gpgme_key_t *r_key, char **r_passwd, int encrypting);
gpgme_error_t passphrase_callback_box (void *opaque, const char *uid_hint,
const char *pass_info,
int prev_was_bad, int fd);
void free_decrypt_key (struct passphrase_cb_s *ctx);
const char *get_pubkey_algo_str (gpgme_pubkey_algo_t id);
/*-- config-dialog.c --*/
void config_dialog_box (HWND parent);
int start_key_manager (void);
int store_extension_value (const char *key, const char *val);
int load_extension_value (const char *key, char **val);
/*-- verify-dialog.c --*/
int verify_dialog_box (gpgme_protocol_t protocol,
gpgme_verify_result_t res,
const char *filename);
#ifdef __cplusplus
}
#endif
#endif /*GPGOL_COMMON_H*/
diff --git a/src/engine.c b/src/engine.c
index f0a1963..b06d513 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -1,786 +1,838 @@
/* engine.c - Crypto engine dispatcher
* Copyright (C) 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "common.h"
#include "engine.h"
#include "engine-gpgme.h"
#include "engine-assuan.h"
#define FILTER_BUFFER_SIZE 128 /* FIXME: Increase it after testing */
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
#define debug_filter (opt.enable_debug & DBG_FILTER)
#define debug_filter_extra (opt.enable_debug & DBG_FILTER_EXTRA)
/* This variable indicates whether the assuan engine is used. */
static int use_assuan;
/* Definition of the key object. */
struct engine_keyinfo_s
{
struct {
gpgme_key_t key;
} gpgme;
};
/* Definition of the filter object. This object shall only be
accessed by one thread. */
struct engine_filter_s
{
int use_assuan; /* The same as the global USE_ASSUAN. */
struct {
CRITICAL_SECTION lock; /* The lock for the this object. */
HANDLE condvar; /* Manual reset event signaled if LENGTH > 0. */
int nonblock; /* Put gpgme data cb in non blocking mode. */
size_t length; /* Number of bytes in BUFFER waiting to be
send down the pipe. */
char buffer[FILTER_BUFFER_SIZE];
int got_eof; /* End of file has been indicated. */
/* These objects are only in this structure because we
use this structure's lock to protect them. */
int ready; /* Set to true if the gpgme process has finished. */
HANDLE ready_event; /* And the corresponding event. */
gpg_error_t status; /* Status of the gpgme process. */
} in;
struct {
CRITICAL_SECTION lock; /* The lock for the this object. */
HANDLE condvar; /* Manual reset event signaled if LENGTH == 0. */
int nonblock; /* Put gpgme data cb in non blocking mode. */
size_t length; /* Number of bytes in BUFFER waiting to be
send back to the caller. */
char buffer[FILTER_BUFFER_SIZE];
} out;
+ /* Flag to push an extra LF out. */
+ int add_extra_lf;
+
/* The data sink as set by engine_create_filter. */
int (*outfnc) (void *, const void *, size_t);
void *outfncdata;
/* Objects to be released by engine_wait/cancel. */
struct gpgme_data_cbs cb_inbound; /* Callback structure for gpgme. */
struct gpgme_data_cbs cb_outbound; /* Ditto. */
gpgme_data_t indata; /* Input data. */
gpgme_data_t outdata; /* Output data. */
void *cancel_data; /* Used by engine_cancel. */
};
static void
take_in_lock (engine_filter_t filter, const char *func)
{
EnterCriticalSection (&filter->in.lock);
if (debug_filter_extra)
log_debug ("%s:%s: in.lock taken\n", SRCNAME, func);
}
static void
release_in_lock (engine_filter_t filter, const char *func)
{
LeaveCriticalSection (&filter->in.lock);
if (debug_filter_extra)
log_debug ("%s:%s: in.lock released\n", SRCNAME, func);
}
static void
take_out_lock (engine_filter_t filter, const char *func)
{
EnterCriticalSection (&filter->out.lock);
if (debug_filter_extra)
log_debug ("%s:%s: out.lock taken\n", SRCNAME, func);
}
static void
release_out_lock (engine_filter_t filter, const char *func)
{
LeaveCriticalSection (&filter->out.lock);
if (debug_filter_extra)
log_debug ("%s:%s: out.lock released\n", SRCNAME, func);
}
/* Create a new filter object. */
static engine_filter_t
create_filter (void)
{
engine_filter_t filter;
filter = xcalloc (1, sizeof *filter);
InitializeCriticalSection (&filter->in.lock);
filter->in.condvar = CreateEvent (NULL, TRUE, 0, NULL);
if (!filter->in.condvar)
log_error_w32 (-1, "%s:%s: create in.condvar failed", SRCNAME, __func__);
InitializeCriticalSection (&filter->out.lock);
filter->out.condvar = CreateEvent (NULL, TRUE, 0, NULL);
if (!filter->out.condvar)
log_error_w32 (-1, "%s:%s: create out.condvar failed", SRCNAME, __func__);
/* Create an automatic event (it only used one time so the type is
actually not important). */
filter->in.ready_event = CreateEvent (NULL, 0, 0, NULL);
if (!filter->in.ready_event)
log_error_w32 (-1, "%s:%s: CreateEvent failed", SRCNAME, __func__);
/* If we are using the assuan engine we need to make the gpgme read
callback non blocking. */
if (use_assuan)
{
filter->use_assuan = 1;
filter->in.nonblock = 1;
}
return filter;
}
static void
release_filter (engine_filter_t filter)
{
if (filter)
{
if (filter->in.condvar)
CloseHandle (filter->in.condvar);
if (filter->out.condvar)
CloseHandle (filter->out.condvar);
if (filter->in.ready_event)
CloseHandle (filter->in.ready_event);
gpgme_data_release (filter->indata);
gpgme_data_release (filter->outdata);
xfree (filter);
}
}
/* This read callback is used by GPGME to read data from a filter
object. The function should return the number of bytes read, 0 on
EOF, and -1 on error. If an error occurs, ERRNO should be set to
describe the type of the error. */
static ssize_t
filter_gpgme_read_cb (void *handle, void *buffer, size_t size)
{
engine_filter_t filter = handle;
int nbytes;
if (!filter || !buffer || !size)
{
errno = EINVAL;
return (ssize_t)(-1);
}
if (debug_filter)
log_debug ("%s:%s: enter\n", SRCNAME, __func__);
take_in_lock (filter, __func__);
while (!filter->in.length)
{
if (filter->in.got_eof || filter->in.ready)
{
release_in_lock (filter, __func__);
if (debug_filter)
log_debug ("%s:%s: returning EOF\n", SRCNAME, __func__);
return 0; /* Return EOF. */
}
release_in_lock (filter, __func__);
if (filter->in.nonblock)
{
errno = EAGAIN;
if (debug_filter_extra)
log_debug ("%s:%s: leave; result=EAGAIN\n", SRCNAME, __func__);
SwitchToThread ();
return -1;
}
if (debug_filter)
log_debug ("%s:%s: waiting for in.condvar\n", SRCNAME, __func__);
WaitForSingleObject (filter->in.condvar, 500);
take_in_lock (filter, __func__);
if (debug_filter)
log_debug ("%s:%s: continuing\n", SRCNAME, __func__);
}
if (debug_filter)
log_debug ("%s:%s: requested read size=%d (filter.in.length=%d)\n",
SRCNAME, __func__, (int)size, (int)filter->in.length);
nbytes = size < filter->in.length ? size : filter->in.length;
memcpy (buffer, filter->in.buffer, nbytes);
if (filter->in.length > nbytes)
memmove (filter->in.buffer, filter->in.buffer + nbytes,
filter->in.length - nbytes);
filter->in.length -= nbytes;
release_in_lock (filter, __func__);
if (debug_filter)
log_debug ("%s:%s: leave; result=%d\n",
SRCNAME, __func__, (int)nbytes);
return nbytes;
}
/* This write callback is used by GPGME to write data to the filter
object. The function should return the number of bytes written,
and -1 on error. If an error occurs, ERRNO should be set to
describe the type of the error. */
static ssize_t
filter_gpgme_write_cb (void *handle, const void *buffer, size_t size)
{
engine_filter_t filter = handle;
int nbytes;
if (!filter || !buffer || !size)
{
errno = EINVAL;
return (ssize_t)(-1);
}
if (debug_filter)
log_debug ("%s:%s: enter\n", SRCNAME, __func__);
take_out_lock (filter, __func__);
while (filter->out.length)
{
release_out_lock (filter, __func__);
if (filter->out.nonblock)
{
errno = EAGAIN;
if (debug_filter_extra)
log_debug ("%s:%s: leave; result=EAGAIN\n", SRCNAME, __func__);
return -1;
}
if (debug_filter)
log_debug ("%s:%s: waiting for out.condvar\n", SRCNAME, __func__);
WaitForSingleObject (filter->out.condvar, 500);
take_out_lock (filter, __func__);
if (debug_filter)
log_debug ("%s:%s: continuing\n", SRCNAME, __func__);
}
if (debug_filter)
log_debug ("%s:%s: requested write size=%d\n",
SRCNAME, __func__, (int)size);
nbytes = size < FILTER_BUFFER_SIZE ? size : FILTER_BUFFER_SIZE;
memcpy (filter->out.buffer, buffer, nbytes);
filter->out.length = nbytes;
release_out_lock (filter, __func__);
if (debug_filter)
log_debug ("%s:%s: leave; result=%d\n", SRCNAME, __func__, (int)nbytes);
return nbytes;
}
/* Store a cancel parameter into FILTER. Only use by the engine backends. */
void
engine_private_set_cancel (engine_filter_t filter, void *cancel_data)
{
filter->cancel_data = cancel_data;
}
/* This function is called by the gpgme backend to notify a filter
object about the final status of an operation. It may only be
called by the engine-gpgme.c module. */
void
engine_private_finished (engine_filter_t filter, gpg_error_t status)
{
if (!filter)
{
log_debug ("%s:%s: called without argument\n", SRCNAME, __func__);
return;
}
if (debug_filter)
log_debug ("%s:%s: filter %p: process terminated: %s <%s>\n",
SRCNAME, __func__, filter,
gpg_strerror (status), gpg_strsource (status));
take_in_lock (filter, __func__);
filter->in.ready = 1;
filter->in.status = status;
filter->cancel_data = NULL;
if (!SetEvent (filter->in.ready_event))
log_error_w32 (-1, "%s:%s: SetEvent failed", SRCNAME, __func__);
release_in_lock (filter, __func__);
if (debug_filter)
log_debug ("%s:%s: leaving\n", SRCNAME, __func__);
}
/* Initialize the engine dispatcher. */
int
engine_init (void)
{
gpg_error_t err;
err = op_gpgme_basic_init ();
if (err)
return err;
err = op_assuan_init ();
if (err)
{
use_assuan = 0;
MessageBox (NULL,
_("The user interface server is not available or does "
"not work. Using an internal user interface.\n\n"
"This is limited to the PGP/MIME protocol and "
"thus S/MIME protected message are not readable."),
_("GpgOL"), MB_ICONWARNING|MB_OK);
err = op_gpgme_init ();
}
else
use_assuan = 1;
return err;
}
/* Shutdown the engine dispatcher. */
void
engine_deinit (void)
{
op_assuan_deinit ();
op_gpgme_deinit ();
}
/* Filter the INDATA of length INDATA and write the output using
OUTFNC. OUTFNCDATA is passed as first argument to OUTFNC, followed
by the data to be written and its length. FILTER is an object
returned for example by engine_encrypt_start. The function returns
0 on success or an error code on error.
Passing INDATA as NULL and INDATALEN as 0, the filter will be
flushed, that is all remaining stuff will be written to OUTFNC.
This indicates EOF and the filter won't accept anymore input. */
int
engine_filter (engine_filter_t filter, const void *indata, size_t indatalen)
{
gpg_error_t err;
int nbytes;
if (debug_filter)
log_debug ("%s:%s: enter; filter=%p\n", SRCNAME, __func__, filter);
/* Our implementation is for now straightforward without any
additional buffer filling etc. */
if (!filter || !filter->outfnc)
return gpg_error (GPG_ERR_INV_VALUE);
if (filter->in.length > FILTER_BUFFER_SIZE
|| filter->out.length > FILTER_BUFFER_SIZE)
return gpg_error (GPG_ERR_BUG);
if (filter->in.got_eof)
return gpg_error (GPG_ERR_CONFLICT); /* EOF has already been indicated. */
if (debug_filter)
log_debug ("%s:%s: indata=%p indatalen=%d outfnc=%p\n",
SRCNAME, __func__, indata, (int)indatalen, filter->outfnc);
for (;;)
{
/* If there is something to write out, do this now to make space
for more data. */
take_out_lock (filter, __func__);
while (filter->out.length)
{
if (debug_filter)
log_debug ("%s:%s: pushing %d bytes to the outfnc\n",
SRCNAME, __func__, filter->out.length);
nbytes = filter->outfnc (filter->outfncdata,
filter->out.buffer, filter->out.length);
if (nbytes == -1)
{
if (debug_filter)
log_debug ("%s:%s: error writing data\n", SRCNAME, __func__);
release_out_lock (filter, __func__);
return gpg_error (GPG_ERR_EIO);
}
assert (nbytes <= filter->out.length && nbytes >= 0);
if (nbytes < filter->out.length)
memmove (filter->out.buffer, filter->out.buffer + nbytes,
filter->out.length - nbytes);
filter->out.length -= nbytes;
}
if (!PulseEvent (filter->out.condvar))
log_error_w32 (-1, "%s:%s: PulseEvent(out) failed", SRCNAME, __func__);
release_out_lock (filter, __func__);
take_in_lock (filter, __func__);
if (!indata && !indatalen)
{
filter->in.got_eof = 1;
/* Flush requested. Tell the output function to also flush. */
nbytes = filter->outfnc (filter->outfncdata, NULL, 0);
if (nbytes == -1)
{
log_debug ("%s:%s: error flushing data\n", SRCNAME, __func__);
err = gpg_error (GPG_ERR_EIO);
}
else
err = 0;
release_in_lock (filter, __func__);
return err;
}
/* Fill the input buffer, relinquish control to the callback
processor and loop until all input data has been
processed. */
if (!filter->in.length && indatalen)
{
filter->in.length = (indatalen > FILTER_BUFFER_SIZE
? FILTER_BUFFER_SIZE : indatalen);
memcpy (filter->in.buffer, indata, filter->in.length);
indata += filter->in.length;
indatalen -= filter->in.length;
}
if (!filter->in.length || (filter->in.ready && !filter->out.length))
{
release_in_lock (filter, __func__);
err = 0;
break; /* the loop. */
}
if (!PulseEvent (filter->in.condvar))
log_error_w32 (-1, "%s:%s: PulseEvent(in) failed", SRCNAME, __func__);
release_in_lock (filter, __func__);
Sleep (50);
}
if (debug_filter)
log_debug ("%s:%s: leave; err=%d\n", SRCNAME, __func__, err);
return err;
}
/* Dummy data sink used if caller does not need an output
function. */
static int
dummy_outfnc (void *opaque, const void *data, size_t datalen)
{
(void)opaque;
(void)data;
return (int)datalen;
}
/* Create a new filter object which uses OUTFNC as its data sink. If
OUTFNC is called with NULL/0 for the data to be written, the
function should do a flush. OUTFNC is expected to return the
number of bytes actually written or -1 on error. It may return 0
to indicate that no data has been written and the caller shall try
again. OUTFNC and OUTFNCDATA are internally used by the engine
even after the call to this function. There lifetime only ends
after an engine_wait or engine_cancel. */
int
engine_create_filter (engine_filter_t *r_filter,
int (*outfnc) (void *, const void *, size_t),
void *outfncdata)
{
gpg_error_t err;
engine_filter_t filter;
filter = create_filter ();
filter->cb_inbound.read = filter_gpgme_read_cb;
filter->cb_outbound.write = filter_gpgme_write_cb;
filter->outfnc = outfnc? outfnc : dummy_outfnc;
filter->outfncdata = outfncdata;
err = gpgme_data_new_from_cbs (&filter->indata,
&filter->cb_inbound, filter);
if (err)
goto failure;
err = gpgme_data_new_from_cbs (&filter->outdata,
&filter->cb_outbound, filter);
if (err)
goto failure;
*r_filter = filter;
return 0;
failure:
release_filter (filter);
return err;
}
+/* Set the FILTER in a mode which pushes an extra lineffed out. */
+void
+engine_request_exra_lf (engine_filter_t filter)
+{
+ filter->add_extra_lf = 1;
+}
+
/* Wait for FILTER to finish. Returns 0 on success. FILTER is not
valid after the function has returned success. */
int
engine_wait (engine_filter_t filter)
{
gpg_error_t err;
int more;
+ int nbytes;
if (!filter || !filter->outfnc)
return gpg_error (GPG_ERR_INV_VALUE);
/* If we are here, engine_filter is not anymore called but there is
likely stuff in the output buffer which needs to be written
out. */
/* Argh, Busy waiting. As soon as we change fromCritical Sections
to a kernel based objects we should use WaitOnMultipleObjects to
wait for the out.lock as well as for the ready_event. */
do
{
more = 0;
take_out_lock (filter, __func__);
if (filter->out.length)
{
- int nbytes;
-
nbytes = filter->outfnc (filter->outfncdata,
filter->out.buffer, filter->out.length);
if (nbytes < 0)
{
log_error ("%s:%s: error writing data\n", SRCNAME, __func__);
release_out_lock (filter, __func__);
return gpg_error (GPG_ERR_EIO);
}
assert (nbytes <= filter->out.length && nbytes >= 0);
if (nbytes < filter->out.length)
memmove (filter->out.buffer, filter->out.buffer + nbytes,
filter->out.length - nbytes);
filter->out.length -= nbytes;
if (filter->out.length)
{
if (debug_filter_extra)
log_debug ("%s:%s: still %d pending bytes for outfnc\n",
SRCNAME, __func__, filter->out.length);
more = 1;
}
}
if (!PulseEvent (filter->out.condvar))
log_error_w32 (-1, "%s:%s: PulseEvent(out) failed", SRCNAME, __func__);
release_out_lock (filter, __func__);
take_in_lock (filter, __func__);
if (!filter->in.ready)
more = 1;
release_in_lock (filter, __func__);
if (more)
Sleep (50);
}
while (more);
+ /* If requested write an extra LF, so that the MIME parser sees a
+ complete line. */
+ if (filter->add_extra_lf)
+ {
+ int extra_written = 0;
+ do
+ {
+ more = 0;
+ take_out_lock (filter, __func__);
+ if (!extra_written)
+ {
+ nbytes = filter->outfnc (filter->outfncdata, "\n", 1);
+ if (nbytes < 0)
+ {
+ log_error ("%s:%s: error writing extra lf\n",
+ SRCNAME, __func__);
+ release_out_lock (filter, __func__);
+ return gpg_error (GPG_ERR_EIO);
+ }
+ if (!nbytes)
+ {
+ if (debug_filter_extra)
+ log_debug ("%s:%s: extra lf still pending for outfnc\n",
+ SRCNAME, __func__);
+ more = 1;
+ }
+ else
+ extra_written = 1;
+ }
+ if (!PulseEvent (filter->out.condvar))
+ log_error_w32 (-1, "%s:%s: PulseEvent(out) failed",
+ SRCNAME, __func__);
+ release_out_lock (filter, __func__);
+ take_in_lock (filter, __func__);
+ if (!filter->in.ready)
+ more = 1;
+ release_in_lock (filter, __func__);
+ if (more)
+ Sleep (50);
+ }
+ while (more);
+ }
+
if (WaitForSingleObject (filter->in.ready_event, INFINITE) != WAIT_OBJECT_0)
{
log_error_w32 (-1, "%s:%s: WFSO failed", SRCNAME, __func__);
return gpg_error (GPG_ERR_GENERAL);
}
err = filter->in.status;
log_debug ("%s:%s: filter %p ready: %s", SRCNAME, __func__,
filter, gpg_strerror (err));
if (!err)
release_filter (filter);
return err;
}
/* Cancel FILTER. */
void
engine_cancel (engine_filter_t filter)
{
void *cancel_data;
if (!filter)
return;
take_in_lock (filter, __func__);
cancel_data = filter->cancel_data;
filter->cancel_data = NULL;
filter->in.ready = 1;
release_in_lock (filter, __func__);
if (cancel_data)
{
log_debug ("%s:%s: filter %p: sending cancel command to backend",
SRCNAME, __func__, filter);
if (filter->use_assuan)
engine_assuan_cancel (cancel_data);
else
engine_gpgme_cancel (cancel_data);
if (WaitForSingleObject (filter->in.ready_event, INFINITE)
!= WAIT_OBJECT_0)
log_error_w32 (-1, "%s:%s: WFSO failed", SRCNAME, __func__);
else
log_debug ("%s:%s: filter %p: backend has been canceled",
SRCNAME, __func__, filter);
}
log_debug ("%s:%s: filter %p: canceled", SRCNAME, __func__, filter);
release_filter (filter);
}
/* Start an encryption operation to all RECIPEINTS using PROTOCOL
RECIPIENTS is a NULL terminated array of rfc2822 addresses. FILTER
is an object created by engine_create_filter. The caller needs to
call engine_wait to finish the operation. A filter object may not
be reused after having been used through this function. However,
the lifetime of the filter object lasts until the final engine_wait
or engine_cancel. On return the protocol to be used is stored at
R_PROTOCOL. */
int
engine_encrypt_start (engine_filter_t filter, HWND hwnd,
protocol_t req_protocol, char **recipients,
protocol_t *r_protocol)
{
gpg_error_t err;
protocol_t used_protocol;
*r_protocol = req_protocol;
if (filter->use_assuan)
{
err = op_assuan_encrypt (req_protocol, filter->indata, filter->outdata,
filter, hwnd, recipients, &used_protocol);
if (!err)
*r_protocol = used_protocol;
}
else
err = op_gpgme_encrypt (req_protocol, filter->indata, filter->outdata,
filter, hwnd, recipients);
return err;
}
/* Start an detached signing operation. FILTER is an object created
by engine_create_filter. The caller needs to call engine_wait to
finish the operation. A filter object may not be reused after
having been used through this function. However, the lifetime of
the filter object lasts until the final engine_wait or
engine_cancel. SENDER is the sender's mailbox or NULL. On return
the protocol to be used is stored at R_PROTOCOL. */
int
engine_sign_start (engine_filter_t filter, HWND hwnd, protocol_t protocol,
const char *sender, protocol_t *r_protocol)
{
gpg_error_t err;
protocol_t used_protocol;
if (filter->use_assuan)
{
err = op_assuan_sign (protocol, filter->indata, filter->outdata,
filter, hwnd, sender, &used_protocol);
if (!err)
*r_protocol = used_protocol;
}
else
{
err = op_gpgme_sign (protocol, filter->indata, filter->outdata,
filter, hwnd);
if (!err)
*r_protocol = (protocol == GPGME_PROTOCOL_UNKNOWN?
GPGME_PROTOCOL_OpenPGP : protocol);
}
return err;
}
/* Start an decrypt operation. FILTER is an object created by
engine_create_filter. The caller needs to call engine_wait to
finish the operation. A filter object may not be reused after
having been used through this function. However, the lifetime of
the filter object lasts until the final engine_wait or
engine_cancel. */
int
engine_decrypt_start (engine_filter_t filter, HWND hwnd, protocol_t protocol,
int with_verify)
{
gpg_error_t err;
if (filter->use_assuan)
err = op_assuan_decrypt (protocol, filter->indata, filter->outdata,
filter, hwnd, with_verify);
else
err = op_gpgme_decrypt (protocol, filter->indata, filter->outdata,
filter, hwnd, with_verify);
return err;
}
/* Start a verify operation. FILTER is an object created by
engine_create_filter; an output function is not required. SIGNATURE
is the detached signature or NULL if FILTER delivers an opaque
signature. The caller needs to call engine_wait to finish the
operation. A filter object may not be reused after having been
used through this function. However, the lifetime of the filter
object lasts until the final engine_wait or engine_cancel. */
int
engine_verify_start (engine_filter_t filter, HWND hwnd, const char *signature,
size_t sig_len, protocol_t protocol)
{
gpg_error_t err;
if (!signature && !filter->use_assuan)
{
log_error ("%s:%s: opaque signatures are not supported "
"by the internal backend\n",
SRCNAME, __func__);
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
if (filter->use_assuan && !signature)
err = op_assuan_verify (protocol, filter->indata, NULL, 0,
filter->outdata, filter, hwnd);
else if (filter->use_assuan)
err = op_assuan_verify (protocol, filter->indata, signature, sig_len,
NULL, filter, hwnd);
else
err = op_gpgme_verify (protocol, filter->indata, signature, sig_len,
filter, hwnd);
return err;
}
/* Fire up the key manager. Returns 0 on success. */
int
engine_start_keymanager (HWND hwnd)
{
if (use_assuan)
return op_assuan_start_keymanager (hwnd);
else
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
diff --git a/src/engine.h b/src/engine.h
index cef58c4..ee8f99f 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -1,84 +1,85 @@
/* engine.h - Crypto engine dispatcher
* Copyright (C) 2007 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifndef GPGOL_ENGINE_H
#define GPGOL_ENGINE_H 1
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
typedef enum
{
OP_SIG_NORMAL = 0,
OP_SIG_DETACH = 1,
OP_SIG_CLEAR = 2
}
engine_sigtype_t;
/* The key info object. */
struct engine_keyinfo_s;
typedef struct engine_keyinfo_s *engine_keyinfo_t;
/* The filter object. */
struct engine_filter_s;
typedef struct engine_filter_s *engine_filter_t;
/*-- engine.c -- */
int engine_init (void);
void engine_deinit (void);
void engine_private_set_cancel (engine_filter_t filter, void *cancel_data);
void engine_private_finished (engine_filter_t filter, gpg_error_t status);
int engine_filter (engine_filter_t filter,
const void *indata, size_t indatalen);
int engine_create_filter (engine_filter_t *r_filter,
int (*outfnc) (void *, const void *, size_t),
void *outfncdata);
+void engine_request_exra_lf (engine_filter_t filter);
int engine_wait (engine_filter_t filter);
void engine_cancel (engine_filter_t filter);
int engine_encrypt_start (engine_filter_t filter, HWND hwnd,
protocol_t req_protocol, char **recipients,
protocol_t *r_protocol);
int engine_sign_start (engine_filter_t filter, HWND hwnd, protocol_t protocol,
const char *sender, protocol_t *r_protocol);
int engine_decrypt_start (engine_filter_t filter, HWND hwnd,
protocol_t protocol, int with_verify);
int engine_verify_start (engine_filter_t filter, HWND hwnd,
const char *signature, size_t sig_len,
protocol_t protocol);
int engine_start_keymanager (HWND hwnd);
#ifdef __cplusplus
}
#endif
#endif /*GPGOL_ENGINE_H*/
diff --git a/src/ext-commands.cpp b/src/ext-commands.cpp
index bcd5a96..97a1eb7 100644
--- a/src/ext-commands.cpp
+++ b/src/ext-commands.cpp
@@ -1,968 +1,979 @@
/* ext-commands.cpp - Subclass impl of IExchExtCommands
* Copyright (C) 2004, 2005, 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <windows.h>
#include "mymapi.h"
#include "mymapitags.h"
#include "myexchext.h"
#include "common.h"
#include "display.h"
#include "msgcache.h"
#include "mapihelp.h"
#include "dialogs.h" /* For IDB_foo. */
#include "olflange-def.h"
#include "olflange.h"
#include "ol-ext-callback.h"
#include "message.h"
#include "engine.h"
#include "ext-commands.h"
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
/* An object to store information about active (installed) toolbar
buttons. */
struct toolbar_info_s
{
toolbar_info_t next;
UINT button_id;/* The ID of the button as assigned by Outlook. */
UINT bitmap; /* The bitmap of the button. */
UINT cmd_id; /* The ID of the command to send on a click. */
const char *desc;/* The description text. */
ULONG context; /* Context under which this entry will be used. */
};
/* Keep copies of some bitmaps. */
static int bitmaps_initialized;
static HBITMAP my_check_bitmap, my_uncheck_bitmap;
static void add_menu (LPEXCHEXTCALLBACK eecb,
UINT FAR *pnCommandIDBase, ...)
#if __GNUC__ >= 4
__attribute__ ((sentinel))
#endif
;
/* Wrapper around UlRelease with error checking. */
static void
ul_release (LPVOID punk, const char *func, int lnr)
{
ULONG res;
if (!punk)
return;
res = UlRelease (punk);
if (opt.enable_debug & DBG_MEMORY)
log_debug ("%s:%s:%d: UlRelease(%p) had %lu references\n",
SRCNAME, func, lnr, punk, res);
}
/* Constructor */
GpgolExtCommands::GpgolExtCommands (GpgolExt* pParentInterface)
{
m_pExchExt = pParentInterface;
m_lRef = 0;
m_lContext = 0;
m_nCmdProtoAuto = 0;
m_nCmdProtoPgpmime = 0;
m_nCmdProtoSmime = 0;
m_nCmdEncrypt = 0;
m_nCmdDecrypt = 0;
m_nCmdSign = 0;
m_nCmdShowInfo = 0;
m_nCmdCheckSig = 0;
m_nCmdKeyManager = 0;
m_nCmdDebug1 = 0;
m_nCmdDebug2 = 0;
m_toolbar_info = NULL;
m_hWnd = NULL;
if (!bitmaps_initialized)
{
my_uncheck_bitmap = get_system_check_bitmap (0);
my_check_bitmap = get_system_check_bitmap (1);
bitmaps_initialized = 1;
}
}
/* Destructor */
GpgolExtCommands::~GpgolExtCommands (void)
{
while (m_toolbar_info)
{
toolbar_info_t tmp = m_toolbar_info->next;
xfree (m_toolbar_info);
m_toolbar_info = tmp;
}
}
STDMETHODIMP
GpgolExtCommands::QueryInterface (REFIID riid, LPVOID FAR * ppvObj)
{
*ppvObj = NULL;
if ((riid == IID_IExchExtCommands) || (riid == IID_IUnknown)) {
*ppvObj = (LPVOID)this;
AddRef ();
return S_OK;
}
return E_NOINTERFACE;
}
/* Add a new menu. The variable entries are made up of pairs of
strings and UINT *. A NULL is used to terminate this list. An
empty string is translated to a separator menu item. One level of
submenus are supported. */
static void
add_menu (LPEXCHEXTCALLBACK eecb, UINT FAR *pnCommandIDBase, ...)
{
va_list arg_ptr;
HMENU mainmenu, submenu, menu;
const char *string;
UINT *cmdptr;
va_start (arg_ptr, pnCommandIDBase);
/* We put all new entries into the tools menu. To make this work we
need to pass the id of an existing item from that menu. */
eecb->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &mainmenu, NULL, NULL, 0);
menu = mainmenu;
submenu = NULL;
while ( (string = va_arg (arg_ptr, const char *)) )
{
cmdptr = va_arg (arg_ptr, UINT*);
if (!*string)
; /* Ignore this entry. */
else if (*string == '@' && !string[1])
AppendMenu (menu, MF_SEPARATOR, 0, NULL);
else if (*string == '>')
{
submenu = CreatePopupMenu ();
AppendMenu (menu, MF_STRING|MF_POPUP, (UINT_PTR)submenu, string+1);
menu = submenu;
}
else if (*string == '<')
{
menu = mainmenu;
submenu = NULL;
}
else
{
AppendMenu (menu, MF_STRING, *pnCommandIDBase, string);
if (menu == submenu)
SetMenuItemBitmaps (menu, *pnCommandIDBase, MF_BYCOMMAND,
my_uncheck_bitmap, my_check_bitmap);
if (cmdptr)
*cmdptr = *pnCommandIDBase;
(*pnCommandIDBase)++;
}
}
va_end (arg_ptr);
}
static void
check_menu (LPEXCHEXTCALLBACK eecb, UINT menu_id, int checked)
{
HMENU menu;
eecb->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &menu, NULL, NULL, 0);
- log_debug ("check_menu: eecb=%p menu_id=%u checked=%d -> menu=%p\n",
- eecb, menu_id, checked, menu);
+ if (debug_commands)
+ log_debug ("check_menu: eecb=%p menu_id=%u checked=%d -> menu=%p\n",
+ eecb, menu_id, checked, menu);
CheckMenuItem (menu, menu_id,
MF_BYCOMMAND | (checked?MF_CHECKED:MF_UNCHECKED));
}
void
GpgolExtCommands::add_toolbar (LPTBENTRY tbearr, UINT n_tbearr, ...)
{
va_list arg_ptr;
const char *desc;
UINT bmapid;
UINT cmdid;
int tbeidx;
toolbar_info_t tb_info;
int rc;
for (tbeidx = n_tbearr-1; tbeidx > -1; tbeidx--)
if (tbearr[tbeidx].tbid == EETBID_STANDARD)
break;
if (!(tbeidx > -1))
{
log_error ("standard toolbar not found");
return;
}
SendMessage (tbearr[tbeidx].hwnd, TB_BUTTONSTRUCTSIZE,
(WPARAM)(int)sizeof (TBBUTTON), 0);
va_start (arg_ptr, n_tbearr);
while ( (desc = va_arg (arg_ptr, const char *)) )
{
bmapid = va_arg (arg_ptr, UINT);
cmdid = va_arg (arg_ptr, UINT);
if (!*desc)
; /* Empty description - ignore this item. */
else
{
TBADDBITMAP tbab;
tb_info = (toolbar_info_t)xcalloc (1, sizeof *tb_info);
tb_info->button_id = tbearr[tbeidx].itbbBase++;
tbab.hInst = glob_hinst;
tbab.nID = bmapid;
rc = SendMessage (tbearr[tbeidx].hwnd, TB_ADDBITMAP,1,(LPARAM)&tbab);
if (rc == -1)
log_error_w32 (-1, "TB_ADDBITMAP failed for `%s'", desc);
tb_info->bitmap = rc;
tb_info->cmd_id = cmdid;
tb_info->desc = desc;
tb_info->context = m_lContext;
tb_info->next = m_toolbar_info;
m_toolbar_info = tb_info;
- log_debug ("%s:%s: ctx=%lx button_id=%d cmd_id=%d '%s'\n",
- SRCNAME, __func__, m_lContext,
- tb_info->button_id, tb_info->cmd_id, tb_info->desc);
+ if (debug_commands)
+ log_debug ("%s:%s: ctx=%lx button_id=%d cmd_id=%d '%s'\n",
+ SRCNAME, __func__, m_lContext,
+ tb_info->button_id, tb_info->cmd_id, tb_info->desc);
}
}
va_end (arg_ptr);
}
/* Called by Exchange to install commands and toolbar buttons. Returns
S_FALSE to signal Exchange to continue calling extensions. */
STDMETHODIMP
GpgolExtCommands::InstallCommands (
LPEXCHEXTCALLBACK eecb, // The Exchange Callback Interface.
HWND hWnd, // The window handle to the main window
// of context.
HMENU hMenu, // The menu handle to main menu of context.
UINT FAR *pnCommandIDBase, // The base command id.
LPTBENTRY pTBEArray, // The array of toolbar button entries.
UINT nTBECnt, // The count of button entries in array.
ULONG lFlags) // reserved
{
HRESULT hr;
m_hWnd = hWnd;
LPDISPATCH pDisp;
DISPID dispid;
DISPID dispid_put = DISPID_PROPERTYPUT;
DISPPARAMS dispparams;
VARIANT aVariant;
int force_encrypt = 0;
- log_debug ("%s:%s: context=%s flags=0x%lx\n", SRCNAME, __func__,
- ext_context_name (m_lContext), lFlags);
+ if (debug_commands)
+ log_debug ("%s:%s: context=%s flags=0x%lx\n", SRCNAME, __func__,
+ ext_context_name (m_lContext), lFlags);
/* Outlook 2003 sometimes displays the plaintext and sometimes the
original undecrypted text when doing a reply. This seems to
depend on the size of the message; my guess it that only short
messages are locally saved in the process and larger ones are
fetched again from the backend - or the other way around.
Anyway, we can't rely on that and thus me make sure to update the
Body object right here with our own copy of the plaintext. To
match the text we use the ConversationIndex property.
Unfortunately there seems to be no way of resetting the saved
property after updating the body, thus even without entering a
single byte the user will be asked when cancelling a reply
whether he really wants to do that.
Note, that we can't optimize the code here by first reading the
body because this would pop up the security window, telling the
user that someone is trying to read this data.
*/
if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
LPMDB mdb = NULL;
LPMESSAGE message = NULL;
/* Note that for read and send the object returned by the
outlook extension callback is of class 43 (MailItem) so we
only need to ask for Body then. */
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (FAILED(hr))
log_debug ("%s:%s: getObject failed: hr=%#lx\n", SRCNAME,__func__,hr);
else if (!opt.compat.no_msgcache)
{
const char *body;
char *key = NULL;
size_t keylen = 0;
void *refhandle = NULL;
pDisp = find_outlook_property (eecb, "ConversationIndex", &dispid);
if (pDisp)
{
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
aVariant.bstrVal = NULL;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, &dispparamsNoArgs,
&aVariant, NULL, NULL);
if (hr != S_OK)
log_debug ("%s:%s: retrieving ConversationIndex failed: %#lx",
SRCNAME, __func__, hr);
else if (aVariant.vt != VT_BSTR)
log_debug ("%s:%s: ConversationIndex is not a string (%d)",
SRCNAME, __func__, aVariant.vt);
else if (aVariant.bstrVal)
{
char *p;
key = wchar_to_utf8 (aVariant.bstrVal);
log_debug ("%s:%s: ConversationIndex is `%s'",
SRCNAME, __func__, key);
/* The key is a hex string. Convert it to binary. */
for (keylen=0,p=key; hexdigitp(p) && hexdigitp(p+1); p += 2)
((unsigned char*)key)[keylen++] = xtoi_2 (p);
SysFreeString (aVariant.bstrVal);
}
pDisp->Release();
pDisp = NULL;
}
if (key && keylen
&& (body = msgcache_get (key, keylen, &refhandle))
&& (pDisp = find_outlook_property (eecb, "Body", &dispid)))
{
#if 1
dispparams.cNamedArgs = 1;
dispparams.rgdispidNamedArgs = &dispid_put;
dispparams.cArgs = 1;
dispparams.rgvarg = &aVariant;
dispparams.rgvarg[0].vt = VT_LPWSTR;
dispparams.rgvarg[0].bstrVal = utf8_to_wchar (body);
hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dispparams,
NULL, NULL, NULL);
xfree (dispparams.rgvarg[0].bstrVal);
log_debug ("%s:%s: PROPERTYPUT(body) result -> %#lx\n",
SRCNAME, __func__, hr);
#else
log_window_hierarchy (hWnd, "%s:%s:%d: Windows hierarchy:",
SRCNAME, __func__, __LINE__);
#endif
pDisp->Release();
pDisp = NULL;
/* Because we found the plaintext in the cache we can assume
that the orginal message has been encrypted and thus we
now set a flag to make sure that by default the reply
gets encrypted too. */
force_encrypt = 1;
}
msgcache_unref (refhandle);
xfree (key);
}
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
/* Now install menu and toolbar items. */
if (m_lContext == EECONTEXT_READNOTEMESSAGE)
{
int need_dvm = 0;
LPMDB mdb = NULL;
LPMESSAGE message = NULL;
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (FAILED(hr))
log_debug ("%s:%s: getObject failed: hr=%#lx\n", SRCNAME, __func__, hr);
else
{
switch (mapi_get_message_type (message))
{
case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
case MSGTYPE_GPGOL_PGP_MESSAGE:
need_dvm = 1;
break;
default:
break;
}
}
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
/* We always enable the verify button as it might be useful on
an already decrypted message. */
add_menu (eecb, pnCommandIDBase,
"@", NULL,
need_dvm? _("&Decrypt and verify message"):"", &m_nCmdDecrypt,
_("&Verify signature"), &m_nCmdCheckSig,
_("&Display crypto information"), &m_nCmdShowInfo,
"@", NULL,
opt.enable_debug? "Debug-1 (open_inspector)":"", &m_nCmdDebug1,
opt.enable_debug? "Debug-2 (change msg class)":"", &m_nCmdDebug2,
NULL);
}
else if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
add_menu (eecb, pnCommandIDBase,
"@", NULL,
_(">GnuPG protocol"), NULL,
_("auto"), &m_nCmdProtoAuto,
_("PGP/MIME"),&m_nCmdProtoPgpmime,
_("S/MIME"), &m_nCmdProtoSmime,
"<", NULL,
_("&encrypt message with GnuPG"), &m_nCmdEncrypt,
_("&sign message with GnuPG"), &m_nCmdSign,
NULL );
add_toolbar (pTBEArray, nTBECnt,
_("Encrypt message with GnuPG"), IDB_ENCRYPT, m_nCmdEncrypt,
_("Sign message with GnuPG"), IDB_SIGN, m_nCmdSign,
NULL, 0, 0);
m_pExchExt->m_protoSelection = opt.default_protocol;
switch (opt.default_protocol)
{
case PROTOCOL_OPENPGP:
check_menu (eecb, m_nCmdProtoAuto, FALSE);
check_menu (eecb, m_nCmdProtoPgpmime, TRUE);
check_menu (eecb, m_nCmdProtoSmime, FALSE);
break;
case PROTOCOL_SMIME:
check_menu (eecb, m_nCmdProtoAuto, FALSE);
check_menu (eecb, m_nCmdProtoPgpmime, FALSE);
check_menu (eecb, m_nCmdProtoSmime, TRUE);
break;
default:
check_menu (eecb, m_nCmdProtoAuto, TRUE);
check_menu (eecb, m_nCmdProtoPgpmime, FALSE);
check_menu (eecb, m_nCmdProtoSmime, FALSE);
break;
}
m_pExchExt->m_gpgEncrypt = opt.encrypt_default;
m_pExchExt->m_gpgSign = opt.sign_default;
if (force_encrypt)
m_pExchExt->m_gpgEncrypt = true;
check_menu (eecb, m_nCmdEncrypt, m_pExchExt->m_gpgEncrypt);
check_menu (eecb, m_nCmdSign, m_pExchExt->m_gpgSign);
}
else if (m_lContext == EECONTEXT_VIEWER)
{
add_menu (eecb, pnCommandIDBase,
"@", NULL,
_("GnuPG Certificate &Manager"), &m_nCmdKeyManager,
NULL);
add_toolbar (pTBEArray, nTBECnt,
_("Open the certificate manager"), IDB_KEY_MANAGER, m_nCmdKeyManager,
NULL, 0, 0);
}
return S_FALSE;
}
/* Called by Exchange when a user selects a command. Return value:
S_OK if command is handled, otherwise S_FALSE. */
STDMETHODIMP
GpgolExtCommands::DoCommand (LPEXCHEXTCALLBACK eecb, UINT nCommandID)
{
HRESULT hr;
HWND hwnd = NULL;
LPMESSAGE message = NULL;
LPMDB mdb = NULL;
if (FAILED (eecb->GetWindow (&hwnd)))
hwnd = NULL;
- log_debug ("%s:%s: commandID=%u (%#x) context=%s hwnd=%p\n",
- SRCNAME, __func__, nCommandID, nCommandID,
- ext_context_name (m_lContext), hwnd);
+ if (debug_commands)
+ log_debug ("%s:%s: commandID=%u (%#x) context=%s hwnd=%p\n",
+ SRCNAME, __func__, nCommandID, nCommandID,
+ ext_context_name (m_lContext), hwnd);
if (nCommandID == SC_CLOSE && m_lContext == EECONTEXT_READNOTEMESSAGE)
{
/* This is the system close command. Replace it with our own to
avoid the "save changes" query, apparently induced by OL
internal syncronisation of our SetWindowText message with its
own OOM (in this case Body). */
LPDISPATCH pDisp;
DISPID dispid;
DISPPARAMS dispparams;
VARIANT aVariant;
- log_debug ("%s:%s: command Close called\n", SRCNAME, __func__);
+ if (debug_commands)
+ log_debug ("%s:%s: command Close called\n", SRCNAME, __func__);
pDisp = find_outlook_property (eecb, "Close", &dispid);
if (pDisp)
{
/* Note that there is a report on the Net from 2005 by Amit
Joshi where he claims that in Outlook XP olDiscard does
not work but is treated like olSave. */
dispparams.rgvarg = &aVariant;
dispparams.rgvarg[0].vt = VT_INT;
dispparams.rgvarg[0].intVal = 1; /* olDiscard */
dispparams.cArgs = 1;
dispparams.cNamedArgs = 0;
hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
NULL, NULL, NULL);
pDisp->Release();
pDisp = NULL;
if (hr == S_OK)
{
log_debug ("%s:%s: invoking Close succeeded", SRCNAME,__func__);
message_wipe_body_cruft (eecb);
return S_OK; /* We handled the close command. */
}
log_debug ("%s:%s: invoking Close failed: %#lx",
SRCNAME, __func__, hr);
}
else
log_debug ("%s:%s: invoking Close failed: no Close method)",
SRCNAME, __func__);
message_wipe_body_cruft (eecb);
/* Closing on our own failed - pass it on. */
return S_FALSE;
}
else if (nCommandID == EECMDID_ComposeReplyToSender)
{
- log_debug ("%s:%s: command Reply called\n", SRCNAME, __func__);
+ if (debug_commands)
+ log_debug ("%s:%s: command Reply called\n", SRCNAME, __func__);
/* What we might want to do is to call Reply, then GetInspector
and then Activate - this allows us to get full control over
the quoted message and avoids the ugly msgcache. */
return S_FALSE; /* Pass it on. */
}
else if (nCommandID == EECMDID_ComposeReplyToAll)
{
- log_debug ("%s:%s: command ReplyAll called\n", SRCNAME, __func__);
+ if (debug_commands)
+ log_debug ("%s:%s: command ReplyAll called\n", SRCNAME, __func__);
return S_FALSE; /* Pass it on. */
}
else if (nCommandID == EECMDID_ComposeForward)
{
- log_debug ("%s:%s: command Forward called\n", SRCNAME, __func__);
+ if (debug_commands)
+ log_debug ("%s:%s: command Forward called\n", SRCNAME, __func__);
return S_FALSE; /* Pass it on. */
}
else if (nCommandID == m_nCmdDecrypt
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
log_debug ("%s:%s: command Decrypt called\n", SRCNAME, __func__);
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
message_decrypt (message, mapi_get_message_type (message), 1, hwnd);
message_display_handler (eecb, hwnd);
}
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
else if (nCommandID == m_nCmdCheckSig
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
log_debug ("%s:%s: command CheckSig called\n", SRCNAME, __func__);
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
message_verify (message, mapi_get_message_type (message), 1, hwnd);
}
else
log_debug_w32 (hr, "%s:%s: CmdCheckSig failed", SRCNAME, __func__);
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
else if (nCommandID == m_nCmdShowInfo
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
log_debug ("%s:%s: command ShowInfo called\n", SRCNAME, __func__);
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
message_show_info (message, hwnd);
}
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
else if (nCommandID == m_nCmdProtoAuto
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
log_debug ("%s:%s: command ProtoAuto called\n", SRCNAME, __func__);
check_menu (eecb, m_nCmdProtoAuto, TRUE);
check_menu (eecb, m_nCmdProtoPgpmime, FALSE);
check_menu (eecb, m_nCmdProtoSmime, FALSE);
m_pExchExt->m_protoSelection = PROTOCOL_UNKNOWN;
}
else if (nCommandID == m_nCmdProtoPgpmime
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
- log_debug ("%s:%s: command ProroPggmime called\n", SRCNAME, __func__);
+ log_debug ("%s:%s: command ProtoPgpmime called\n", SRCNAME, __func__);
check_menu (eecb, m_nCmdProtoAuto, FALSE);
check_menu (eecb, m_nCmdProtoPgpmime, TRUE);
check_menu (eecb, m_nCmdProtoSmime, FALSE);
m_pExchExt->m_protoSelection = PROTOCOL_OPENPGP;
}
else if (nCommandID == m_nCmdProtoSmime
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
log_debug ("%s:%s: command ProtoSmime called\n", SRCNAME, __func__);
if (opt.enable_smime)
{
check_menu (eecb, m_nCmdProtoAuto, FALSE);
check_menu (eecb, m_nCmdProtoPgpmime, FALSE);
check_menu (eecb, m_nCmdProtoSmime, TRUE);
m_pExchExt->m_protoSelection = PROTOCOL_SMIME;
}
}
else if (nCommandID == m_nCmdEncrypt
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
log_debug ("%s:%s: command Encrypt called\n", SRCNAME, __func__);
m_pExchExt->m_gpgEncrypt = !m_pExchExt->m_gpgEncrypt;
check_menu (eecb, m_nCmdEncrypt, m_pExchExt->m_gpgEncrypt);
}
else if (nCommandID == m_nCmdSign
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
log_debug ("%s:%s: command Sign called\n", SRCNAME, __func__);
m_pExchExt->m_gpgSign = !m_pExchExt->m_gpgSign;
check_menu (eecb, m_nCmdSign, m_pExchExt->m_gpgSign);
}
else if (nCommandID == m_nCmdKeyManager
&& m_lContext == EECONTEXT_VIEWER)
{
log_debug ("%s:%s: command KeyManager called\n", SRCNAME, __func__);
if (engine_start_keymanager (hwnd))
if (start_key_manager ())
MessageBox (NULL, _("Could not start certificate manager"),
_("GpgOL"), MB_ICONERROR|MB_OK);
}
else if (opt.enable_debug && nCommandID == m_nCmdDebug1
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
log_debug ("%s:%s: command Debug1 (open inspector) called\n",
SRCNAME, __func__);
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
open_inspector (eecb, message);
}
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
else if (opt.enable_debug && nCommandID == m_nCmdDebug2
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
log_debug ("%s:%s: command Debug2 (change message class) called\n",
SRCNAME, __func__);
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
/* We sync here. */
mapi_change_message_class (message, 1);
}
ul_release (message, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
else
{
- log_debug ("%s:%s: command passed on\n", SRCNAME, __func__);
+ if (debug_commands)
+ log_debug ("%s:%s: command passed on\n", SRCNAME, __func__);
return S_FALSE; /* Pass on unknown command. */
}
return S_OK;
}
/* Called by Exchange when it receives a WM_INITMENU message, allowing
the extension object to enable, disable, or update its menu
commands before the user sees them. This method is called
frequently and should be written in a very efficient manner. */
STDMETHODIMP_(VOID)
GpgolExtCommands::InitMenu(LPEXCHEXTCALLBACK eecb)
{
}
/* Called by Exchange when the user requests help for a menu item.
EECB is the pointer to Exchange Callback Interface. NCOMMANDID is
the command id. Return value: S_OK when it is a menu item of this
plugin and the help was shown; otherwise S_FALSE. */
STDMETHODIMP
GpgolExtCommands::Help (LPEXCHEXTCALLBACK eecb, UINT nCommandID)
{
if (nCommandID == m_nCmdDecrypt &&
m_lContext == EECONTEXT_READNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to decrypt and verify the message."),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdShowInfo
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to show information"
" on the crypto status"),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdCheckSig
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Check the signature now and display the result"),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdProtoAuto
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to automatically select the protocol."),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdProtoPgpmime
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to select the PGP/MIME protocol."),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdProtoSmime
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to select the S/MIME protocol."),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdEncrypt
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to encrypt the message."),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdSign
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
MessageBox (m_hWnd,
_("Select this option to sign the message."),
"GpgOL", MB_OK);
}
else if (nCommandID == m_nCmdKeyManager
&& m_lContext == EECONTEXT_VIEWER)
{
MessageBox (m_hWnd,
_("Select this option to open the certificate manager"),
"GpgOL", MB_OK);
}
else
return S_FALSE;
return S_OK;
}
/* Called by Exchange to get the status bar text or the tooltip of a
menu item. NCOMMANDID is the command id corresponding to the menu
item activated. LFLAGS identifies either EECQHT_STATUS or
EECQHT_TOOLTIP. PSZTEXT is a pointer to buffer to received the
text to be displayed. NCHARCNT is the available space in PSZTEXT.
Returns S_OK when it is a menu item of this plugin and the text was
set; otherwise S_FALSE. */
STDMETHODIMP
GpgolExtCommands::QueryHelpText(UINT nCommandID, ULONG lFlags,
LPTSTR pszText, UINT nCharCnt)
{
if (nCommandID == m_nCmdShowInfo
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
if (lFlags == EECQHT_STATUS)
lstrcpyn (pszText, ".", nCharCnt);
if (lFlags == EECQHT_TOOLTIP)
lstrcpyn (pszText, _("Show S/MIME status info"),
nCharCnt);
}
else if (nCommandID == m_nCmdCheckSig
&& m_lContext == EECONTEXT_READNOTEMESSAGE)
{
if (lFlags == EECQHT_STATUS)
lstrcpyn (pszText, ".", nCharCnt);
if (lFlags == EECQHT_TOOLTIP)
lstrcpyn (pszText,
_("Check the signature now and display the result"),
nCharCnt);
}
else if (nCommandID == m_nCmdProtoSmime
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
if (lFlags == EECQHT_STATUS)
lstrcpyn (pszText, ".", nCharCnt);
if (lFlags == EECQHT_TOOLTIP)
lstrcpyn (pszText,
_("Use S/MIME for sign/encrypt"),
nCharCnt);
}
else if (nCommandID == m_nCmdEncrypt
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
if (lFlags == EECQHT_STATUS)
lstrcpyn (pszText, ".", nCharCnt);
if (lFlags == EECQHT_TOOLTIP)
lstrcpyn (pszText,
_("Encrypt message with GnuPG"),
nCharCnt);
}
else if (nCommandID == m_nCmdSign
&& m_lContext == EECONTEXT_SENDNOTEMESSAGE)
{
if (lFlags == EECQHT_STATUS)
lstrcpyn (pszText, ".", nCharCnt);
if (lFlags == EECQHT_TOOLTIP)
lstrcpyn (pszText,
_("Sign message with GnuPG"),
nCharCnt);
}
else if (nCommandID == m_nCmdKeyManager
&& m_lContext == EECONTEXT_VIEWER)
{
if (lFlags == EECQHT_STATUS)
lstrcpyn (pszText, ".", nCharCnt);
if (lFlags == EECQHT_TOOLTIP)
lstrcpyn (pszText,
_("Open the GpgOL certificate manager"),
nCharCnt);
}
else
return S_FALSE;
return S_OK;
}
/* Called by Exchange to get toolbar button infos. TOOLBARID is the
toolbar identifier. BUTTONID is the toolbar button index. PTBB is
a pointer to toolbar button structure. DESCRIPTION is a pointer to
buffer receiving the text for the button. DESCRIPTION_SIZE is the
maximum size of DESCRIPTION. FLAGS are flags which might have the
EXCHEXT_UNICODE bit set.
Returns S_OK when it is a button of this plugin and the requested
info was delivered; otherwise S_FALSE. */
STDMETHODIMP
GpgolExtCommands::QueryButtonInfo (ULONG toolbarid, UINT buttonid,
LPTBBUTTON pTBB,
LPTSTR description, UINT description_size,
ULONG flags)
{
toolbar_info_t tb_info;
for (tb_info = m_toolbar_info; tb_info; tb_info = tb_info->next )
if (tb_info->button_id == buttonid
&& tb_info->context == m_lContext)
break;
if (!tb_info)
return S_FALSE; /* Not one of our toolbar buttons. */
- log_debug ("%s:%s: ctx=%lx tbid=%ld button_id(req)=%d got=%d cmd_id=%d '%s'\n",
- SRCNAME, __func__, m_lContext, toolbarid, buttonid,
- tb_info->button_id, tb_info->cmd_id, tb_info->desc);
+ if (debug_commands)
+ log_debug ("%s:%s: ctx=%lx tbid=%ld button_id(req)=%d got=%d"
+ " cmd_id=%d '%s'\n",
+ SRCNAME, __func__, m_lContext, toolbarid, buttonid,
+ tb_info->button_id, tb_info->cmd_id, tb_info->desc);
pTBB->iBitmap = tb_info->bitmap;
pTBB->idCommand = tb_info->cmd_id;
pTBB->fsState = TBSTATE_ENABLED;
pTBB->fsStyle = TBSTYLE_BUTTON;
pTBB->dwData = 0;
pTBB->iString = -1;
lstrcpyn (description, tb_info->desc, strlen (tb_info->desc));
if (tb_info->cmd_id == m_nCmdEncrypt)
{
pTBB->fsStyle |= TBSTYLE_CHECK;
if (m_pExchExt->m_gpgEncrypt)
pTBB->fsState |= TBSTATE_CHECKED;
}
else if (tb_info->cmd_id == m_nCmdSign)
{
pTBB->fsStyle |= TBSTYLE_CHECK;
if (m_pExchExt->m_gpgSign)
pTBB->fsState |= TBSTATE_CHECKED;
}
else if (tb_info->cmd_id == m_nCmdProtoAuto)
{
pTBB->fsStyle |= TBSTYLE_CHECK;
if (m_pExchExt->m_protoSelection == PROTOCOL_UNKNOWN)
pTBB->fsState |= TBSTATE_CHECKED;
}
else if (tb_info->cmd_id == m_nCmdProtoPgpmime)
{
pTBB->fsStyle |= TBSTYLE_CHECK;
if (m_pExchExt->m_protoSelection == PROTOCOL_OPENPGP)
pTBB->fsState |= TBSTATE_CHECKED;
}
else if (tb_info->cmd_id == m_nCmdProtoSmime)
{
pTBB->fsStyle |= TBSTYLE_CHECK;
if (m_pExchExt->m_protoSelection == PROTOCOL_SMIME)
pTBB->fsState |= TBSTATE_CHECKED;
}
return S_OK;
}
STDMETHODIMP
GpgolExtCommands::ResetToolbar (ULONG lToolbarID, ULONG lFlags)
{
return S_OK;
}
diff --git a/src/message.cpp b/src/message.cpp
index 00d6727..8441b27 100644
--- a/src/message.cpp
+++ b/src/message.cpp
@@ -1,1269 +1,1273 @@
/* message.cpp - Functions for message handling
* Copyright (C) 2006, 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <windows.h>
#include "mymapi.h"
#include "mymapitags.h"
#include "myexchext.h"
#include "common.h"
#include "mapihelp.h"
#include "mimeparser.h"
#include "mimemaker.h"
#include "display.h"
#include "ol-ext-callback.h"
#include "message.h"
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
static void
ul_release (LPVOID punk, const char *func)
{
ULONG res;
if (!punk)
return;
res = UlRelease (punk);
if (opt.enable_debug & DBG_MEMORY)
log_debug ("%s:%s: UlRelease(%p) had %lu references\n",
SRCNAME, func, punk, res);
}
/* A helper function used by OnRead and OnOpen to dispatch the
message. Returns true if the message has been processed. */
bool
message_incoming_handler (LPMESSAGE message, HWND hwnd)
{
bool retval = false;
msgtype_t msgtype;
int pass = 0;
retry:
pass++;
msgtype = mapi_get_message_type (message);
switch (msgtype)
{
case MSGTYPE_UNKNOWN:
/* If this message has never passed our change message class
code it won't have an unknown msgtype _and_ no sig status
flag. Thus we look at the message class now and change it if
required. It won't get displayed correctly right away but a
latter decrypt command or when viewed a second time all has
been set. Note that we should have similar code for some
message classes in GpgolUserEvents:OnSelectionChange; but
tehre are a couiple of problems. */
if (pass == 1 && !mapi_has_sig_status (message))
{
log_debug ("%s:%s: message class not yet checked - doing now\n",
SRCNAME, __func__);
if (mapi_change_message_class (message, 0))
goto retry;
}
break;
case MSGTYPE_SMIME:
if (pass == 1 && opt.enable_smime)
{
log_debug ("%s:%s: message class not checked with smime enabled "
"- doing now\n", SRCNAME, __func__);
if (mapi_change_message_class (message, 0))
goto retry;
}
break;
case MSGTYPE_GPGOL:
log_debug ("%s:%s: ignoring unknown message of original SMIME class\n",
SRCNAME, __func__);
break;
case MSGTYPE_GPGOL_MULTIPART_SIGNED:
log_debug ("%s:%s: processing multipart signed message\n",
SRCNAME, __func__);
retval = true;
message_verify (message, msgtype, 0, hwnd);
break;
case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
log_debug ("%s:%s: processing multipart encrypted message\n",
SRCNAME, __func__);
retval = true;
message_decrypt (message, msgtype, 0, hwnd);
break;
case MSGTYPE_GPGOL_OPAQUE_SIGNED:
log_debug ("%s:%s: processing opaque signed message\n",
SRCNAME, __func__);
retval = true;
message_verify (message, msgtype, 0, hwnd);
break;
case MSGTYPE_GPGOL_CLEAR_SIGNED:
log_debug ("%s:%s: processing clear signed pgp message\n",
SRCNAME, __func__);
retval = true;
message_verify (message, msgtype, 0, hwnd);
break;
case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
log_debug ("%s:%s: processing opaque encrypted message\n",
SRCNAME, __func__);
retval = true;
message_decrypt (message, msgtype, 0, hwnd);
break;
case MSGTYPE_GPGOL_PGP_MESSAGE:
log_debug ("%s:%s: processing pgp message\n", SRCNAME, __func__);
retval = true;
message_decrypt (message, msgtype, 0, hwnd);
break;
}
return retval;
}
/* Common Code used by OnReadComplete and OnOpenComplete to display a
modified message. Returns true if the message was encrypted. */
bool
message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
{
int err;
HRESULT hr;
LPMESSAGE message = NULL;
LPMDB mdb = NULL;
int ishtml, wasprotected = false;
char *body;
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
/* (old: If the message was protected we don't allow a fallback to the
OOM display methods.) Now: As it is difficult to find the
actual window we now use the OOM display always. */
err = mapi_get_gpgol_body_attachment (message, &body, NULL,
&ishtml, &wasprotected);
if (!err && body)
{
put_outlook_property (eecb, "GpgOLStatus",
mapi_get_sig_status (message));
update_display (hwnd, /*wasprotected? NULL:*/ eecb, ishtml, body);
}
else
{
put_outlook_property (eecb, "GpgOLStatus", "?");
update_display (hwnd, NULL, 0,
_("[Crypto operation failed - "
"can't show the body of the message]"));
}
xfree (body);
}
else
log_debug_w32 (hr, "%s:%s: error getting message", SRCNAME, __func__);
ul_release (message, __func__);
ul_release (mdb, __func__);
return !!wasprotected;
}
/* Helper for message_wipe_body_cruft. */
static void
do_wipe_body (LPMESSAGE message)
{
HRESULT hr;
SPropTagArray proparray;
int anyokay = 0;
proparray.cValues = 1;
proparray.aulPropTag[0] = PR_BODY;
hr = message->DeleteProps (&proparray, NULL);
if (hr)
log_debug_w32 (hr, "%s:%s: deleting PR_BODY failed", SRCNAME, __func__);
else
anyokay++;
proparray.cValues = 1;
proparray.aulPropTag[0] = PR_BODY_HTML;
message->DeleteProps (&proparray, NULL);
if (hr)
log_debug_w32 (hr, "%s:%s: deleting PR_BODY_HTML failed",
SRCNAME, __func__);
else
anyokay++;
if (anyokay)
{
hr = message->SaveChanges (KEEP_OPEN_READWRITE);
if (hr)
log_error_w32 (hr, "%s:%s: SaveChanges failed", SRCNAME, __func__);
else
log_debug ("%s:%s: SaveChanges succeded; body cruft removed",
SRCNAME, __func__);
}
}
/* If the current message is an encrypted one remove the body
properties which might have come up due to OL internal
syncronization and a failing olDiscard feature. */
void
message_wipe_body_cruft (LPEXCHEXTCALLBACK eecb)
{
HRESULT hr;
LPMESSAGE message = NULL;
LPMDB mdb = NULL;
log_debug ("%s:%s: enter", SRCNAME, __func__);
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
if (SUCCEEDED (hr))
{
switch (mapi_get_message_type (message))
{
case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
{
if (mapi_has_last_decrypted (message))
do_wipe_body (message);
else
log_debug_w32 (hr, "%s:%s: "
"error getting message decryption status",
SRCNAME, __func__);
}
break;
case MSGTYPE_GPGOL_PGP_MESSAGE:
{
/* In general we can't delete the body of a message if it
is an inline PGP encrypted message because the body
holds the ciphertext. However, while decrypting, we
take a copy of the body and work on that in future; if
this has been done we can delete the body. */
mapi_attach_item_t *table;
int found = 0;
int tblidx;
table = mapi_create_attach_table (message, 0);
if (table)
{
for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
if (table[tblidx].attach_type == ATTACHTYPE_PGPBODY
&& table[tblidx].filename
&& !strcmp (table[tblidx].filename, PGPBODYFILENAME))
{
found = 1;
break;
}
}
mapi_release_attach_table (table);
if (found)
do_wipe_body (message);
}
break;
default:
break;
}
ul_release (message, __func__);
ul_release (mdb, __func__);
}
}
/* Display some information about MESSAGE. */
void
message_show_info (LPMESSAGE message, HWND hwnd)
{
char *msgcls = mapi_get_message_class (message);
char *sigstat = mapi_get_sig_status (message);
char *mimeinfo = mapi_get_mime_info (message);
size_t buflen;
char *buffer;
buflen = strlen (msgcls) + strlen (sigstat) + strlen (mimeinfo) + 200;
buffer = (char*)xmalloc (buflen+1);
snprintf (buffer, buflen,
_("Signature status: %s\n"
"Message class ..: %s\n"
"MIME structure .:\n"
"%s"),
sigstat,
msgcls,
mimeinfo);
MessageBox (hwnd, buffer, _("GpgOL - Message Information"),
MB_ICONINFORMATION|MB_OK);
xfree (buffer);
xfree (mimeinfo);
xfree (sigstat);
xfree (msgcls);
}
static void
show_message (HWND hwnd, const char *text)
{
MessageBox (hwnd, text, _("GpgOL"), MB_ICONINFORMATION|MB_OK);
}
/* Convert the clear signed message from INPUT into a PGP/MIME signed
message and return it in a new allocated buffer. OUTPUTLEN
received the valid length of that buffer; the buffer is guarnateed
to be Nul terminated. */
static char *
pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
{
HRESULT hr;
STATSTG statinfo;
ULONG nread;
char *body = NULL;
char *p, *p0, *dest, *mark;
char boundary[BOUNDARYSIZE+1];
char top_header[200 + 2*BOUNDARYSIZE];
char sig_header[100 + BOUNDARYSIZE];
char end_header[10 + BOUNDARYSIZE];
size_t reserved_space;
int state;
*outputlen = 0;
/* Note that our parser does not make use of the micalg parameter. */
generate_boundary (boundary);
snprintf (top_header, sizeof top_header,
"MIME-Version: 1.0\r\n"
"Content-Type: multipart/signed; boundary=\"%s\";\r\n"
" protocol=\"application/pgp-signature\"\r\n"
"\r\n"
"--%s\r\n", boundary, boundary);
snprintf (sig_header, sizeof sig_header,
"--%s\r\n"
"Content-Type: application/pgp-signature\r\n"
"\r\n", boundary);
snprintf (end_header, sizeof end_header,
"\r\n"
"--%s--\r\n", boundary);
reserved_space = (strlen (top_header) + strlen (sig_header)
+ strlen (end_header)+ 100);
hr = input->Stat (&statinfo, STATFLAG_NONAME);
if (hr)
{
log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
return NULL;
}
body = (char*)xmalloc (reserved_space
+ (size_t)statinfo.cbSize.QuadPart + 2);
hr = input->Read (body+reserved_space,
(size_t)statinfo.cbSize.QuadPart, &nread);
if (hr)
{
log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
xfree (body);
return NULL;
}
body[reserved_space + nread] = 0;
body[reserved_space + nread+1] = 0; /* Just in case this is
accidently an wchar_t. */
if (nread != statinfo.cbSize.QuadPart)
{
log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
xfree (body);
return NULL;
}
/* We do in place conversion. */
state = 0;
dest = NULL;
for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
{
if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
&& trailing_ws_p (p+34) )
{
dest = stpcpy (body, top_header);
state = 1;
}
else if (state == 1)
{
/* Wait for an empty line. */
if (trailing_ws_p (p))
state = 2;
}
else if (state == 2 && strncmp (p, "-----", 5))
{
/* Copy signed data. */
p0 = p;
if (*p == '-' && p[1] == ' ')
p +=2; /* Remove escaping. */
mark = NULL;
while (*p && *p != '\n')
{
if (*p == ' ' || *p == '\t' || *p == '\r')
mark = p;
else
mark = NULL;
*dest++ = *p++;
}
if (mark)
*mark =0; /* Remove trailing white space. */
if (*p == '\n')
{
if (p[-1] == '\r')
*dest++ = '\r';
*dest++ = '\n';
}
if (p > p0)
p--; /* Adjust so that the strchr (p+1, '\n') can work. */
}
else if (state == 2)
{
/* Armor line encountered. */
p0 = p;
if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
|| !trailing_ws_p (p+29) )
fprintf (stderr,"Invalid clear signed message\n");
state = 3;
dest = stpcpy (dest, sig_header);
while (*p && *p != '\n')
*dest++ = *p++;
if (*p == '\n')
{
if (p[-1] == '\r')
*dest++ = '\r';
*dest++ = '\n';
}
if (p > p0)
p--; /* Adjust so that the strchr (p+1, '\n') can work. */
}
else if (state == 3 && strncmp (p, "-----", 5))
{
/* Copy signature. */
p0 = p;
while (*p && *p != '\n')
*dest++ = *p++;
if (*p == '\n')
{
if (p[-1] == '\r')
*dest++ = '\r';
*dest++ = '\n';
}
if (p > p0)
p--; /* Adjust so that the strchr (p+1, '\n') can work. */
}
else if (state == 3)
{
/* Armor line encountered. */
p0 = p;
if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
|| !trailing_ws_p (p+27) )
fprintf (stderr,"Invalid clear signed message (no end)\n");
while (*p && *p != '\n')
*dest++ = *p++;
if (*p == '\n')
{
if (p[-1] == '\r')
*dest++ = '\r';
*dest++ = '\n';
}
dest = stpcpy (dest, end_header);
if (p > p0)
p--; /* Adjust so that the strchr (p+1, '\n') can work. */
break; /* Ah well, we can stop here. */
}
}
if (!dest)
{
xfree (body);
return NULL;
}
*dest = 0;
*outputlen = strlen (body);
return body;
}
/* Verify MESSAGE and update the attachments as required. MSGTYPE
should be the type of the message so that the fucntion can decide
what to do. With FORCE set the verification is done regardlessless
of a cached signature result. */
int
message_verify (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
{
HRESULT hr;
mapi_attach_item_t *table = NULL;
LPSTREAM opaquestream = NULL;
int moss_idx = -1;
int i;
char *inbuf = NULL;
size_t inbuflen = 0;
protocol_t protocol = PROTOCOL_UNKNOWN;
int err;
switch (msgtype)
{
case MSGTYPE_GPGOL_MULTIPART_SIGNED:
case MSGTYPE_GPGOL_OPAQUE_SIGNED:
case MSGTYPE_GPGOL_CLEAR_SIGNED:
break;
case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
case MSGTYPE_GPGOL_PGP_MESSAGE:
log_debug ("%s:%s: message of type %d not expected",
SRCNAME, __func__, msgtype);
if (force)
show_message (hwnd, _("Signature verification of an encrypted message "
"is not possible."));
return -1; /* Should not be called for such a message. */
case MSGTYPE_GPGOL:
case MSGTYPE_SMIME:
case MSGTYPE_UNKNOWN:
log_debug ("%s:%s: message of type %d ignored",
SRCNAME, __func__, msgtype);
if (!force)
;
else if (msgtype == MSGTYPE_GPGOL)
show_message (hwnd, _("Signature verification of this "
"message class is not possible."));
else if (msgtype == MSGTYPE_SMIME)
show_message (hwnd, _("Signature verification of this "
"S/MIME message is not possible. Please check "
"that S/MIME processing has been enabled."));
else
show_message (hwnd, _("This message has no signature."));
return 0; /* Nothing to do. */
}
/* If a verification is forced, we set the cached signature status
first to "?" to mark that no verification has yet happened. If a
verification status has been set and the body attachment is
available we don't do a verification again. The need to check
for the body attachment is to avoid problems if that attachment
has accidently be deleted. */
if (force)
mapi_set_sig_status (message, "?");
else if (mapi_test_sig_status (message)
&& !mapi_get_gpgol_body_attachment (message, NULL,NULL,NULL,NULL))
return 0; /* Already checked that message. */
if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
{
/* PGP's clear signed messages are special: All is contained in
the body and thus there is no requirement for an
attachment. */
LPSTREAM rawstream;
rawstream = mapi_get_body_as_stream (message);
if (!rawstream)
return -1;
inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
rawstream->Release ();
if (!inbuf)
return -1;
protocol = PROTOCOL_OPENPGP;
}
else if (msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED)
{
/* S/MIME opaque signed message: The data is expected to be in
an attachment. */
table = mapi_create_attach_table (message, 0);
if (!table)
return -1; /* No attachment - this should not happen. */
for (i=0; !table[i].end_of_table; i++)
if (table[i].content_type
&& (!strcmp (table[i].content_type, "application/pkcs7-mime")
|| !strcmp (table[i].content_type,
"application/x-pkcs7-mime"))
&& table[i].filename
&& !strcmp (table[i].filename, "smime.p7m"))
break;
if (table[i].end_of_table)
{
log_debug ("%s:%s: attachment for opaque signed S/MIME not found",
SRCNAME, __func__);
mapi_release_attach_table (table);
return -1;
}
opaquestream = mapi_get_attach_as_stream (message, table+i, NULL);
if (!opaquestream)
{
mapi_release_attach_table (table);
return -1; /* Problem getting the attachment. */
}
protocol = PROTOCOL_SMIME;
}
else
{
/* PGP/MIME or S/MIME stuff. */
table = mapi_create_attach_table (message, 0);
if (!table)
return -1; /* No attachment - this should not happen. */
for (i=0; !table[i].end_of_table; i++)
if (table[i].attach_type == ATTACHTYPE_MOSS)
{
moss_idx = i;
break;
}
if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
{
/* No MOSS flag found in the table but there is only one
attachment. Due to the message type we know that this is
the original MOSS message. We mark this attachment as
hidden, so that it won't get displayed. We further mark
it as our original MOSS attachment so that after parsing
we have a mean to find it again (see above). */
moss_idx = 0;
mapi_mark_moss_attach (message, table+0);
}
if (moss_idx == -1)
{
mapi_release_attach_table (table);
return -1; /* No original attachment - this should not happen. */
}
inbuf = mapi_get_attach (message, table+0, &inbuflen);
if (!inbuf)
{
mapi_release_attach_table (table);
return -1; /* Problem getting the attachment. */
}
}
if (opaquestream)
err = mime_verify_opaque (protocol, opaquestream, message, hwnd, 0);
else
err = mime_verify (protocol, inbuf, inbuflen, message, hwnd, 0);
log_debug ("mime_verify%s returned %d", opaquestream? "_opaque":"", err);
if (err)
{
char buf[200];
snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
}
if (opaquestream)
opaquestream->Release ();
xfree (inbuf);
if (err)
{
char buf[200];
snprintf (buf, sizeof buf, "- %s", gpg_strerror (err));
mapi_set_sig_status (message, gpg_strerror (err));
}
else
mapi_set_sig_status (message, "! Good signature");
hr = message->SaveChanges (KEEP_OPEN_READWRITE);
if (hr)
log_error_w32 (hr, "%s:%s: SaveChanges failed",
SRCNAME, __func__);
mapi_release_attach_table (table);
return 0;
}
/* Copy the MAPI body to a PGPBODY type attachment. */
static int
pgp_body_to_attachment (LPMESSAGE message)
{
HRESULT hr;
LPSTREAM instream;
ULONG newpos;
LPATTACH newatt = NULL;
SPropValue prop;
LPSTREAM outstream = NULL;
LPUNKNOWN punk;
instream = mapi_get_body_as_stream (message);
if (!instream)
return -1;
hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
if (hr)
{
log_error ("%s:%s: can't create attachment: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
prop.ulPropTag = PR_ATTACH_METHOD;
prop.Value.ul = ATTACH_BY_VALUE;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach method: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
/* Mark that attachment so that we know why it has been created. */
if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
goto leave;
prop.Value.l = ATTACHTYPE_PGPBODY;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Attach Type", hr);
goto leave;
}
prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
prop.Value.b = TRUE;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
prop.ulPropTag = PR_ATTACH_FILENAME_A;
prop.Value.lpszA = PGPBODYFILENAME;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
punk = (LPUNKNOWN)outstream;
hr = newatt->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
MAPI_CREATE|MAPI_MODIFY, &punk);
if (FAILED (hr))
{
log_error ("%s:%s: can't create output stream: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
outstream = (LPSTREAM)punk;
/* Insert a blank line so that our mime parser skips over the mail
headers. */
hr = outstream->Write ("\r\n", 2, NULL);
if (hr)
{
log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
goto leave;
}
{
ULARGE_INTEGER cb;
cb.QuadPart = 0xffffffffffffffffll;
hr = instream->CopyTo (outstream, cb, NULL, NULL);
}
if (hr)
{
log_error ("%s:%s: can't copy streams: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
hr = outstream->Commit (0);
if (hr)
{
log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
SRCNAME, __func__, hr);
goto leave;
}
outstream->Release ();
outstream = NULL;
hr = newatt->SaveChanges (0);
if (hr)
{
log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
newatt->Release ();
newatt = NULL;
hr = message->SaveChanges (KEEP_OPEN_READWRITE);
if (hr)
log_error ("%s:%s: SaveChanges failed: hr=%#lx\n", SRCNAME, __func__, hr);
leave:
if (outstream)
{
outstream->Revert ();
outstream->Release ();
}
if (newatt)
newatt->Release ();
instream->Release ();
return hr? -1:0;
}
/* Decrypt MESSAGE, check signature and update the attachments as
required. MSGTYPE should be the type of the message so that the
function can decide what to do. With FORCE set the decryption is
done regardless whether it has already been done. */
int
message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
{
mapi_attach_item_t *table = NULL;
int part1_idx, part2_idx;
int tblidx;
int retval = -1;
LPSTREAM cipherstream;
gpg_error_t err;
int is_opaque = 0;
protocol_t protocol;
LPATTACH saved_attach = NULL;
int need_saved_attach = 0;
int need_rfc822_parser = 0;
+ int is_simple_pgp = 0;
+
switch (msgtype)
{
case MSGTYPE_UNKNOWN:
case MSGTYPE_SMIME:
case MSGTYPE_GPGOL:
case MSGTYPE_GPGOL_OPAQUE_SIGNED:
case MSGTYPE_GPGOL_MULTIPART_SIGNED:
case MSGTYPE_GPGOL_CLEAR_SIGNED:
if (force)
show_message (hwnd, _("This message is not encrypted."));
return -1; /* Should not have been called for this. */
case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
break;
case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
is_opaque = 1;
break;
case MSGTYPE_GPGOL_PGP_MESSAGE:
break;
}
if (!force && mapi_test_last_decrypted (message))
return 0; /* Already decrypted this message once during this
session. No need to do it again. */
if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
{
/* PGP messages are special: All is contained in the body and
thus there would be no requirement for an attachment.
However, due to problems with Outlook overwriting the body of
the message after decryption, we need to save the body away
before decrypting it. We then always look for that original
body atatchment and create one if it does not exist. */
part1_idx = -1;
table = mapi_create_attach_table (message, 0);
if (!table)
;
else
{
for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
if (table[tblidx].attach_type == ATTACHTYPE_PGPBODY
&& table[tblidx].filename
&& !strcmp (table[tblidx].filename, PGPBODYFILENAME))
{
part1_idx = tblidx;
break;
}
}
if (part1_idx == -1)
{
mapi_release_attach_table (table);
if (pgp_body_to_attachment (message))
table = NULL;
else
table = mapi_create_attach_table (message, 0);
if (table)
{
for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
if (table[tblidx].attach_type == ATTACHTYPE_PGPBODY
&& table[tblidx].filename
&& !strcmp (table[tblidx].filename, PGPBODYFILENAME))
{
part1_idx = tblidx;
break;
}
}
}
if (!table || part1_idx == -1)
{
log_debug ("%s:%s: problem copying the PGP inline encrypted message",
SRCNAME, __func__);
goto leave;
}
cipherstream = mapi_get_attach_as_stream (message, table+part1_idx,
NULL);
if (!cipherstream)
goto leave; /* Problem getting the attachment. */
protocol = PROTOCOL_OPENPGP;
need_rfc822_parser = 1;
+ is_simple_pgp = 1;
+
}
else
{
/* PGP/MIME or S/MIME stuff. */
table = mapi_create_attach_table (message, 0);
if (!table)
goto leave; /* No attachment - this should not happen. */
if (is_opaque)
{
/* S/MIME opaque encrypted message: We expect one
attachment. As we don't know wether we are called the
first time, we first try to find this attachment by
looking at all attachments. Only if this fails we
identify it by its order. */
part2_idx = -1;
for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
if (table[tblidx].attach_type == ATTACHTYPE_MOSSTEMPL)
{
/* This attachment has been generated by us in the
course of sending a new message. The content will
be multipart/signed because we used this to trick
out OL. We stop here and use this part for further
processing. */
part2_idx = tblidx;
need_rfc822_parser = 1;
break;
}
else if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
{
if (part2_idx == -1 && table[tblidx].content_type
&& (!strcmp (table[tblidx].content_type,
"application/pkcs7-mime")
|| !strcmp (table[tblidx].content_type,
"application/x-pkcs7-mime")))
part2_idx = tblidx;
}
if (part2_idx == -1 && tblidx >= 1)
{
/* We have attachments but none are marked. Thus we
assume that this is the first time we see this
message and we will set the mark now if we see
appropriate content types. */
if (table[0].content_type
&& (!strcmp (table[0].content_type, "application/pkcs7-mime")
|| !strcmp (table[0].content_type,
"application/x-pkcs7-mime")))
part2_idx = 0;
if (part2_idx != -1)
mapi_mark_moss_attach (message, table+part2_idx);
}
if (part2_idx == -1)
{
log_debug ("%s:%s: this is not an S/MIME encrypted message",
SRCNAME, __func__);
goto leave;
}
protocol = PROTOCOL_SMIME;
}
else
{
/* Multipart/encrypted message: We expect 2 attachments.
The first one with the version number and the second one
with the ciphertext. As we don't know wether we are
called the first time, we first try to find these
attachments by looking at all attachments. Only if this
fails we identify them by their order (i.e. the first 2
attachments) and mark them as part1 and part2. */
part1_idx = part2_idx = -1;
for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
{
if (part1_idx == -1 && table[tblidx].content_type
&& !strcmp (table[tblidx].content_type,
"application/pgp-encrypted"))
part1_idx = tblidx;
else if (part2_idx == -1 && table[tblidx].content_type
&& !strcmp (table[tblidx].content_type,
"application/octet-stream"))
part2_idx = tblidx;
}
if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
{
/* At least 2 attachments but none are marked. Thus we
assume that this is the first time we see this
message and we will set the mark now if we see
appropriate content types. */
if (table[0].content_type
&& !strcmp (table[0].content_type,
"application/pgp-encrypted"))
part1_idx = 0;
if (table[1].content_type
&& !strcmp (table[1].content_type,
"application/octet-stream"))
part2_idx = 1;
if (part1_idx != -1 && part2_idx != -1)
{
mapi_mark_moss_attach (message, table+part1_idx);
mapi_mark_moss_attach (message, table+part2_idx);
}
}
if (part1_idx == -1 || part2_idx == -1
&& !table[0].end_of_table && table[1].end_of_table
&& table[0].attach_type == ATTACHTYPE_MOSS
&& table[0].filename
&& !strcmp (table[0].filename, MIMEATTACHFILENAME))
{
/* This is likely a PGP/MIME created by us. Due to the
way we created that message, the MAPI derived content
type is wrong and there is only one attachment
(gpgolXXX.dat). We simply assume that it is PGP/MIME
encrypted and pass it on to the mime parser. We also
keep the attachment open so that we can later set it
to hidden if not yet done. I can't remember whether
it is possible to set the hidden attribute when
creating the message - probably not. Thus we take
care of it here. */
log_debug ("%s:%s: "
"assuming self-created PGP/MIME encrypted message",
SRCNAME, __func__);
part2_idx = 0;
need_saved_attach = 1;
}
else if (part1_idx == -1 || part2_idx == -1)
{
log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
SRCNAME, __func__);
goto leave;
}
protocol = PROTOCOL_OPENPGP;
}
cipherstream = mapi_get_attach_as_stream (message, table+part2_idx,
need_saved_attach?
&saved_attach : NULL );
if (!cipherstream)
goto leave; /* Problem getting the attachment. */
}
err = mime_decrypt (protocol, cipherstream, message,
- need_rfc822_parser, hwnd, 0);
+ need_rfc822_parser, is_simple_pgp, hwnd, 0);
log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
if (err)
{
char buf[200];
switch (gpg_err_code (err))
{
case GPG_ERR_NO_DATA:
/* The UI server already displayed a message. */
break;
default:
snprintf (buf, sizeof buf,
_("Decryption failed\n(%s)"), gpg_strerror (err));
MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
break;
}
}
else
{
if (saved_attach)
mapi_set_attach_hidden (saved_attach);
}
cipherstream->Release ();
retval = 0;
leave:
if (saved_attach)
saved_attach->Release ();
mapi_release_attach_table (table);
return retval;
}
/* Return an array of strings with the recipients of the message. On
success a malloced array is returned containing allocated strings
for each recipient. The end of the array is marked by NULL.
Caller is responsible for releasing the array. On failure NULL is
returned. */
static char **
get_recipients (LPMESSAGE message)
{
static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
HRESULT hr;
LPMAPITABLE lpRecipientTable = NULL;
LPSRowSet lpRecipientRows = NULL;
unsigned int rowidx;
LPSPropValue row;
char **rset;
int rsetidx;
if (!message)
return NULL;
hr = message->GetRecipientTable (0, &lpRecipientTable);
if (FAILED (hr))
{
log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
return NULL;
}
hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
NULL, NULL, 0L, &lpRecipientRows);
if (FAILED (hr))
{
log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
if (lpRecipientTable)
lpRecipientTable->Release();
return NULL;
}
rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
{
if (!lpRecipientRows->aRow[rowidx].cValues)
continue;
row = lpRecipientRows->aRow[rowidx].lpProps;
switch (PROP_TYPE (row->ulPropTag))
{
case PT_UNICODE:
if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
rsetidx++;
else
log_debug ("%s:%s: error converting recipient to utf8\n",
SRCNAME, __func__);
break;
case PT_STRING8: /* Assume ASCII. */
rset[rsetidx++] = xstrdup (row->Value.lpszA);
break;
default:
log_debug ("%s:%s: proptag=0x%08lx not supported\n",
SRCNAME, __func__, row->ulPropTag);
break;
}
}
if (lpRecipientTable)
lpRecipientTable->Release();
if (lpRecipientRows)
FreeProws(lpRecipientRows);
log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
for (rsetidx=0; rset[rsetidx]; rsetidx++)
log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
return rset;
}
static void
release_recipient_array (char **recipients)
{
int idx;
if (recipients)
{
for (idx=0; recipients[idx]; idx++)
xfree (recipients[idx]);
xfree (recipients);
}
}
static int
sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd, int signflag)
{
gpg_error_t err;
char **recipients;
recipients = get_recipients (message);
if (!recipients || !recipients[0])
{
MessageBox (hwnd, _("No recipients to encrypt to are given"),
"GpgOL", MB_ICONERROR|MB_OK);
err = gpg_error (GPG_ERR_GENERAL);
}
else
{
if (signflag)
err = mime_sign_encrypt (message, hwnd, protocol, recipients);
else
err = mime_encrypt (message, hwnd, protocol, recipients);
if (err)
{
char buf[200];
snprintf (buf, sizeof buf,
_("Encryption failed (%s)"), gpg_strerror (err));
MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
}
}
release_recipient_array (recipients);
return err;
}
/* Sign the MESSAGE. */
int
message_sign (LPMESSAGE message, protocol_t protocol, HWND hwnd)
{
gpg_error_t err;
err = mime_sign (message, hwnd, protocol);
if (err)
{
char buf[200];
snprintf (buf, sizeof buf,
_("Signing failed (%s)"), gpg_strerror (err));
MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
}
return err;
}
/* Encrypt the MESSAGE. */
int
message_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
{
return sign_encrypt (message, protocol, hwnd, 0);
}
/* Sign+Encrypt the MESSAGE. */
int
message_sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
{
return sign_encrypt (message, protocol, hwnd, 1);
}
diff --git a/src/mimeparser.c b/src/mimeparser.c
index b0e7e62..dc5871e 100644
--- a/src/mimeparser.c
+++ b/src/mimeparser.c
@@ -1,1702 +1,1744 @@
/* mimeparser.c - Parse multipart MIME message
* Copyright (C) 2005, 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define COBJMACROS
#include <windows.h>
#include <objidl.h> /* For IStream. */
#include <gpgme.h>
#include "mymapi.h"
#include "mymapitags.h"
#include "rfc822parse.h"
#include "common.h"
#include "engine.h"
#include "mapihelp.h"
#include "serpent.h"
#include "mimeparser.h"
/* Define the next to get extra debug message for the MIME parser. */
#define DEBUG_PARSER 1
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
static const char oid_mimetag[] =
{0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
/* 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
/* 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 fileanme 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
{
HWND hwnd; /* A window handle to be used for message boxes etc. */
rfc822parse_t msg; /* The handle of the RFC822 parser. */
int preview; /* Do only decryption and pop up no message bozes. */
int protect_mode; /* Encrypt all attachments etc. (cf. SYMENC). */
int verify_mode; /* True if we want to verify a signature. */
+ int no_mail_header; /* True if we want to bypass all MIME parsing. */
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. */
gpgme_data_t signed_data;/* NULL or the data object used to collect
the signed data. It would be better to
just hash it but there is no support in
gpgme for this yet. */
gpgme_data_t sig_data; /* NULL or data object to collect the
signature attachment which should be a
signature then. */
int collect_attachment; /* True if we are collecting an attachment
or the body. */
int collect_signeddata; /* True if we are collecting the signed data. */
int collect_signature; /* True if we are collecting a signature. */
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. */
engine_filter_t outfilter; /* Fiter as used by ciphertext_handler. */
/* 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;
LPMESSAGE mapi_message; /* The MAPI message object we are working on. */
LPSTREAM outstream; /* NULL or a stream to write a part to. */
LPATTACH mapi_attach; /* The attachment object we are writing. */
symenc_t symenc; /* NULL or the context used to protect
an attachment. */
struct {
LPSTREAM outstream; /* Saved stream used to continue a body
part. */
LPATTACH mapi_attach; /* Saved attachment used to continue a body part. */
symenc_t symenc; /* Saved encryption context used to continue
a body part. */
} body_saved;
int any_attachments_created; /* True if we created a new atatchment. */
b64_state_t base64; /* The state of the Base-64 decoder. */
int line_too_long; /* Indicates that a received line was too long. */
int parser_error; /* Indicates that we encountered a error from
the parser. */
/* Buffer used to constructed complete files. */
size_t linebufsize; /* The allocated size of the buffer. */
size_t linebufpos; /* The actual write posituion. */
char linebuf[1]; /* The buffer. */
};
typedef struct mime_context *mime_context_t;
/* This function is a wrapper around gpgme_data_write to convert the
data to utf-8 first. We assume Latin-1 here. */
/* static int */
/* latin1_data_write (gpgme_data_t data, const char *line, size_t len) */
/* { */
/* const char *s; */
/* char *buffer, *p; */
/* size_t i, n; */
/* int rc; */
/* for (s=line, i=0, n=0 ; i < len; s++, i++ ) */
/* { */
/* n++; */
/* if (*s & 0x80) */
/* n++; */
/* } */
/* buffer = xmalloc (n + 1); */
/* for (s=line, i=0, p=buffer; i < len; s++, i++ ) */
/* { */
/* if (*s & 0x80) */
/* { */
/* *p++ = 0xc0 | ((*s >> 6) & 3); */
/* *p++ = 0x80 | (*s & 0x3f); */
/* } */
/* else */
/* *p++ = *s; */
/* } */
/* assert (p-buffer == n); */
/* rc = gpgme_data_write (data, buffer, n); */
/* xfree (buffer); */
/* return rc; */
/* } */
/* Print the message event EVENT. */
static void
debug_message_event (mime_context_t ctx, 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;
}
#ifdef DEBUG_PARSER
log_debug ("%s: ctx=%p, rfc822 event %s\n", SRCNAME, ctx, s);
#endif
}
/* Start a new atatchment. With IS_BODY set, the attachment is
actually the body part of the message which is treated in a special
way. */
static int
start_attachment (mime_context_t ctx, int is_body)
{
int retval = -1;
HRESULT hr;
ULONG newpos;
SPropValue prop;
LPATTACH newatt = NULL;
LPSTREAM to = NULL;
LPUNKNOWN punk;
#ifdef DEBUG_PARSER
log_debug ("%s:%s: for ctx=%p is_body=%d", SRCNAME, __func__, ctx, is_body);
#endif
/* Just in case something has not been finished, do it here. */
if (ctx->outstream)
{
IStream_Release (ctx->outstream);
ctx->outstream = NULL;
}
if (ctx->mapi_attach)
{
IAttach_Release (ctx->mapi_attach);
ctx->mapi_attach = NULL;
}
if (ctx->symenc)
{
symenc_close (ctx->symenc);
ctx->symenc = NULL;
}
/* Before we start with the first attachment we need to delete all
attachments which might have been created already by a past
parser run. */
if (!ctx->any_attachments_created)
{
mapi_attach_item_t *table;
int i;
table = mapi_create_attach_table (ctx->mapi_message, 1);
if (table)
{
for (i=0; !table[i].end_of_table; i++)
if (table[i].attach_type == ATTACHTYPE_FROMMOSS)
{
hr = IMessage_DeleteAttach (ctx->mapi_message,
table[i].mapipos,
0, NULL, 0);
if (hr)
log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
SRCNAME, __func__, hr);
}
mapi_release_attach_table (table);
}
ctx->any_attachments_created = 1;
}
/* Now create a new attachment. */
hr = IMessage_CreateAttach (ctx->mapi_message, NULL, 0, &newpos, &newatt);
if (hr)
{
log_error ("%s:%s: can't create attachment: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
prop.ulPropTag = PR_ATTACH_METHOD;
prop.Value.ul = ATTACH_BY_VALUE;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr != S_OK)
{
log_error ("%s:%s: can't set attach method: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
/* Mark that attachment so that we know why it has been created. */
if (get_gpgolattachtype_tag (ctx->mapi_message, &prop.ulPropTag) )
goto leave;
prop.Value.l = ATTACHTYPE_FROMMOSS;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Attach Type", hr);
goto leave;
}
/* The body attachment is special and should not be shown in the list
of attachments. */
if (is_body)
{
prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
prop.Value.b = TRUE;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
}
ctx->is_body = is_body;
/* We need to insert a short filename . Without it, the _displayed_
list of attachments won't get updated although the attachment has
been created. */
prop.ulPropTag = PR_ATTACH_FILENAME_A;
{
char buf[100];
if (is_body)
prop.Value.lpszA = is_body == 2? "gpgol000.htm":"gpgol000.txt";
else
{
snprintf (buf, 100, "gpgol%03d.dat", ctx->part_counter);
prop.Value.lpszA = buf;
}
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
}
if (hr)
{
log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
/* And now for the real name. */
if (ctx->mimestruct_cur && ctx->mimestruct_cur->filename)
{
prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
prop.Value.lpszA = ctx->mimestruct_cur->filename;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach long filename: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
}
prop.ulPropTag = PR_ATTACH_TAG;
prop.Value.bin.cb = sizeof oid_mimetag;
prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach tag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
assert (ctx->mimestruct_cur && ctx->mimestruct_cur->content_type);
prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
prop.Value.lpszA = ctx->mimestruct_cur->content_type;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
/* If we have the MIME info and a charset info and that is not
UTF-8, set our own Charset property. */
if (ctx->mimestruct_cur)
{
const char *s = ctx->mimestruct_cur->charset;
if (s && strcmp (s, "utf-8") && strcmp (s, "UTF-8")
&& strcmp (s, "utf8") && strcmp (s, "UTF8"))
mapi_set_gpgol_charset ((LPMESSAGE)newatt, s);
}
/* If we are in protect mode (i.e. working on a decrypted message,
we need to setup the symkey context to protect (encrypt) the
attachment in the MAPI. */
if (ctx->protect_mode)
{
char *iv;
if (get_gpgolprotectiv_tag (ctx->mapi_message, &prop.ulPropTag) )
goto leave;
iv = create_initialization_vector (16);
if (!iv)
{
log_error ("%s:%s: error creating initialization vector",
SRCNAME, __func__);
goto leave;
}
prop.Value.bin.cb = 16;
prop.Value.bin.lpb = iv;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Protect IV", hr);
goto leave;
}
ctx->symenc = symenc_open (get_128bit_session_key (), 16, iv, 16);
xfree (iv);
if (!ctx->symenc)
{
log_error ("%s:%s: error creating cipher context",
SRCNAME, __func__);
goto leave;
}
}
punk = (LPUNKNOWN)to;
hr = IAttach_OpenProperty (newatt, PR_ATTACH_DATA_BIN, &IID_IStream, 0,
MAPI_CREATE|MAPI_MODIFY, &punk);
if (FAILED (hr))
{
log_error ("%s:%s: can't create output stream: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
to = (LPSTREAM)punk;
ctx->outstream = to;
to = NULL;
ctx->mapi_attach = newatt;
newatt = NULL;
if (ctx->symenc)
{
char tmpbuf[16];
/* Write an encrypted fixed 16 byte string which we need to
check at decryption time to see whether we have actually
encrypted it using this session key. */
symenc_cfb_encrypt (ctx->symenc, tmpbuf, "GpgOL attachment", 16);
IStream_Write (ctx->outstream, tmpbuf, 16, NULL);
}
retval = 0; /* Success. */
leave:
if (to)
{
IStream_Revert (to);
IStream_Release (to);
}
if (newatt)
IAttach_Release (newatt);
return retval;
}
static int
finish_attachment (mime_context_t ctx, int cancel)
{
HRESULT hr;
int retval = -1;
#ifdef DEBUG_PARSER
log_debug ("%s:%s: for ctx=%p cancel=%d", SRCNAME, __func__, ctx, cancel);
#endif
if (ctx->outstream && ctx->is_body && !ctx->body_saved.outstream)
{
ctx->body_saved.outstream = ctx->outstream;
ctx->outstream = NULL;
retval = 0;
}
else if (ctx->outstream)
{
IStream_Commit (ctx->outstream, 0);
IStream_Release (ctx->outstream);
ctx->outstream = NULL;
if (cancel)
retval = 0;
else if (ctx->mapi_attach)
{
hr = IAttach_SaveChanges (ctx->mapi_attach, 0);
if (hr)
{
log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
SRCNAME, __func__, hr);
}
else
retval = 0; /* Success. */
}
}
if (ctx->mapi_attach && ctx->is_body && !ctx->body_saved.mapi_attach)
{
ctx->body_saved.mapi_attach = ctx->mapi_attach;
ctx->mapi_attach = NULL;
}
else if (ctx->mapi_attach)
{
IAttach_Release (ctx->mapi_attach);
ctx->mapi_attach = NULL;
}
if (ctx->symenc && ctx->is_body && !ctx->body_saved.symenc)
{
ctx->body_saved.symenc = ctx->symenc;
ctx->symenc = NULL;
}
else if (ctx->symenc)
{
symenc_close (ctx->symenc);
ctx->symenc = NULL;
}
ctx->is_body = 0;
return retval;
}
/* Finish the saved body part. This is required because we delay the
finishing of body parts. */
static int
finish_saved_body (mime_context_t ctx, int cancel)
{
HRESULT hr;
int retval = -1;
if (ctx->body_saved.outstream)
{
IStream_Commit (ctx->body_saved.outstream, 0);
IStream_Release (ctx->body_saved.outstream);
ctx->body_saved.outstream = NULL;
if (cancel)
retval = 0;
else if (ctx->body_saved.mapi_attach)
{
hr = IAttach_SaveChanges (ctx->body_saved.mapi_attach, 0);
if (hr)
{
log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
SRCNAME, __func__, hr);
}
else
retval = 0; /* Success. */
}
}
if (ctx->body_saved.mapi_attach)
{
IAttach_Release (ctx->body_saved.mapi_attach);
ctx->body_saved.mapi_attach = NULL;
}
if (ctx->symenc)
{
symenc_close (ctx->body_saved.symenc);
ctx->body_saved.symenc = NULL;
}
return retval;
}
/* Create the MIME info string. This is a LF delimited string
with one line per MIME part. Each line is formatted this way:
LEVEL:ENCINFO:SIGINFO:CT:CHARSET:FILENAME
LEVEL is the nesting level with 0 as the top (rfc822 header)
ENCINFO is one of
p PGP/MIME encrypted
s S/MIME encryptyed
SIGINFO is one of
pX PGP/MIME signed (also used for clearsigned)
sX S/MIME signed
With X being:
? unklnown status
- Bad signature
~ Good signature but with some problems
! Good signature
CT ist the content type of this part
CHARSET is the charset used for this part
FILENAME is the file name.
*/
static char *
build_mimeinfo (mimestruct_item_t mimestruct)
{
mimestruct_item_t ms;
size_t buflen, n;
char *buffer, *p;
char numbuf[20];
/* FIXME: We need to escape stuff so that there are no colons. */
for (buflen=0, ms = mimestruct; ms; ms = ms->next)
{
buflen += sizeof numbuf;
buflen += strlen (ms->content_type);
buflen += ms->charset? strlen (ms->charset) : 0;
buflen += ms->filename? strlen (ms->filename) : 0;
buflen += 20;
}
p = buffer = xmalloc (buflen+1);
for (ms=mimestruct; ms; ms = ms->next)
{
snprintf (p, buflen, "%d:::%s:%s:%s:\n",
ms->level, ms->content_type,
ms->charset? ms->charset : "",
ms->filename? ms->filename : "");
n = strlen (p);
assert (n < buflen);
buflen -= n;
p += n;
}
return buffer;
}
static int
finish_message (LPMESSAGE message, gpg_error_t err, int protect_mode,
mimestruct_item_t mimestruct)
{
HRESULT hr;
SPropValue prop;
/* If this was an encrypted message we save the session marker in a
speciat property so that we now that we already decrypted that
message within this session. This is pretty useful when
scrolling through messages and preview decryption has been
enabled. */
if (protect_mode)
{
char sesmrk[8];
if (get_gpgollastdecrypted_tag (message, &prop.ulPropTag) )
return -1;
if (err)
memset (sesmrk, 0, 8);
else
memcpy (sesmrk, get_64bit_session_marker (), 8);
prop.Value.bin.cb = 8;
prop.Value.bin.lpb = sesmrk;
hr = IMessage_SetProps (message, 1, &prop, NULL);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Last Decrypted", hr);
return -1;
}
}
/* Store the MIME structure away. */
if (get_gpgolmimeinfo_tag (message, &prop.ulPropTag) )
return -1;
prop.Value.lpszA = build_mimeinfo (mimestruct);
hr = IMessage_SetProps (message, 1, &prop, NULL);
xfree (prop.Value.lpszA);
if (hr)
{
log_error_w32 (hr, "%s:%s: error setting the mime info",
SRCNAME, __func__);
return -1;
}
hr = IMessage_SaveChanges (message, KEEP_OPEN_READWRITE|FORCE_SAVE);
if (hr)
{
log_error_w32 (hr, "%s:%s: SaveChanges to the message failed",
SRCNAME, __func__);
return -1;
}
return 0;
}
/* 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. */
static int
t2body (mime_context_t ctx, rfc822parse_t msg)
{
rfc822parse_field_t field;
const char *ctmain, *ctsub;
const char *s;
size_t off;
char *p;
int is_text = 0;
char *filename = 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);
}
free (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 = xstrdup (s);
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";
}
#ifdef DEBUG_PARSER
log_debug ("%s:%s: ctx=%p, ct=`%s/%s'\n",
SRCNAME, __func__, ctx, ctmain, ctsub);
#endif
s = rfc822parse_query_parameter (field, "charset", 0);
if (s)
charset = xstrdup (s);
/* Update our idea of the entire MIME structure. */
{
mimestruct_item_t ms;
ms = 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;
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 (ctx->verify_mode && !ctx->signed_data
&& !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 (ctmain, "text"))
{
is_text = !strcmp (ctsub, "html")? 2:1;
}
else if (ctx->verify_mode && ctx->nesting_level == 1 && !ctx->sig_data
&& !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. */
if (!ctx->preview && !gpgme_data_new (&ctx->sig_data))
ctx->collect_signature = 1;
}
else /* Other type. */
{
if (!ctx->preview)
ctx->collect_attachment = 1;
}
rfc822parse_release_field (field); /* (Content-type) */
ctx->in_data = 1;
#ifdef DEBUG_PARSER
log_debug ("%s: this body: nesting=%d partno=%d is_text=%d charset=\"%s\"\n",
SRCNAME, ctx->nesting_level, ctx->part_counter, is_text,
ctx->mimestruct_cur->charset?ctx->mimestruct_cur->charset:"");
#endif
/* If this is a text part, decide whether we treat it as our body. */
if (is_text)
{
ctx->collect_attachment = 1;
/* If this is the first text part at all we will start to
collect it and use it later as the regular body. */
if (!ctx->body_seen)
{
ctx->body_seen = 1;
if (start_attachment (ctx, 1))
return -1;
assert (ctx->outstream);
}
else if (!ctx->body_saved.outstream || !ctx->body_saved.mapi_attach)
{
/* Oops: We expected to continue a body but the state is not
correct. Create a plain attachment instead. */
log_debug ("%s:%s: ctx=%p, no saved outstream or mapi_attach (%p,%p)",
SRCNAME, __func__, ctx,
ctx->body_saved.outstream, ctx->body_saved.mapi_attach);
if (start_attachment (ctx, 0))
return -1;
assert (ctx->outstream);
}
else if (ctx->outstream || ctx->mapi_attach || ctx->symenc)
{
/* We expected to continue a body but the last attachment
has not been properly closed. Create a plain attachment
instead. */
log_debug ("%s:%s: ctx=%p, outstream, mapi_attach or symenc not "
"closed (%p,%p,%p)",
SRCNAME, __func__, ctx,
ctx->outstream, ctx->mapi_attach, ctx->symenc);
if (start_attachment (ctx, 0))
return -1;
assert (ctx->outstream);
}
else
{
/* We already got one body and thus we can continue that
last attachment. */
#ifdef DEBUG_PARSER
log_debug ("%s:%s: continuing body part\n", SRCNAME, __func__);
#endif
ctx->is_body = 1;
ctx->outstream = ctx->body_saved.outstream;
ctx->mapi_attach = ctx->body_saved.mapi_attach;
ctx->symenc = ctx->body_saved.symenc;
ctx->body_saved.outstream = NULL;
ctx->body_saved.mapi_attach = NULL;
ctx->body_saved.symenc = NULL;
}
}
else if (ctx->collect_attachment)
{
/* Now that if we have an attachment prepare a new MAPI
attachment. */
if (start_attachment (ctx, 0))
return -1;
assert (ctx->outstream);
}
return 0;
}
/* This routine gets called by the RFC822 parser for all kind of
events. OPAQUE carries in our case an smime context. Should
return 0 on success or -1 as well as setting errno on
failure. */
static int
message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
{
int retval = 0;
mime_context_t ctx = opaque;
debug_message_event (ctx, event);
+ if (ctx->no_mail_header)
+ {
+ /* Assume that this is not a regular mail but plain text. */
+ if (!ctx->body_seen)
+ {
+#ifdef DEBUG_PARSER
+ log_debug ("%s:%s: assuming this is plain text without headers\n",
+ SRCNAME, __func__);
+#endif
+ ctx->in_data = 1;
+ ctx->collect_attachment = 2; /* 2 so we don't skip the first line. */
+ ctx->body_seen = 1;
+ /* Create a fake MIME structure. */
+ /* Fixme: We might want to take it from the enclosing message. */
+ {
+ const char ctmain[] = "text";
+ const char ctsub[] = "plain";
+ mimestruct_item_t ms;
+
+ ms = 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 = 0;
+ }
+ if (start_attachment (ctx, 1))
+ return -1;
+ assert (ctx->outstream);
+ }
+ return 0;
+ }
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_signeddata = 1;
gpgme_data_new (&ctx->signed_data);
}
}
switch (event)
{
case RFC822PARSE_T2BODY:
retval = t2body (ctx, 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 = 1;
}
break;
case RFC822PARSE_BOUNDARY:
case RFC822PARSE_LAST_BOUNDARY:
ctx->any_boundary = 1;
ctx->in_data = 0;
ctx->collect_attachment = 0;
finish_attachment (ctx, 0);
assert (!ctx->outstream);
if (ctx->start_hashing == 2 && ctx->hashing_level == ctx->nesting_level)
{
ctx->start_hashing = 3; /* Avoid triggering it again. */
ctx->collect_signeddata = 0;
}
break;
case RFC822PARSE_BEGIN_HEADER:
ctx->part_counter++;
break;
default: /* Ignore all other events. */
break;
}
return retval;
}
/* This handler is called by GPGME with the decrypted plaintext. */
static int
plaintext_handler (void *handle, const void *buffer, size_t size)
{
mime_context_t ctx = handle;
const char *s;
size_t nleft, pos, len;
s = buffer;
pos = ctx->linebufpos;
nleft = size;
for (; nleft ; nleft--, s++)
{
if (pos >= ctx->linebufsize)
{
log_error ("%s: ctx=%p, rfc822 parser failed: line too long\n",
SRCNAME, ctx);
ctx->line_too_long = 1;
return -1; /* Error. */
}
if (*s != '\n')
ctx->linebuf[pos++] = *s;
else
{ /* Got a complete line. Remove the last CR */
if (pos && ctx->linebuf[pos-1] == '\r')
pos--;
+/* log_debug ("%s:%s: ctx=%p, line=`%.*s'\n", */
+/* SRCNAME, __func__, ctx, (int)pos, ctx->linebuf); */
if (rfc822parse_insert (ctx->msg, ctx->linebuf, pos))
{
log_error ("%s: ctx=%p, rfc822 parser failed: %s\n",
SRCNAME, ctx, strerror (errno));
ctx->parser_error = 1;
return -1; /* Error. */
}
if (ctx->collect_signeddata && ctx->signed_data)
{
/* Save the signed data. Note that we need to delay
the CR/LF because the last line ending belongs to the
next boundary. */
if (ctx->collect_signeddata == 2)
gpgme_data_write (ctx->signed_data, "\r\n", 2);
gpgme_data_write (ctx->signed_data, ctx->linebuf, pos);
ctx->collect_signeddata = 2;
}
if (ctx->in_data && ctx->collect_attachment)
{
/* We are inside of an attachment part. Write it out. */
if (ctx->collect_attachment == 1) /* Skip the first line. */
ctx->collect_attachment = 2;
else if (ctx->outstream)
{
HRESULT hr = 0;
int slbrk = 0;
if (ctx->is_qp_encoded)
len = qp_decode (ctx->linebuf, pos, &slbrk);
else if (ctx->is_base64_encoded)
len = b64_decode (&ctx->base64, ctx->linebuf, pos);
else
len = pos;
if (len)
{
if (ctx->symenc)
symenc_cfb_encrypt (ctx->symenc, ctx->linebuf,
ctx->linebuf, len);
hr = IStream_Write (ctx->outstream, ctx->linebuf,
len, NULL);
}
if (!hr && !ctx->is_base64_encoded && !slbrk)
{
char tmp[3] = "\r\n";
if (ctx->symenc)
symenc_cfb_encrypt (ctx->symenc, tmp, tmp, 2);
hr = IStream_Write (ctx->outstream, tmp, 2, NULL);
}
if (hr)
{
log_debug ("%s:%s: Write failed: hr=%#lx",
SRCNAME, __func__, hr);
if (!ctx->preview)
MessageBox (ctx->hwnd, _("Error writing to stream"),
_("I/O-Error"), MB_ICONERROR|MB_OK);
ctx->parser_error = 1;
return -1; /* Error. */
}
}
}
else if (ctx->in_data && ctx->collect_signature)
{
/* We are inside of a signature attachment part. */
if (ctx->collect_signature == 1) /* Skip the first line. */
ctx->collect_signature = 2;
else if (ctx->sig_data)
{
int slbrk = 0;
if (ctx->is_qp_encoded)
len = qp_decode (ctx->linebuf, pos, &slbrk);
else if (ctx->is_base64_encoded)
len = b64_decode (&ctx->base64, ctx->linebuf, pos);
else
len = pos;
if (len)
gpgme_data_write (ctx->sig_data, ctx->linebuf, len);
if (!ctx->is_base64_encoded && !slbrk)
gpgme_data_write (ctx->sig_data, "\r\n", 2);
}
}
/* Continue with next line. */
pos = 0;
}
}
ctx->linebufpos = pos;
return size;
}
int
mime_verify (protocol_t protocol, const char *message, size_t messagelen,
LPMESSAGE mapi_message, HWND hwnd, int preview_mode)
{
gpg_error_t err = 0;
mime_context_t ctx;
const char *s;
size_t len;
char *signature = NULL;
size_t sig_len;
engine_filter_t filter = NULL;
/* Note: PROTOCOL is not used here but figured out directly while
collecting the message. Eventually it might help use setup a
proper verification context right at startup to avoid collecting
all the stuff. However there are a couple of problems with that
- for example we don't know whether gpgsm behaves correctly by
first reading all the data and only the reading the signature. I
guess it is the case but that needs to be checked first. It is
just a performance issue. */
ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
ctx->linebufsize = LINEBUFSIZE;
ctx->hwnd = hwnd;
ctx->preview = preview_mode;
ctx->verify_mode = 1;
ctx->mapi_message = mapi_message;
ctx->mimestruct_tail = &ctx->mimestruct;
ctx->msg = rfc822parse_open (message_cb, ctx);
if (!ctx->msg)
{
err = gpg_error_from_syserror ();
log_error ("%s:%s: failed to open the RFC822 parser: %s",
SRCNAME, __func__, gpg_strerror (err));
goto leave;
}
/* Need to pass the data line by line to the handler. */
while ( (s = memchr (message, '\n', messagelen)) )
{
len = s - message + 1;
/* log_debug ("passing '%.*s'\n", (int)len, message); */
plaintext_handler (ctx, message, len);
if (ctx->parser_error || ctx->line_too_long)
{
err = gpg_error (GPG_ERR_GENERAL);
break;
}
message += len;
assert (messagelen >= len);
messagelen -= len;
}
/* Note: the last character should be a LF, if not we ignore such an
incomplete last line. */
if (ctx->sig_data && gpgme_data_write (ctx->sig_data, "", 1) == 1)
{
signature = gpgme_data_release_and_get_mem (ctx->sig_data, &sig_len);
ctx->sig_data = NULL;
}
/* Now actually verify the signature. */
if (!err && ctx->signed_data && signature)
{
gpgme_data_seek (ctx->signed_data, 0, SEEK_SET);
if ((err=engine_create_filter (&filter, NULL, NULL)))
goto leave;
if ((err=engine_verify_start (filter, hwnd, signature, sig_len,
ctx->protocol)))
goto leave;
/* Filter the data. */
do
{
int nread;
char buffer[4096];
nread = gpgme_data_read (ctx->signed_data, buffer, sizeof buffer);
if (nread < 0)
{
err = gpg_error_from_syserror ();
log_error ("%s:%s: gpgme_data_read failed: %s",
SRCNAME, __func__, gpg_strerror (err));
}
else if (nread)
{
err = engine_filter (filter, buffer, nread);
}
else
break; /* EOF */
}
while (!err);
if (err)
goto leave;
/* Wait for the engine to finish. */
if ((err = engine_filter (filter, NULL, 0)))
goto leave;
if ((err = engine_wait (filter)))
goto leave;
filter = NULL;
}
leave:
gpgme_free (signature);
engine_cancel (filter);
if (ctx)
{
/* Cancel any left open attachment. */
finish_attachment (ctx, 1);
/* Save the body atatchment. */
finish_saved_body (ctx, 0);
rfc822parse_close (ctx->msg);
gpgme_data_release (ctx->signed_data);
gpgme_data_release (ctx->sig_data);
finish_message (mapi_message, err, ctx->protect_mode, ctx->mimestruct);
while (ctx->mimestruct)
{
mimestruct_item_t tmp = ctx->mimestruct->next;
xfree (ctx->mimestruct->filename);
xfree (ctx->mimestruct->charset);
xfree (ctx->mimestruct);
ctx->mimestruct = tmp;
}
symenc_close (ctx->symenc);
symenc_close (ctx->body_saved.symenc);
xfree (ctx);
}
return err;
}
/* A special version of mime_verify which works only for S/MIME opaque
signed messages. The message is expected to be a binary data
stream with a CMS signature. This function passes the entire
message to the crypto engine and then parses the (cleartext) output
for rendering the data. */
int
mime_verify_opaque (protocol_t protocol, LPSTREAM instream,
LPMESSAGE mapi_message, HWND hwnd, int preview_mode)
{
gpg_error_t err = 0;
mime_context_t ctx;
engine_filter_t filter = NULL;
log_debug ("%s:%s: enter (protocol=%d)", SRCNAME, __func__, protocol);
if (protocol != PROTOCOL_SMIME)
return gpg_error (GPG_ERR_INV_VALUE);
ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
ctx->linebufsize = LINEBUFSIZE;
ctx->hwnd = hwnd;
ctx->preview = preview_mode;
ctx->verify_mode = 0;
ctx->mapi_message = mapi_message;
ctx->mimestruct_tail = &ctx->mimestruct;
ctx->msg = rfc822parse_open (message_cb, ctx);
if (!ctx->msg)
{
err = gpg_error_from_syserror ();
log_error ("%s:%s: failed to open the RFC822 parser: %s",
SRCNAME, __func__, gpg_strerror (err));
goto leave;
}
if ((err=engine_create_filter (&filter, plaintext_handler, ctx)))
goto leave;
if ((err=engine_verify_start (filter, hwnd, NULL, 0, protocol)))
goto leave;
/* Filter the stream. */
do
{
HRESULT hr;
ULONG nread;
char buffer[4096];
hr = IStream_Read (instream, buffer, sizeof buffer, &nread);
if (hr)
{
log_error ("%s:%s: IStream::Read failed: hr=%#lx",
SRCNAME, __func__, hr);
err = gpg_error (GPG_ERR_EIO);
}
else if (nread)
{
err = engine_filter (filter, buffer, nread);
}
else
break; /* EOF */
}
while (!err);
if (err)
goto leave;
/* Wait for the engine to finish. */
if ((err = engine_filter (filter, NULL, 0)))
goto leave;
if ((err = engine_wait (filter)))
goto leave;
filter = NULL;
if (ctx->parser_error || ctx->line_too_long)
err = gpg_error (GPG_ERR_GENERAL);
leave:
engine_cancel (filter);
if (ctx)
{
/* Cancel any left over attachment which means that the MIME
structure was not complete. However if we have not seen any
boundary the message is a non-MIME one but we may have
started the body attachment (gpgol000.txt) - this one needs
to be finished properly. */
finish_attachment (ctx, ctx->any_boundary? 1: 0);
/* Save the body attachment. */
finish_saved_body (ctx, 0);
rfc822parse_close (ctx->msg);
if (ctx->signed_data)
gpgme_data_release (ctx->signed_data);
if (ctx->sig_data)
gpgme_data_release (ctx->sig_data);
finish_message (mapi_message, err, ctx->protect_mode, ctx->mimestruct);
while (ctx->mimestruct)
{
mimestruct_item_t tmp = ctx->mimestruct->next;
xfree (ctx->mimestruct->filename);
xfree (ctx->mimestruct->charset);
xfree (ctx->mimestruct);
ctx->mimestruct = tmp;
}
symenc_close (ctx->symenc);
symenc_close (ctx->body_saved.symenc);
xfree (ctx);
}
return err;
}
/* Process the transition to body event in the decryption parser.
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. */
static int
ciphermessage_t2body (mime_context_t ctx, rfc822parse_t msg)
{
rfc822parse_field_t field;
const char *ctmain, *ctsub;
size_t off;
char *p;
int is_text = 0;
/* 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);
}
free (p);
}
/* Process the Content-type and all its parameters. */
/* Fixme: Currently we don't make any use of it but consider all the
content to be the encrypted data. */
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";
}
#ifdef DEBUG_PARSER
log_debug ("%s:%s: ctx=%p, ct=`%s/%s'\n",
SRCNAME, __func__, ctx, ctmain, ctsub);
#endif
rfc822parse_release_field (field); /* (Content-type) */
ctx->in_data = 1;
#ifdef DEBUG_PARSER
log_debug ("%s:%s: this body: nesting=%d part_counter=%d is_text=%d\n",
SRCNAME, __func__,
ctx->nesting_level, ctx->part_counter, is_text);
#endif
return 0;
}
/* This routine gets called by the RFC822 decryption parser for all
kind of events. Should return 0 on success or -1 as well as
setting errno on failure. */
static int
ciphermessage_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
{
int retval = 0;
mime_context_t decctx = opaque;
debug_message_event (decctx, event);
switch (event)
{
case RFC822PARSE_T2BODY:
retval = ciphermessage_t2body (decctx, msg);
break;
case RFC822PARSE_LEVEL_DOWN:
decctx->nesting_level++;
break;
case RFC822PARSE_LEVEL_UP:
if (decctx->nesting_level)
decctx->nesting_level--;
else
{
log_error ("%s: decctx=%p, invalid structure: bad nesting level\n",
SRCNAME, decctx);
decctx->parser_error = 1;
}
break;
case RFC822PARSE_BOUNDARY:
case RFC822PARSE_LAST_BOUNDARY:
decctx->any_boundary = 1;
decctx->in_data = 0;
break;
case RFC822PARSE_BEGIN_HEADER:
decctx->part_counter++;
break;
default: /* Ignore all other events. */
break;
}
return retval;
}
/* This handler is called by us with the MIME message containing the
ciphertext. */
static int
ciphertext_handler (void *handle, const void *buffer, size_t size)
{
mime_context_t ctx = handle;
const char *s;
size_t nleft, pos, len;
gpg_error_t err;
s = buffer;
pos = ctx->linebufpos;
nleft = size;
for (; nleft ; nleft--, s++)
{
if (pos >= ctx->linebufsize)
{
log_error ("%s:%s: ctx=%p, rfc822 parser failed: line too long\n",
SRCNAME, __func__, ctx);
ctx->line_too_long = 1;
return -1; /* Error. */
}
if (*s != '\n')
ctx->linebuf[pos++] = *s;
else
{ /* Got a complete line. Remove the last CR. */
if (pos && ctx->linebuf[pos-1] == '\r')
pos--;
+/* log_debug ("%s:%s: ctx=%p, line=`%.*s'\n", */
+/* SRCNAME, __func__, ctx, (int)pos, ctx->linebuf); */
if (rfc822parse_insert (ctx->msg, ctx->linebuf, pos))
{
log_error ("%s:%s: ctx=%p, rfc822 parser failed: %s\n",
SRCNAME, __func__, ctx, strerror (errno));
ctx->parser_error = 1;
return -1; /* Error. */
}
if (ctx->in_data)
{
/* We are inside the data. That should be the actual
ciphertext in the given encoding. Pass it on to the
crypto engine. */
int slbrk = 0;
if (ctx->is_qp_encoded)
len = qp_decode (ctx->linebuf, pos, &slbrk);
else if (ctx->is_base64_encoded)
len = b64_decode (&ctx->base64, ctx->linebuf, pos);
else
len = pos;
if (len)
err = engine_filter (ctx->outfilter, ctx->linebuf, len);
else
err = 0;
if (!err && !ctx->is_base64_encoded && !slbrk)
{
char tmp[3] = "\r\n";
err = engine_filter (ctx->outfilter, tmp, 2);
}
if (err)
{
log_debug ("%s:%s: sending ciphertext to engine failed: %s",
SRCNAME, __func__, gpg_strerror (err));
ctx->parser_error = 1;
return -1; /* Error. */
}
}
/* Continue with next line. */
pos = 0;
}
}
ctx->linebufpos = pos;
return size;
}
/* Decrypt the PGP or S/MIME message taken from INSTREAM. HWND is the
window to be used for message box and such. In PREVIEW_MODE no
verification will be done, no messages saved and no messages boxes
will pop up. If IS_RFC822 is set, the message is expected to be in
- rfc822 format. */
+ rfc822 format. The caller should send SIMPLE_PGP is the input
+ message is a simple PGP message. */
int
mime_decrypt (protocol_t protocol, LPSTREAM instream, LPMESSAGE mapi_message,
- int is_rfc822, HWND hwnd, int preview_mode)
+ int is_rfc822, int simple_pgp, HWND hwnd, int preview_mode)
{
gpg_error_t err;
mime_context_t decctx, ctx;
engine_filter_t filter = NULL;
log_debug ("%s:%s: enter (protocol=%d, is_rfc822=%d)",
SRCNAME, __func__, protocol, is_rfc822);
if (is_rfc822)
{
decctx = xcalloc (1, sizeof *decctx + LINEBUFSIZE);
decctx->linebufsize = LINEBUFSIZE;
decctx->hwnd = hwnd;
}
else
decctx = NULL;
ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
ctx->linebufsize = LINEBUFSIZE;
ctx->protect_mode = 1;
ctx->hwnd = hwnd;
ctx->preview = preview_mode;
ctx->mapi_message = mapi_message;
ctx->mimestruct_tail = &ctx->mimestruct;
+ ctx->no_mail_header = simple_pgp;
if (decctx)
{
decctx->msg = rfc822parse_open (ciphermessage_cb, decctx);
if (!decctx->msg)
{
err = gpg_error_from_syserror ();
log_error ("%s:%s: failed to open the RFC822 decryption parser: %s",
SRCNAME, __func__, gpg_strerror (err));
goto leave;
}
}
ctx->msg = rfc822parse_open (message_cb, ctx);
if (!ctx->msg)
{
err = gpg_error_from_syserror ();
log_error ("%s:%s: failed to open the RFC822 parser: %s",
SRCNAME, __func__, gpg_strerror (err));
goto leave;
}
/* Prepare the decryption. */
/* title = native_to_utf8 (_("[Encrypted S/MIME message]")); */
/* title = native_to_utf8 (_("[Encrypted PGP/MIME message]")); */
if ((err=engine_create_filter (&filter, plaintext_handler, ctx)))
goto leave;
+ if (simple_pgp)
+ engine_request_exra_lf (filter);
if ((err=engine_decrypt_start (filter, hwnd, protocol, !preview_mode)))
goto leave;
if (decctx)
decctx->outfilter = filter;
/* Filter the stream. */
do
{
HRESULT hr;
ULONG nread;
char buffer[4096];
/* For EOF detection we assume that Read returns no error and
thus NREAD will be 0. The specs say that "Depending on the
implementation, either S_FALSE or an error code could be
returned when reading past the end of the stream"; thus we
are not really sure whether our assumption is correct. At
another place the documentation says that the implementation
used by ISequentialStream exhibits the same EOF behaviour has
found on the MSDOS FAT file system. So we seem to have good
karma. */
hr = IStream_Read (instream, buffer, sizeof buffer, &nread);
if (hr)
{
log_error ("%s:%s: IStream::Read failed: hr=%#lx",
SRCNAME, __func__, hr);
err = gpg_error (GPG_ERR_EIO);
}
else if (nread)
{
if (decctx)
{
ciphertext_handler (decctx, buffer, nread);
if (ctx->parser_error || ctx->line_too_long)
{
err = gpg_error (GPG_ERR_GENERAL);
break;
}
}
else
err = engine_filter (filter, buffer, nread);
}
else
break; /* EOF */
}
while (!err);
if (err)
goto leave;
/* Wait for the engine to finish. */
if ((err = engine_filter (filter, NULL, 0)))
goto leave;
if ((err = engine_wait (filter)))
goto leave;
filter = NULL;
if (ctx->parser_error || ctx->line_too_long)
err = gpg_error (GPG_ERR_GENERAL);
leave:
engine_cancel (filter);
if (ctx)
{
/* Cancel any left over attachment which means that the MIME
structure was not complete. However if we have not seen any
boundary the message is a non-MIME one but we may have
started the body attachment (gpgol000.txt) - this one needs
to be finished properly. */
finish_attachment (ctx, ctx->any_boundary? 1: 0);
/* Save the body attachment. */
finish_saved_body (ctx, 0);
rfc822parse_close (ctx->msg);
if (ctx->signed_data)
gpgme_data_release (ctx->signed_data);
if (ctx->sig_data)
gpgme_data_release (ctx->sig_data);
finish_message (mapi_message, err, ctx->protect_mode, ctx->mimestruct);
while (ctx->mimestruct)
{
mimestruct_item_t tmp = ctx->mimestruct->next;
xfree (ctx->mimestruct->filename);
xfree (ctx->mimestruct->charset);
xfree (ctx->mimestruct);
ctx->mimestruct = tmp;
}
symenc_close (ctx->symenc);
symenc_close (ctx->body_saved.symenc);
xfree (ctx);
}
if (decctx)
{
rfc822parse_close (decctx->msg);
xfree (decctx);
}
return err;
}
diff --git a/src/mimeparser.h b/src/mimeparser.h
index 3c687b9..0343174 100644
--- a/src/mimeparser.h
+++ b/src/mimeparser.h
@@ -1,44 +1,44 @@
/* mimeparser.h - MIME parser.
* Copyright (C) 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifndef MIMEPARSER_H
#define MIMEPARSER_H
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
int mime_verify (protocol_t protocol, const char *message, size_t messagelen,
LPMESSAGE mapi_message,
HWND hwnd, int preview_mode);
int mime_verify_opaque (protocol_t protocol, LPSTREAM instream,
LPMESSAGE mapi_message, HWND hwnd, int preview_mode);
int mime_decrypt (protocol_t protocol,
LPSTREAM instream, LPMESSAGE mapi_message, int is_rfc822,
- HWND hwnd, int preview_mode);
+ int simple_pgp, HWND hwnd, int preview_mode);
#ifdef __cplusplus
}
#endif
#endif /*MIMEPARSER_H*/
diff --git a/src/user-events.cpp b/src/user-events.cpp
index 1d19860..f4a960f 100644
--- a/src/user-events.cpp
+++ b/src/user-events.cpp
@@ -1,185 +1,188 @@
/* user-events.cpp - Subclass impl of IExchExtUserEvents
* Copyright (C) 2007, 2008 g10 Code 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <windows.h>
#include "mymapi.h"
#include "mymapitags.h"
#include "myexchext.h"
#include "display.h"
#include "common.h"
#include "msgcache.h"
#include "engine.h"
#include "mapihelp.h"
#include "olflange-def.h"
#include "olflange.h"
#include "user-events.h"
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
/* Wrapper around UlRelease with error checking. */
static void
ul_release (LPVOID punk, const char *func, int lnr)
{
ULONG res;
if (!punk)
return;
res = UlRelease (punk);
if (opt.enable_debug & DBG_MEMORY)
log_debug ("%s:%s:%d: UlRelease(%p) had %lu references\n",
SRCNAME, func, lnr, punk, res);
}
/* Our constructor. */
GpgolUserEvents::GpgolUserEvents (GpgolExt *pParentInterface)
{
m_pExchExt = pParentInterface;
m_lRef = 0;
}
/* The QueryInterface which does the actual subclassing. */
STDMETHODIMP
GpgolUserEvents::QueryInterface (REFIID riid, LPVOID FAR *ppvObj)
{
*ppvObj = NULL;
if (riid == IID_IExchExtUserEvents)
{
*ppvObj = (LPVOID)this;
AddRef();
return S_OK;
}
if (riid == IID_IUnknown)
{
*ppvObj = (LPVOID)m_pExchExt;
m_pExchExt->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
/* Called from Outlook for all selection changes.
PEECB is a pointer to the IExchExtCallback interface. */
STDMETHODIMP_ (VOID)
GpgolUserEvents::OnSelectionChange (LPEXCHEXTCALLBACK eecb)
{
HRESULT hr;
ULONG count, objtype, msgflags;
char msgclass[256];
LPENTRYID entryid = NULL;
ULONG entryidlen;
- log_debug ("%s:%s: received\n", SRCNAME, __func__);
+ if (debug_commands)
+ log_debug ("%s:%s: received\n", SRCNAME, __func__);
hr = eecb->GetSelectionCount (&count);
if (SUCCEEDED (hr) && count > 0)
{
/* Get the first selected item. */
hr = eecb->GetSelectionItem (0L, &entryidlen, &entryid, &objtype,
msgclass, sizeof msgclass -1,
&msgflags, 0L);
if (SUCCEEDED(hr) && objtype == MAPI_MESSAGE)
{
- log_debug ("%s:%s: message class: %s\n",
- SRCNAME, __func__, msgclass);
+ if (debug_commands)
+ log_debug ("%s:%s: message class: %s\n",
+ SRCNAME, __func__, msgclass);
/* If SMIME has been enabled and the current message is of
class SMIME or in the past processed by CryptoEx, we
change the message class.
Note that there is a report on the Net that OL2007
crashes when changing the message here. */
if (opt.enable_smime
&& (!strncmp (msgclass, "IPM.Note.SMIME", 14)
|| !strncmp (msgclass, "IPM.Note.Secure.Cex", 19)))
{
LPMAPIFOLDER folder = NULL;
LPMDB mdb = NULL;
LPMESSAGE message = NULL;
if (entryid)
log_hexdump (entryid, entryidlen, "selected entryid=");
else
log_debug ("no selected entry id");
hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&folder);
if (SUCCEEDED (hr) && entryid)
{
hr = mdb->OpenEntry (entryidlen, entryid,
&IID_IMessage, MAPI_BEST_ACCESS,
&objtype, (IUnknown**)&message);
if (SUCCEEDED (hr))
{
if (objtype == MAPI_MESSAGE)
{
log_debug ("%s:%s: about to change or sync "
"the message class",
SRCNAME, __func__);
/* We sync the message class here to get rid
of IPM.Note.SMIME etc. */
mapi_change_message_class (message, 1);
}
}
else
log_error ("%s:%s: OpenEntry failed: hr=%#lx\n",
SRCNAME, __func__, hr);
ul_release (message, __func__, __LINE__);
}
ul_release (folder, __func__, __LINE__);
ul_release (mdb, __func__, __LINE__);
}
}
else if (SUCCEEDED(hr) && objtype == MAPI_FOLDER)
{
- log_debug ("%s:%s: objtype: %lu\n",
- SRCNAME, __func__, objtype);
+ if (debug_commands)
+ log_debug ("%s:%s: objtype: %lu\n",
+ SRCNAME, __func__, objtype);
}
}
if (entryid)
MAPIFreeBuffer (entryid);
}
/* I assume this is called from Outlook for all object changes.
PEECB is a pointer to the IExchExtCallback interface. */
STDMETHODIMP_ (VOID)
GpgolUserEvents::OnObjectChange (LPEXCHEXTCALLBACK eecb)
{
- log_debug ("%s:%s: received\n", SRCNAME, __func__);
-
+ if (debug_commands)
+ log_debug ("%s:%s: received\n", SRCNAME, __func__);
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 8, 12:15 PM (11 h, 15 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ad/c1/2e9f005682c5960baa5188314c6b

Event Timeline