diff --git a/.gitignore b/.gitignore index a8d3075..8f08b05 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ coverage Gemfile.lock /tmp/* /ports/* +.byebug_history diff --git a/ext/gpgme/gpgme_n.c b/ext/gpgme/gpgme_n.c index a1ccd24..b02aa26 100644 --- a/ext/gpgme/gpgme_n.c +++ b/ext/gpgme/gpgme_n.c @@ -514,6 +514,46 @@ rb_s_gpgme_release (VALUE dummy, VALUE vctx) return Qnil; } +static VALUE +rb_s_gpgme_set_ctx_flag (VALUE dummy, VALUE vctx, VALUE vname, VALUE vstr) +{ + gpgme_ctx_t ctx; + gpgme_error_t err; + char* name; + char* str; + + name = StringValueCStr(vname); + str = StringValueCStr(vstr); + + UNWRAP_GPGME_CTX(vctx, ctx); + if (!ctx) + rb_raise (rb_eArgError, "released ctx"); + + err = gpgme_set_ctx_flag(ctx, name, str); + return LONG2NUM(err); +} + +static VALUE +rb_s_gpgme_get_ctx_flag (VALUE dummy, VALUE vctx, VALUE vname) +{ + gpgme_ctx_t ctx; + const char* name; + int yes; + + name = StringValueCStr(vname); + + UNWRAP_GPGME_CTX(vctx, ctx); + if (!ctx) + rb_raise (rb_eArgError, "released ctx"); + + const char* result; + result = gpgme_get_ctx_flag(ctx, name); + if (result == NULL) + rb_raise (rb_eArgError, "incorrect ctx flag name"); + + return rb_str_new_cstr(result); +} + static VALUE rb_s_gpgme_set_protocol (VALUE dummy, VALUE vctx, VALUE vproto) { @@ -566,6 +606,42 @@ rb_s_gpgme_get_armor (VALUE dummy, VALUE vctx) return INT2FIX(yes); } +static VALUE +rb_s_gpgme_set_ignore_mdc_error (VALUE dummy, VALUE vctx, VALUE vyes) +{ + gpgme_ctx_t ctx; + gpgme_error_t err; + int yes; + + yes = NUM2INT(vyes); + + UNWRAP_GPGME_CTX(vctx, ctx); + if (!ctx) + rb_raise (rb_eArgError, "released ctx"); + + err = gpgme_set_ctx_flag(ctx, "ignore-mdc-error", yes ? "1" : ""); + return LONG2NUM(err); +} + +static VALUE +rb_s_gpgme_get_ignore_mdc_error (VALUE dummy, VALUE vctx) +{ + gpgme_ctx_t ctx; + int yes; + + UNWRAP_GPGME_CTX(vctx, ctx); + if (!ctx) + rb_raise (rb_eArgError, "released ctx"); + + const char* result; + result = gpgme_get_ctx_flag(ctx, "ignore-mdc-error"); + if (result == NULL) + rb_raise (rb_eArgError, "incorrect ctx flag name"); + + yes = (result && *result)? !!atoi (result) : 0; + return INT2FIX(yes); +} + static VALUE rb_s_gpgme_set_textmode (VALUE dummy, VALUE vctx, VALUE vyes) { @@ -2373,6 +2449,10 @@ Init_gpgme_n (void) rb_s_gpgme_release, 1); /* Context Attributes */ + rb_define_module_function (mGPGME, "gpgme_set_ctx_flag", + rb_s_gpgme_set_ctx_flag, 3); + rb_define_module_function (mGPGME, "gpgme_get_ctx_flag", + rb_s_gpgme_get_ctx_flag, 2); rb_define_module_function (mGPGME, "gpgme_set_protocol", rb_s_gpgme_set_protocol, 2); rb_define_module_function (mGPGME, "gpgme_get_protocol", @@ -2381,6 +2461,10 @@ Init_gpgme_n (void) rb_s_gpgme_set_armor, 2); rb_define_module_function (mGPGME, "gpgme_get_armor", rb_s_gpgme_get_armor, 1); + rb_define_module_function (mGPGME, "gpgme_set_ignore_mdc_error", + rb_s_gpgme_set_ignore_mdc_error, 2); + rb_define_module_function (mGPGME, "gpgme_get_ignore_mdc_error", + rb_s_gpgme_get_ignore_mdc_error, 1); rb_define_module_function (mGPGME, "gpgme_set_textmode", rb_s_gpgme_set_textmode, 2); rb_define_module_function (mGPGME, "gpgme_get_textmode", diff --git a/lib/gpgme/ctx.rb b/lib/gpgme/ctx.rb index 45a0371..a1e0600 100644 --- a/lib/gpgme/ctx.rb +++ b/lib/gpgme/ctx.rb @@ -49,12 +49,13 @@ def self.new(options = {}) raise exc if exc ctx = rctx[0] - ctx.protocol = options[:protocol] if options[:protocol] - ctx.armor = options[:armor] if options[:armor] - ctx.textmode = options[:textmode] if options[:textmode] - ctx.keylist_mode = options[:keylist_mode] if options[:keylist_mode] - ctx.pinentry_mode = options[:pinentry_mode] if options[:pinentry_mode] - ctx.offline = options[:offline] if options[:offline] + ctx.protocol = options[:protocol] if options[:protocol] + ctx.armor = options[:armor] if options[:armor] + ctx.textmode = options[:textmode] if options[:textmode] + ctx.keylist_mode = options[:keylist_mode] if options[:keylist_mode] + ctx.pinentry_mode = options[:pinentry_mode] if options[:pinentry_mode] + ctx.offline = options[:offline] if options[:offline] + ctx.ignore_mdc_error = options[:ignore_mdc_error] if options[:ignore_mdc_error] if options[:password] ctx.set_passphrase_callback GPGME::Ctx.method(:pass_function), @@ -103,6 +104,43 @@ def release # Getters and setters ## + # Get the value of the Ctx flag with the given name. + # + # Allowed flag names may include: + # - 'redraw' + # - 'full-status' + # - 'raw-description' + # - 'export-session-key' + # - 'override-session-key' + # - 'include-key-block' + # - 'auto-key-import' + # - 'auto-key-retrieve' + # - 'request-origin' + # - 'no-symkey-cache' + # - 'ignore-mdc-error' + # - 'auto-key-locate' + # - 'trust-model' + # - 'extended-edit' + # - 'cert-expire' + # - 'key-origin' + # - 'import-filter' + # - 'no-auto-check-trustdb' + # + # Please consult the GPGPME documentation for more details + # + def get_ctx_flag(flag_name) + GPGME::gpgme_get_ctx_flag(self, flag_name.to_s) + end + + # Set the Ctx flag with the given name + # to the given value. + def set_ctx_flag(flag_name, val) + err = GPGME::gpgme_set_ctx_flag(self, flag_name.to_s, val.to_s) + exc = GPGME::error_to_exception(err) + raise exc if exc + val + end + # Set the +protocol+ used within this context. See {GPGME::Ctx.new} for # possible values. def protocol=(proto) @@ -128,6 +166,22 @@ def armor GPGME::gpgme_get_armor(self) == 1 ? true : false end + # This option ignores a MDC integrity protection failure. + # It is required to decrypt old messages which did not use an MDC. + # It may also be useful if a message is partially garbled, + # but it is necessary to get as much data as possible out of that garbled message. + # Be aware that a missing or failed MDC can be an indication of an attack. + # Use with great caution. + def ignore_mdc_error=(yes) + GPGME::gpgme_set_ignore_mdc_error(self, yes ? 1 : 0) + yes + end + + # Return true if the MDC integrity protection is disabled. + def ignore_mdc_error + GPGME::gpgme_get_ignore_mdc_error(self) == 1 ? true : false + end + # Tell whether canonical text mode should be used. def textmode=(yes) GPGME::gpgme_set_textmode(self, yes ? 1 : 0) diff --git a/test/ctx_test.rb b/test/ctx_test.rb index 187bad2..01df13a 100644 --- a/test/ctx_test.rb +++ b/test/ctx_test.rb @@ -81,6 +81,70 @@ end end + describe :get_ctx_flag do + it "reads flags with getters and setters" do + ctx = GPGME::Ctx.new + + refute ctx.ignore_mdc_error + assert_equal "", ctx.get_ctx_flag("ignore-mdc-error") + + ctx.ignore_mdc_error = true + + assert ctx.ignore_mdc_error + assert_equal "1", ctx.get_ctx_flag("ignore-mdc-error") + end + + it "can get flags without getters and setters" do + ctx = GPGME::Ctx.new + + assert_equal "", ctx.get_ctx_flag("auto-key-locate") + ctx.set_ctx_flag("auto-key-locate", "cert") + assert_equal "cert", ctx.get_ctx_flag("auto-key-locate") + end + + it "raises an error when a flag doesn't exist" do + ctx = GPGME::Ctx.new + + assert_raises ArgumentError do + ctx.get_ctx_flag("foo") + end + end + end + + describe :set_ctx_flag do + it "sets the value for a flag with a getter" do + ctx = GPGME::Ctx.new + refute ctx.ignore_mdc_error + + ctx.set_ctx_flag("ignore-mdc-error", "1") + assert ctx.ignore_mdc_error + end + + it "unsets the value for a flag with a getter" do + ctx = GPGME::Ctx.new(ignore_mdc_error: true) + assert ctx.ignore_mdc_error + + ctx.set_ctx_flag("ignore-mdc-error", "0") + refute ctx.ignore_mdc_error + end + + it "can set flags without getters and setters" do + ctx = GPGME::Ctx.new + + assert_equal "", ctx.get_ctx_flag("auto-key-locate") + ctx.set_ctx_flag("auto-key-locate", "cert") + assert_equal "cert", ctx.get_ctx_flag("auto-key-locate") + end + + it "raises an error when a flag doesn't exist" do + ctx = GPGME::Ctx.new + + assert_raises GPGME::Error do + ctx.set_ctx_flag("foo", "bar") + end + end + end + describe :armor do it "sets false by default" do ctx = GPGME::Ctx.new @@ -101,6 +165,36 @@ end end + describe :ignore_mdc_error do + it "sets false by default" do + ctx = GPGME::Ctx.new + refute ctx.ignore_mdc_error + end + + it "can set" do + ctx = GPGME::Ctx.new + + ctx.ignore_mdc_error = true + assert ctx.ignore_mdc_error + end + + it "can unset" do + ctx = GPGME::Ctx.new(ignore_mdc_error: true) + assert ctx.ignore_mdc_error + + ctx.ignore_mdc_error = false + refute ctx.ignore_mdc_error + end + + it "can set and get in constructor" do + ctx = GPGME::Ctx.new(:ignore_mdc_error => false) + refute ctx.ignore_mdc_error + + ctx = GPGME::Ctx.new(:ignore_mdc_error => true) + assert ctx.ignore_mdc_error + end + end + describe :protocol do it "sets 0 by default" do ctx = GPGME::Ctx.new