gpgme 2.0.25 → 2.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/gpgme/extconf.rb +13 -0
- data/ext/gpgme/gpgme_n.c +485 -12
- data/lib/gpgme/constants.rb +18 -0
- data/lib/gpgme/crypto.rb +56 -1
- data/lib/gpgme/ctx.rb +25 -6
- data/lib/gpgme/io_callbacks.rb +6 -1
- data/lib/gpgme/version.rb +1 -1
- data/lib/gpgme.rb +56 -1
- data/test/ctx_test.rb +1 -4
- data/test/encryption_flags_test.rb +65 -0
- data/test/io_callbacks_test.rb +169 -0
- metadata +4 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6fe08b2c986a5e8b47b37c96aec3bca4a490ae41ec56d8b596746e3392632e11
|
|
4
|
+
data.tar.gz: 15191272b4aefcf3df3cfec3d2bccc51eb41863036e8e746bf68a22f6c22e323
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa950f73817ce3afce83d1f0d2fb5ef9c2d1dc7a251f12376c079ef6b685f8d7b83f5242df381b1cac3632e5aa4946f8eb38947ad3d3e1b64ee24b7ddc2c6a5a
|
|
7
|
+
data.tar.gz: a77cf22e0fc1c41936c537a3a47a4c651ec1f8b2d7e9a8a0708d99ffc011b887b339cc57ecf82114370f3a644ce27eb6e6e4483e5277db8af03dc05d89dc854e
|
data/ext/gpgme/extconf.rb
CHANGED
|
@@ -225,6 +225,19 @@ End
|
|
|
225
225
|
end
|
|
226
226
|
|
|
227
227
|
have_func('gpgme_op_export_keys')
|
|
228
|
+
have_const('GPGME_ENCRYPT_ALWAYS_TRUST', 'gpgme.h')
|
|
229
|
+
have_const('GPGME_ENCRYPT_NO_ENCRYPT_TO', 'gpgme.h')
|
|
230
|
+
have_const('GPGME_ENCRYPT_PREPARE', 'gpgme.h')
|
|
231
|
+
have_const('GPGME_ENCRYPT_EXPECT_SIGN', 'gpgme.h')
|
|
232
|
+
have_const('GPGME_ENCRYPT_NO_COMPRESS', 'gpgme.h')
|
|
233
|
+
have_const('GPGME_ENCRYPT_SYMMETRIC', 'gpgme.h')
|
|
234
|
+
have_const('GPGME_ENCRYPT_THROW_KEYIDS', 'gpgme.h')
|
|
235
|
+
have_const('GPGME_ENCRYPT_WRAP', 'gpgme.h')
|
|
236
|
+
have_const('GPGME_ENCRYPT_WANT_ADDRESS', 'gpgme.h')
|
|
237
|
+
have_const('GPGME_ENCRYPT_ARCHIVE', 'gpgme.h')
|
|
238
|
+
have_const('GPGME_ENCRYPT_FILE', 'gpgme.h')
|
|
239
|
+
have_const('GPGME_ENCRYPT_ADD_RECP', 'gpgme.h')
|
|
240
|
+
have_const('GPGME_ENCRYPT_CHG_RECP', 'gpgme.h')
|
|
228
241
|
|
|
229
242
|
create_makefile ('gpgme_n')
|
|
230
243
|
|
data/ext/gpgme/gpgme_n.c
CHANGED
|
@@ -88,30 +88,105 @@
|
|
|
88
88
|
#define RSTRING_LEN(a) RSTRING(a)->len
|
|
89
89
|
#endif
|
|
90
90
|
|
|
91
|
+
/* TypedData type definitions for Ruby 3.x compatibility */
|
|
92
|
+
static void
|
|
93
|
+
gpgme_data_free(void *ptr)
|
|
94
|
+
{
|
|
95
|
+
if (ptr)
|
|
96
|
+
gpgme_data_release((gpgme_data_t)ptr);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static const rb_data_type_t gpgme_data_type = {
|
|
100
|
+
.wrap_struct_name = "GPGME::Data",
|
|
101
|
+
.function = {
|
|
102
|
+
.dmark = NULL,
|
|
103
|
+
.dfree = gpgme_data_free,
|
|
104
|
+
.dsize = NULL,
|
|
105
|
+
},
|
|
106
|
+
.data = NULL,
|
|
107
|
+
.flags = 0,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
static void
|
|
111
|
+
gpgme_ctx_free(void *ptr)
|
|
112
|
+
{
|
|
113
|
+
if (ptr)
|
|
114
|
+
gpgme_release((gpgme_ctx_t)ptr);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static const rb_data_type_t gpgme_ctx_type = {
|
|
118
|
+
.wrap_struct_name = "GPGME::Ctx",
|
|
119
|
+
.function = {
|
|
120
|
+
.dmark = NULL,
|
|
121
|
+
.dfree = gpgme_ctx_free,
|
|
122
|
+
.dsize = NULL,
|
|
123
|
+
},
|
|
124
|
+
.data = NULL,
|
|
125
|
+
.flags = 0,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
static void
|
|
129
|
+
gpgme_key_free(void *ptr)
|
|
130
|
+
{
|
|
131
|
+
if (ptr)
|
|
132
|
+
gpgme_key_unref((gpgme_key_t)ptr);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static const rb_data_type_t gpgme_key_type = {
|
|
136
|
+
.wrap_struct_name = "GPGME::Key",
|
|
137
|
+
.function = {
|
|
138
|
+
.dmark = NULL,
|
|
139
|
+
.dfree = gpgme_key_free,
|
|
140
|
+
.dsize = NULL,
|
|
141
|
+
},
|
|
142
|
+
.data = NULL,
|
|
143
|
+
.flags = 0,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
#if defined(GPGME_VERSION_NUMBER) && GPGME_VERSION_NUMBER < 0x020000
|
|
147
|
+
static void
|
|
148
|
+
gpgme_trust_item_free(void *ptr)
|
|
149
|
+
{
|
|
150
|
+
if (ptr)
|
|
151
|
+
gpgme_trust_item_unref((gpgme_trust_item_t)ptr);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static const rb_data_type_t gpgme_trust_item_type = {
|
|
155
|
+
.wrap_struct_name = "GPGME::TrustItem",
|
|
156
|
+
.function = {
|
|
157
|
+
.dmark = NULL,
|
|
158
|
+
.dfree = gpgme_trust_item_free,
|
|
159
|
+
.dsize = NULL,
|
|
160
|
+
},
|
|
161
|
+
.data = NULL,
|
|
162
|
+
.flags = 0,
|
|
163
|
+
};
|
|
164
|
+
#endif
|
|
165
|
+
|
|
91
166
|
#define WRAP_GPGME_DATA(dh) \
|
|
92
|
-
|
|
167
|
+
TypedData_Wrap_Struct(cData, &gpgme_data_type, dh)
|
|
93
168
|
/* `gpgme_data_t' is typedef'ed as `struct gpgme_data *'. */
|
|
94
169
|
#define UNWRAP_GPGME_DATA(vdh, dh) \
|
|
95
|
-
|
|
170
|
+
TypedData_Get_Struct(vdh, struct gpgme_data, &gpgme_data_type, dh)
|
|
96
171
|
|
|
97
172
|
#define WRAP_GPGME_CTX(ctx) \
|
|
98
|
-
|
|
173
|
+
TypedData_Wrap_Struct(cCtx, &gpgme_ctx_type, ctx)
|
|
99
174
|
/* `gpgme_ctx_t' is typedef'ed as `struct gpgme_context *'. */
|
|
100
175
|
#define UNWRAP_GPGME_CTX(vctx, ctx) \
|
|
101
|
-
|
|
176
|
+
TypedData_Get_Struct(vctx, struct gpgme_context, &gpgme_ctx_type, ctx)
|
|
102
177
|
|
|
103
178
|
#define WRAP_GPGME_KEY(key) \
|
|
104
|
-
|
|
179
|
+
TypedData_Wrap_Struct(cKey, &gpgme_key_type, key)
|
|
105
180
|
/* `gpgme_key_t' is typedef'ed as `struct _gpgme_key *'. */
|
|
106
181
|
#define UNWRAP_GPGME_KEY(vkey, key) \
|
|
107
|
-
|
|
182
|
+
TypedData_Get_Struct(vkey, struct _gpgme_key, &gpgme_key_type, key)
|
|
108
183
|
|
|
109
184
|
#if defined(GPGME_VERSION_NUMBER) && GPGME_VERSION_NUMBER < 0x020000
|
|
110
185
|
#define WRAP_GPGME_TRUST_ITEM(item) \
|
|
111
|
-
|
|
186
|
+
TypedData_Wrap_Struct(cTrustItem, &gpgme_trust_item_type, item)
|
|
112
187
|
/* `gpgme_trust_item_t' is typedef'ed as `struct _gpgme_trust_item *'. */
|
|
113
188
|
#define UNWRAP_GPGME_TRUST_ITEM(vitem, item) \
|
|
114
|
-
|
|
189
|
+
TypedData_Get_Struct(vitem, struct _gpgme_trust_item, &gpgme_trust_item_type, item)
|
|
115
190
|
#endif
|
|
116
191
|
|
|
117
192
|
static VALUE cEngineInfo,
|
|
@@ -345,11 +420,21 @@ static ssize_t
|
|
|
345
420
|
write_cb (void *handle, const void *buffer, size_t size)
|
|
346
421
|
{
|
|
347
422
|
VALUE vcb = (VALUE)handle, vcbs, vhook_value, vbuffer, vnwrite;
|
|
423
|
+
rb_encoding *enc;
|
|
348
424
|
|
|
349
425
|
vcbs = RARRAY_PTR(vcb)[0];
|
|
350
426
|
vhook_value = RARRAY_PTR(vcb)[1];
|
|
351
427
|
vbuffer = rb_str_new (buffer, size);
|
|
352
428
|
|
|
429
|
+
/* Associate encoding with the buffer string.
|
|
430
|
+
* Use default internal encoding if set, otherwise use binary encoding.
|
|
431
|
+
* This allows the app author to control the encoding via
|
|
432
|
+
* Encoding.default_internal while maintaining backward compatibility. */
|
|
433
|
+
enc = rb_default_internal_encoding ();
|
|
434
|
+
if (!enc)
|
|
435
|
+
enc = rb_ascii8bit_encoding ();
|
|
436
|
+
rb_enc_associate (vbuffer, enc);
|
|
437
|
+
|
|
353
438
|
vnwrite = rb_funcall (vcbs, rb_intern ("write"), 3,
|
|
354
439
|
vhook_value, vbuffer, LONG2NUM(size));
|
|
355
440
|
return NUM2LONG(vnwrite);
|
|
@@ -514,7 +599,7 @@ rb_s_gpgme_release (VALUE dummy, VALUE vctx)
|
|
|
514
599
|
if (!ctx)
|
|
515
600
|
rb_raise (rb_eArgError, "released ctx");
|
|
516
601
|
gpgme_release (ctx);
|
|
517
|
-
|
|
602
|
+
RTYPEDDATA_DATA(vctx) = NULL;
|
|
518
603
|
return Qnil;
|
|
519
604
|
}
|
|
520
605
|
|
|
@@ -1125,6 +1210,9 @@ rb_s_gpgme_op_keylist_next (VALUE dummy, VALUE vctx, VALUE rkey)
|
|
|
1125
1210
|
save_gpgme_key_attrs (vkey, key);
|
|
1126
1211
|
rb_ary_store (rkey, 0, vkey);
|
|
1127
1212
|
}
|
|
1213
|
+
|
|
1214
|
+
RB_GC_GUARD(vctx);
|
|
1215
|
+
|
|
1128
1216
|
return LONG2NUM(err);
|
|
1129
1217
|
}
|
|
1130
1218
|
|
|
@@ -1540,6 +1628,305 @@ rb_s_gpgme_op_delete_start (VALUE dummy, VALUE vctx, VALUE vkey,
|
|
|
1540
1628
|
return LONG2NUM(err);
|
|
1541
1629
|
}
|
|
1542
1630
|
|
|
1631
|
+
/*
|
|
1632
|
+
* Key Edit Interface
|
|
1633
|
+
*
|
|
1634
|
+
* GPGME 2.0.0 deprecated gpgme_op_edit, gpgme_op_edit_start, gpgme_op_card_edit,
|
|
1635
|
+
* and gpgme_op_card_edit_start in favor of gpgme_op_interact and gpgme_op_interact_start.
|
|
1636
|
+
*
|
|
1637
|
+
* The new interact callback has a different signature:
|
|
1638
|
+
* Old: gpgme_error_t (*)(void *opaque, gpgme_status_code_t status, const char *args, int fd)
|
|
1639
|
+
* New: gpgme_error_t (*)(void *opaque, const char *keyword, const char *args, int fd)
|
|
1640
|
+
*
|
|
1641
|
+
* For backward compatibility, we keep the same Ruby interface (gpgme_op_edit, etc.)
|
|
1642
|
+
* but internally use gpgme_op_interact for GPGME 2.0.0+, converting keyword strings
|
|
1643
|
+
* to status codes in the callback shim.
|
|
1644
|
+
*/
|
|
1645
|
+
|
|
1646
|
+
#if defined(GPGME_VERSION_NUMBER) && GPGME_VERSION_NUMBER >= 0x020000
|
|
1647
|
+
/*
|
|
1648
|
+
* GPGME 2.0.0+: Use gpgme_op_interact internally, but expose the same
|
|
1649
|
+
* gpgme_op_edit interface to Ruby for backward compatibility.
|
|
1650
|
+
*
|
|
1651
|
+
* The shim callback converts keyword strings to status codes.
|
|
1652
|
+
*/
|
|
1653
|
+
|
|
1654
|
+
/* Macro to define keyword-to-status mapping entries */
|
|
1655
|
+
#define STATUS_ENTRY(x) { #x, GPGME_STATUS_##x }
|
|
1656
|
+
|
|
1657
|
+
/* Mapping table from keyword strings to status codes */
|
|
1658
|
+
static struct {
|
|
1659
|
+
const char *keyword;
|
|
1660
|
+
gpgme_status_code_t status;
|
|
1661
|
+
} keyword_to_status[] = {
|
|
1662
|
+
STATUS_ENTRY(EOF),
|
|
1663
|
+
STATUS_ENTRY(ENTER),
|
|
1664
|
+
STATUS_ENTRY(LEAVE),
|
|
1665
|
+
STATUS_ENTRY(ABORT),
|
|
1666
|
+
STATUS_ENTRY(GOODSIG),
|
|
1667
|
+
STATUS_ENTRY(BADSIG),
|
|
1668
|
+
STATUS_ENTRY(ERRSIG),
|
|
1669
|
+
STATUS_ENTRY(BADARMOR),
|
|
1670
|
+
STATUS_ENTRY(RSA_OR_IDEA),
|
|
1671
|
+
STATUS_ENTRY(KEYEXPIRED),
|
|
1672
|
+
STATUS_ENTRY(KEYREVOKED),
|
|
1673
|
+
STATUS_ENTRY(TRUST_UNDEFINED),
|
|
1674
|
+
STATUS_ENTRY(TRUST_NEVER),
|
|
1675
|
+
STATUS_ENTRY(TRUST_MARGINAL),
|
|
1676
|
+
STATUS_ENTRY(TRUST_FULLY),
|
|
1677
|
+
STATUS_ENTRY(TRUST_ULTIMATE),
|
|
1678
|
+
STATUS_ENTRY(SHM_INFO),
|
|
1679
|
+
STATUS_ENTRY(SHM_GET),
|
|
1680
|
+
STATUS_ENTRY(SHM_GET_BOOL),
|
|
1681
|
+
STATUS_ENTRY(SHM_GET_HIDDEN),
|
|
1682
|
+
STATUS_ENTRY(NEED_PASSPHRASE),
|
|
1683
|
+
STATUS_ENTRY(VALIDSIG),
|
|
1684
|
+
STATUS_ENTRY(SIG_ID),
|
|
1685
|
+
STATUS_ENTRY(ENC_TO),
|
|
1686
|
+
STATUS_ENTRY(NODATA),
|
|
1687
|
+
STATUS_ENTRY(BAD_PASSPHRASE),
|
|
1688
|
+
STATUS_ENTRY(NO_PUBKEY),
|
|
1689
|
+
STATUS_ENTRY(NO_SECKEY),
|
|
1690
|
+
STATUS_ENTRY(NEED_PASSPHRASE_SYM),
|
|
1691
|
+
STATUS_ENTRY(DECRYPTION_FAILED),
|
|
1692
|
+
STATUS_ENTRY(DECRYPTION_OKAY),
|
|
1693
|
+
STATUS_ENTRY(MISSING_PASSPHRASE),
|
|
1694
|
+
STATUS_ENTRY(GOOD_PASSPHRASE),
|
|
1695
|
+
STATUS_ENTRY(GOODMDC),
|
|
1696
|
+
STATUS_ENTRY(BADMDC),
|
|
1697
|
+
STATUS_ENTRY(ERRMDC),
|
|
1698
|
+
STATUS_ENTRY(IMPORTED),
|
|
1699
|
+
STATUS_ENTRY(IMPORT_OK),
|
|
1700
|
+
STATUS_ENTRY(IMPORT_PROBLEM),
|
|
1701
|
+
STATUS_ENTRY(IMPORT_RES),
|
|
1702
|
+
STATUS_ENTRY(FILE_START),
|
|
1703
|
+
STATUS_ENTRY(FILE_DONE),
|
|
1704
|
+
STATUS_ENTRY(FILE_ERROR),
|
|
1705
|
+
STATUS_ENTRY(BEGIN_DECRYPTION),
|
|
1706
|
+
STATUS_ENTRY(END_DECRYPTION),
|
|
1707
|
+
STATUS_ENTRY(BEGIN_ENCRYPTION),
|
|
1708
|
+
STATUS_ENTRY(END_ENCRYPTION),
|
|
1709
|
+
STATUS_ENTRY(DELETE_PROBLEM),
|
|
1710
|
+
STATUS_ENTRY(GET_BOOL),
|
|
1711
|
+
STATUS_ENTRY(GET_LINE),
|
|
1712
|
+
STATUS_ENTRY(GET_HIDDEN),
|
|
1713
|
+
STATUS_ENTRY(GOT_IT),
|
|
1714
|
+
STATUS_ENTRY(PROGRESS),
|
|
1715
|
+
STATUS_ENTRY(SIG_CREATED),
|
|
1716
|
+
STATUS_ENTRY(SESSION_KEY),
|
|
1717
|
+
STATUS_ENTRY(NOTATION_NAME),
|
|
1718
|
+
STATUS_ENTRY(NOTATION_DATA),
|
|
1719
|
+
STATUS_ENTRY(POLICY_URL),
|
|
1720
|
+
STATUS_ENTRY(BEGIN_STREAM),
|
|
1721
|
+
STATUS_ENTRY(END_STREAM),
|
|
1722
|
+
STATUS_ENTRY(KEY_CREATED),
|
|
1723
|
+
STATUS_ENTRY(USERID_HINT),
|
|
1724
|
+
STATUS_ENTRY(UNEXPECTED),
|
|
1725
|
+
STATUS_ENTRY(INV_RECP),
|
|
1726
|
+
STATUS_ENTRY(NO_RECP),
|
|
1727
|
+
STATUS_ENTRY(ALREADY_SIGNED),
|
|
1728
|
+
STATUS_ENTRY(SIGEXPIRED),
|
|
1729
|
+
STATUS_ENTRY(EXPSIG),
|
|
1730
|
+
STATUS_ENTRY(EXPKEYSIG),
|
|
1731
|
+
STATUS_ENTRY(TRUNCATED),
|
|
1732
|
+
STATUS_ENTRY(ERROR),
|
|
1733
|
+
STATUS_ENTRY(NEWSIG),
|
|
1734
|
+
STATUS_ENTRY(REVKEYSIG),
|
|
1735
|
+
STATUS_ENTRY(SIG_SUBPACKET),
|
|
1736
|
+
STATUS_ENTRY(NEED_PASSPHRASE_PIN),
|
|
1737
|
+
STATUS_ENTRY(SC_OP_FAILURE),
|
|
1738
|
+
STATUS_ENTRY(SC_OP_SUCCESS),
|
|
1739
|
+
STATUS_ENTRY(CARDCTRL),
|
|
1740
|
+
STATUS_ENTRY(BACKUP_KEY_CREATED),
|
|
1741
|
+
STATUS_ENTRY(PKA_TRUST_BAD),
|
|
1742
|
+
STATUS_ENTRY(PKA_TRUST_GOOD),
|
|
1743
|
+
STATUS_ENTRY(PLAINTEXT),
|
|
1744
|
+
STATUS_ENTRY(INV_SGNR),
|
|
1745
|
+
STATUS_ENTRY(NO_SGNR),
|
|
1746
|
+
STATUS_ENTRY(SUCCESS),
|
|
1747
|
+
STATUS_ENTRY(DECRYPTION_INFO),
|
|
1748
|
+
STATUS_ENTRY(PLAINTEXT_LENGTH),
|
|
1749
|
+
STATUS_ENTRY(MOUNTPOINT),
|
|
1750
|
+
STATUS_ENTRY(PINENTRY_LAUNCHED),
|
|
1751
|
+
STATUS_ENTRY(ATTRIBUTE),
|
|
1752
|
+
STATUS_ENTRY(BEGIN_SIGNING),
|
|
1753
|
+
STATUS_ENTRY(KEY_NOT_CREATED),
|
|
1754
|
+
STATUS_ENTRY(INQUIRE_MAXLEN),
|
|
1755
|
+
STATUS_ENTRY(FAILURE),
|
|
1756
|
+
STATUS_ENTRY(KEY_CONSIDERED),
|
|
1757
|
+
STATUS_ENTRY(TOFU_USER),
|
|
1758
|
+
STATUS_ENTRY(TOFU_STATS),
|
|
1759
|
+
STATUS_ENTRY(TOFU_STATS_LONG),
|
|
1760
|
+
STATUS_ENTRY(NOTATION_FLAGS),
|
|
1761
|
+
STATUS_ENTRY(DECRYPTION_COMPLIANCE_MODE),
|
|
1762
|
+
STATUS_ENTRY(VERIFICATION_COMPLIANCE_MODE),
|
|
1763
|
+
STATUS_ENTRY(CANCELED_BY_USER),
|
|
1764
|
+
{ NULL, GPGME_STATUS_EOF }
|
|
1765
|
+
};
|
|
1766
|
+
|
|
1767
|
+
#undef STATUS_ENTRY
|
|
1768
|
+
|
|
1769
|
+
static gpgme_status_code_t
|
|
1770
|
+
keyword_to_status_code (const char *keyword)
|
|
1771
|
+
{
|
|
1772
|
+
size_t i;
|
|
1773
|
+
|
|
1774
|
+
if (!keyword)
|
|
1775
|
+
return GPGME_STATUS_EOF;
|
|
1776
|
+
|
|
1777
|
+
for (i = 0; keyword_to_status[i].keyword != NULL; i++)
|
|
1778
|
+
{
|
|
1779
|
+
if (strcmp (keyword, keyword_to_status[i].keyword) == 0)
|
|
1780
|
+
return keyword_to_status[i].status;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
/* Unknown keyword - return EOF as fallback */
|
|
1784
|
+
return GPGME_STATUS_EOF;
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
/*
|
|
1788
|
+
* Shim callback that converts keyword strings to status codes
|
|
1789
|
+
* for backward compatibility with existing Ruby callbacks.
|
|
1790
|
+
*/
|
|
1791
|
+
static gpgme_error_t
|
|
1792
|
+
edit_cb_shim (void *hook, const char *keyword, const char *args, int fd)
|
|
1793
|
+
{
|
|
1794
|
+
VALUE vcb = (VALUE) hook, veditfunc, vhook_value;
|
|
1795
|
+
gpgme_status_code_t status;
|
|
1796
|
+
|
|
1797
|
+
veditfunc = RARRAY_PTR (vcb)[0];
|
|
1798
|
+
vhook_value = RARRAY_PTR (vcb)[1];
|
|
1799
|
+
|
|
1800
|
+
status = keyword_to_status_code (keyword);
|
|
1801
|
+
|
|
1802
|
+
rb_funcall (veditfunc, rb_intern ("call"), 4, vhook_value, INT2FIX (status),
|
|
1803
|
+
args ? rb_str_new2 (args) : rb_str_new2 (""), INT2NUM (fd));
|
|
1804
|
+
return gpgme_err_make (GPG_ERR_SOURCE_USER_1, GPG_ERR_NO_ERROR);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
static VALUE
|
|
1808
|
+
rb_s_gpgme_op_edit (VALUE dummy, VALUE vctx, VALUE vkey,
|
|
1809
|
+
VALUE veditfunc, VALUE vhook_value, VALUE vout)
|
|
1810
|
+
{
|
|
1811
|
+
gpgme_ctx_t ctx;
|
|
1812
|
+
gpgme_key_t key;
|
|
1813
|
+
gpgme_data_t out = NULL;
|
|
1814
|
+
VALUE vcb;
|
|
1815
|
+
gpgme_error_t err;
|
|
1816
|
+
|
|
1817
|
+
CHECK_KEYLIST_NOT_IN_PROGRESS (vctx);
|
|
1818
|
+
|
|
1819
|
+
UNWRAP_GPGME_CTX(vctx, ctx);
|
|
1820
|
+
if (!ctx)
|
|
1821
|
+
rb_raise (rb_eArgError, "released ctx");
|
|
1822
|
+
UNWRAP_GPGME_KEY(vkey, key);
|
|
1823
|
+
if (!NIL_P(vout))
|
|
1824
|
+
UNWRAP_GPGME_DATA(vout, out);
|
|
1825
|
+
|
|
1826
|
+
vcb = rb_ary_new ();
|
|
1827
|
+
rb_ary_push (vcb, veditfunc);
|
|
1828
|
+
rb_ary_push (vcb, vhook_value);
|
|
1829
|
+
/* Keep a reference to avoid GC. */
|
|
1830
|
+
rb_iv_set (vctx, "@edit_cb", vcb);
|
|
1831
|
+
|
|
1832
|
+
/* Use gpgme_op_interact with flags=0, shim converts keywords to status codes */
|
|
1833
|
+
err = gpgme_op_interact (ctx, key, 0, edit_cb_shim, (void *)vcb, out);
|
|
1834
|
+
return LONG2NUM(err);
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
static VALUE
|
|
1838
|
+
rb_s_gpgme_op_edit_start (VALUE dummy, VALUE vctx, VALUE vkey,
|
|
1839
|
+
VALUE veditfunc, VALUE vhook_value, VALUE vout)
|
|
1840
|
+
{
|
|
1841
|
+
gpgme_ctx_t ctx;
|
|
1842
|
+
gpgme_key_t key;
|
|
1843
|
+
gpgme_data_t out = NULL;
|
|
1844
|
+
VALUE vcb;
|
|
1845
|
+
gpgme_error_t err;
|
|
1846
|
+
|
|
1847
|
+
CHECK_KEYLIST_NOT_IN_PROGRESS(vctx);
|
|
1848
|
+
|
|
1849
|
+
UNWRAP_GPGME_CTX(vctx, ctx);
|
|
1850
|
+
if (!ctx)
|
|
1851
|
+
rb_raise (rb_eArgError, "released ctx");
|
|
1852
|
+
UNWRAP_GPGME_KEY(vkey, key);
|
|
1853
|
+
if (!NIL_P(vout))
|
|
1854
|
+
UNWRAP_GPGME_DATA(vout, out);
|
|
1855
|
+
|
|
1856
|
+
vcb = rb_ary_new ();
|
|
1857
|
+
rb_ary_push (vcb, veditfunc);
|
|
1858
|
+
rb_ary_push (vcb, vhook_value);
|
|
1859
|
+
/* Keep a reference to avoid GC. */
|
|
1860
|
+
rb_iv_set (vctx, "@edit_cb", vcb);
|
|
1861
|
+
|
|
1862
|
+
/* Use gpgme_op_interact_start with flags=0, shim converts keywords to status codes */
|
|
1863
|
+
err = gpgme_op_interact_start (ctx, key, 0, edit_cb_shim, (void *)vcb, out);
|
|
1864
|
+
return LONG2NUM(err);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
static VALUE
|
|
1868
|
+
rb_s_gpgme_op_card_edit (VALUE dummy, VALUE vctx, VALUE vkey,
|
|
1869
|
+
VALUE veditfunc, VALUE vhook_value, VALUE vout)
|
|
1870
|
+
{
|
|
1871
|
+
gpgme_ctx_t ctx;
|
|
1872
|
+
gpgme_key_t key;
|
|
1873
|
+
gpgme_data_t out = NULL;
|
|
1874
|
+
VALUE vcb;
|
|
1875
|
+
gpgme_error_t err;
|
|
1876
|
+
|
|
1877
|
+
CHECK_KEYLIST_NOT_IN_PROGRESS(vctx);
|
|
1878
|
+
|
|
1879
|
+
UNWRAP_GPGME_CTX(vctx, ctx);
|
|
1880
|
+
if (!ctx)
|
|
1881
|
+
rb_raise (rb_eArgError, "released ctx");
|
|
1882
|
+
UNWRAP_GPGME_KEY(vkey, key);
|
|
1883
|
+
if (!NIL_P(vout))
|
|
1884
|
+
UNWRAP_GPGME_DATA(vout, out);
|
|
1885
|
+
|
|
1886
|
+
vcb = rb_ary_new ();
|
|
1887
|
+
rb_ary_push (vcb, veditfunc);
|
|
1888
|
+
rb_ary_push (vcb, vhook_value);
|
|
1889
|
+
/* Keep a reference to avoid GC. */
|
|
1890
|
+
rb_iv_set (vctx, "@card_edit_cb", vcb);
|
|
1891
|
+
|
|
1892
|
+
/* Use gpgme_op_interact with GPGME_INTERACT_CARD flag */
|
|
1893
|
+
err = gpgme_op_interact (ctx, key, GPGME_INTERACT_CARD, edit_cb_shim, (void *)vcb, out);
|
|
1894
|
+
return LONG2NUM(err);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
static VALUE
|
|
1898
|
+
rb_s_gpgme_op_card_edit_start (VALUE dummy, VALUE vctx, VALUE vkey,
|
|
1899
|
+
VALUE veditfunc, VALUE vhook_value, VALUE vout)
|
|
1900
|
+
{
|
|
1901
|
+
gpgme_ctx_t ctx;
|
|
1902
|
+
gpgme_key_t key;
|
|
1903
|
+
gpgme_data_t out = NULL;
|
|
1904
|
+
VALUE vcb;
|
|
1905
|
+
gpgme_error_t err;
|
|
1906
|
+
|
|
1907
|
+
CHECK_KEYLIST_NOT_IN_PROGRESS(vctx);
|
|
1908
|
+
|
|
1909
|
+
UNWRAP_GPGME_CTX(vctx, ctx);
|
|
1910
|
+
if (!ctx)
|
|
1911
|
+
rb_raise (rb_eArgError, "released ctx");
|
|
1912
|
+
UNWRAP_GPGME_KEY(vkey, key);
|
|
1913
|
+
if (!NIL_P(vout))
|
|
1914
|
+
UNWRAP_GPGME_DATA(vout, out);
|
|
1915
|
+
|
|
1916
|
+
vcb = rb_ary_new ();
|
|
1917
|
+
rb_ary_push (vcb, veditfunc);
|
|
1918
|
+
rb_ary_push (vcb, vhook_value);
|
|
1919
|
+
/* Keep a reference to avoid GC. */
|
|
1920
|
+
rb_iv_set (vctx, "@card_edit_cb", vcb);
|
|
1921
|
+
|
|
1922
|
+
/* Use gpgme_op_interact_start with GPGME_INTERACT_CARD flag */
|
|
1923
|
+
err = gpgme_op_interact_start (ctx, key, GPGME_INTERACT_CARD, edit_cb_shim, (void *)vcb, out);
|
|
1924
|
+
return LONG2NUM(err);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
#else
|
|
1928
|
+
/* GPGME < 2.0.0: Use the original gpgme_op_edit functions directly */
|
|
1929
|
+
|
|
1543
1930
|
static gpgme_error_t
|
|
1544
1931
|
edit_cb (void *hook, gpgme_status_code_t status, const char *args, int fd)
|
|
1545
1932
|
{
|
|
@@ -1669,6 +2056,7 @@ rb_s_gpgme_op_card_edit_start (VALUE dummy, VALUE vctx, VALUE vkey,
|
|
|
1669
2056
|
err = gpgme_op_card_edit_start (ctx, key, edit_cb, (void *)vcb, out);
|
|
1670
2057
|
return LONG2NUM(err);
|
|
1671
2058
|
}
|
|
2059
|
+
#endif /* GPGME_VERSION_NUMBER >= 0x020000 */
|
|
1672
2060
|
|
|
1673
2061
|
#if defined(GPGME_VERSION_NUMBER) && GPGME_VERSION_NUMBER < 0x020000
|
|
1674
2062
|
static VALUE
|
|
@@ -1752,6 +2140,11 @@ rb_s_gpgme_op_decrypt (VALUE dummy, VALUE vctx, VALUE vcipher, VALUE vplain)
|
|
|
1752
2140
|
UNWRAP_GPGME_DATA(vplain, plain);
|
|
1753
2141
|
|
|
1754
2142
|
err = gpgme_op_decrypt (ctx, cipher, plain);
|
|
2143
|
+
|
|
2144
|
+
RB_GC_GUARD(vctx);
|
|
2145
|
+
RB_GC_GUARD(vcipher);
|
|
2146
|
+
RB_GC_GUARD(vplain);
|
|
2147
|
+
|
|
1755
2148
|
return LONG2NUM(err);
|
|
1756
2149
|
}
|
|
1757
2150
|
|
|
@@ -1831,6 +2224,12 @@ rb_s_gpgme_op_verify (VALUE dummy, VALUE vctx, VALUE vsig, VALUE vsigned_text,
|
|
|
1831
2224
|
UNWRAP_GPGME_DATA(vplain, plain);
|
|
1832
2225
|
|
|
1833
2226
|
err = gpgme_op_verify (ctx, sig, signed_text, plain);
|
|
2227
|
+
|
|
2228
|
+
RB_GC_GUARD(vctx);
|
|
2229
|
+
RB_GC_GUARD(vsig);
|
|
2230
|
+
RB_GC_GUARD(vsigned_text);
|
|
2231
|
+
RB_GC_GUARD(vplain);
|
|
2232
|
+
|
|
1834
2233
|
return LONG2NUM(err);
|
|
1835
2234
|
}
|
|
1836
2235
|
|
|
@@ -1935,6 +2334,11 @@ rb_s_gpgme_op_decrypt_verify (VALUE dummy, VALUE vctx, VALUE vcipher,
|
|
|
1935
2334
|
UNWRAP_GPGME_DATA(vplain, plain);
|
|
1936
2335
|
|
|
1937
2336
|
err = gpgme_op_decrypt_verify (ctx, cipher, plain);
|
|
2337
|
+
|
|
2338
|
+
RB_GC_GUARD(vctx);
|
|
2339
|
+
RB_GC_GUARD(vcipher);
|
|
2340
|
+
RB_GC_GUARD(vplain);
|
|
2341
|
+
|
|
1938
2342
|
return LONG2NUM(err);
|
|
1939
2343
|
}
|
|
1940
2344
|
|
|
@@ -2019,6 +2423,11 @@ rb_s_gpgme_op_sign (VALUE dummy, VALUE vctx, VALUE vplain, VALUE vsig,
|
|
|
2019
2423
|
UNWRAP_GPGME_DATA(vsig, sig);
|
|
2020
2424
|
|
|
2021
2425
|
err = gpgme_op_sign (ctx, plain, sig, NUM2INT(vmode));
|
|
2426
|
+
|
|
2427
|
+
RB_GC_GUARD(vctx);
|
|
2428
|
+
RB_GC_GUARD(vplain);
|
|
2429
|
+
RB_GC_GUARD(vsig);
|
|
2430
|
+
|
|
2022
2431
|
return LONG2NUM(err);
|
|
2023
2432
|
}
|
|
2024
2433
|
|
|
@@ -2128,6 +2537,12 @@ rb_s_gpgme_op_encrypt (VALUE dummy, VALUE vctx, VALUE vrecp, VALUE vflags,
|
|
|
2128
2537
|
err = gpgme_op_encrypt (ctx, recp, NUM2INT(vflags), plain, cipher);
|
|
2129
2538
|
if (recp)
|
|
2130
2539
|
xfree (recp);
|
|
2540
|
+
|
|
2541
|
+
RB_GC_GUARD(vctx);
|
|
2542
|
+
RB_GC_GUARD(vrecp);
|
|
2543
|
+
RB_GC_GUARD(vplain);
|
|
2544
|
+
RB_GC_GUARD(vcipher);
|
|
2545
|
+
|
|
2131
2546
|
return LONG2NUM(err);
|
|
2132
2547
|
}
|
|
2133
2548
|
|
|
@@ -2227,6 +2642,12 @@ rb_s_gpgme_op_encrypt_sign (VALUE dummy, VALUE vctx, VALUE vrecp, VALUE vflags,
|
|
|
2227
2642
|
err = gpgme_op_encrypt_sign (ctx, recp, NUM2INT(vflags), plain, cipher);
|
|
2228
2643
|
if (recp)
|
|
2229
2644
|
xfree (recp);
|
|
2645
|
+
|
|
2646
|
+
RB_GC_GUARD(vctx);
|
|
2647
|
+
RB_GC_GUARD(vrecp);
|
|
2648
|
+
RB_GC_GUARD(vplain);
|
|
2649
|
+
RB_GC_GUARD(vcipher);
|
|
2650
|
+
|
|
2230
2651
|
return LONG2NUM(err);
|
|
2231
2652
|
}
|
|
2232
2653
|
|
|
@@ -2395,7 +2816,7 @@ rb_s_gpgme_op_random_bytes (VALUE dummy, VALUE vctx, VALUE vsize, VALUE vmode)
|
|
|
2395
2816
|
UNWRAP_GPGME_CTX(vctx, ctx);
|
|
2396
2817
|
if (!ctx)
|
|
2397
2818
|
rb_raise (rb_eArgError, "released ctx");
|
|
2398
|
-
|
|
2819
|
+
|
|
2399
2820
|
size = NUM2SIZET(vsize);
|
|
2400
2821
|
buffer = ALLOC_N(char, size);
|
|
2401
2822
|
|
|
@@ -2648,6 +3069,11 @@ Init_gpgme_n (void)
|
|
|
2648
3069
|
rb_s_gpgme_op_delete_ext, 3);
|
|
2649
3070
|
rb_define_module_function (mGPGME, "gpgme_op_delete_start",
|
|
2650
3071
|
rb_s_gpgme_op_delete_start, 3);
|
|
3072
|
+
|
|
3073
|
+
/* Key Edit Interface
|
|
3074
|
+
* For GPGME 2.0.0+, these functions use gpgme_op_interact internally
|
|
3075
|
+
* with a shim to maintain backward compatibility with existing callbacks.
|
|
3076
|
+
*/
|
|
2651
3077
|
rb_define_module_function (mGPGME, "gpgme_op_edit",
|
|
2652
3078
|
rb_s_gpgme_op_edit, 5);
|
|
2653
3079
|
rb_define_module_function (mGPGME, "gpgme_op_edit_start",
|
|
@@ -3218,11 +3644,58 @@ Init_gpgme_n (void)
|
|
|
3218
3644
|
/* The available flags for gpgme_op_encrypt. */
|
|
3219
3645
|
rb_define_const (mGPGME, "GPGME_ENCRYPT_ALWAYS_TRUST",
|
|
3220
3646
|
INT2FIX(GPGME_ENCRYPT_ALWAYS_TRUST));
|
|
3221
|
-
|
|
3222
|
-
#ifdef GPGME_ENCRYPT_NO_ENCRYPT_TO
|
|
3647
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_NO_ENCRYPT_TO
|
|
3223
3648
|
rb_define_const (mGPGME, "GPGME_ENCRYPT_NO_ENCRYPT_TO",
|
|
3224
3649
|
INT2FIX(GPGME_ENCRYPT_NO_ENCRYPT_TO));
|
|
3225
3650
|
#endif
|
|
3651
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_PREPARE
|
|
3652
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_PREPARE",
|
|
3653
|
+
INT2FIX(GPGME_ENCRYPT_PREPARE));
|
|
3654
|
+
#endif
|
|
3655
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_EXPECT_SIGN
|
|
3656
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_EXPECT_SIGN",
|
|
3657
|
+
INT2FIX(GPGME_ENCRYPT_EXPECT_SIGN));
|
|
3658
|
+
#endif
|
|
3659
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_NO_COMPRESS
|
|
3660
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_NO_COMPRESS",
|
|
3661
|
+
INT2FIX(GPGME_ENCRYPT_NO_COMPRESS));
|
|
3662
|
+
#endif
|
|
3663
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK
|
|
3664
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK",
|
|
3665
|
+
INT2FIX(GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK));
|
|
3666
|
+
#endif
|
|
3667
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_SYMMETRIC
|
|
3668
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_SYMMETRIC",
|
|
3669
|
+
INT2FIX(GPGME_ENCRYPT_SYMMETRIC));
|
|
3670
|
+
#endif
|
|
3671
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_THROW_KEYIDS
|
|
3672
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_THROW_KEYIDS",
|
|
3673
|
+
INT2FIX(GPGME_ENCRYPT_THROW_KEYIDS));
|
|
3674
|
+
#endif
|
|
3675
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_WRAP
|
|
3676
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_WRAP",
|
|
3677
|
+
INT2FIX(GPGME_ENCRYPT_WRAP));
|
|
3678
|
+
#endif
|
|
3679
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_WANT_ADDRESS
|
|
3680
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_WANT_ADDRESS",
|
|
3681
|
+
INT2FIX(GPGME_ENCRYPT_WANT_ADDRESS));
|
|
3682
|
+
#endif
|
|
3683
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_ARCHIVE
|
|
3684
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_ARCHIVE",
|
|
3685
|
+
INT2FIX(GPGME_ENCRYPT_ARCHIVE));
|
|
3686
|
+
#endif
|
|
3687
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_FILE
|
|
3688
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_FILE",
|
|
3689
|
+
INT2FIX(GPGME_ENCRYPT_FILE));
|
|
3690
|
+
#endif
|
|
3691
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_ADD_RECP
|
|
3692
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_ADD_RECP",
|
|
3693
|
+
INT2FIX(GPGME_ENCRYPT_ADD_RECP));
|
|
3694
|
+
#endif
|
|
3695
|
+
#ifdef HAVE_CONST_GPGME_ENCRYPT_CHG_RECP
|
|
3696
|
+
rb_define_const (mGPGME, "GPGME_ENCRYPT_CHG_RECP",
|
|
3697
|
+
INT2FIX(GPGME_ENCRYPT_CHG_RECP));
|
|
3698
|
+
#endif
|
|
3226
3699
|
|
|
3227
3700
|
/* Random number generation mode flags added in 2.0.0 */
|
|
3228
3701
|
#if defined(GPGME_VERSION_NUMBER) && GPGME_VERSION_NUMBER >= 0x020000
|
data/lib/gpgme/constants.rb
CHANGED
|
@@ -101,6 +101,24 @@ module GPGME
|
|
|
101
101
|
if defined?(GPGME_ENCRYPT_NO_ENCRYPT_TO)
|
|
102
102
|
ENCRYPT_NO_ENCRYPT_TO = GPGME_ENCRYPT_NO_ENCRYPT_TO
|
|
103
103
|
end
|
|
104
|
+
if defined?(GPGME_ENCRYPT_PREPARE)
|
|
105
|
+
ENCRYPT_PREPARE = GPGME_ENCRYPT_PREPARE
|
|
106
|
+
end
|
|
107
|
+
if defined?(GPGME_ENCRYPT_EXPECT_SIGN)
|
|
108
|
+
ENCRYPT_EXPECT_SIGN = GPGME_ENCRYPT_EXPECT_SIGN
|
|
109
|
+
end
|
|
110
|
+
if defined?(GPGME_ENCRYPT_NO_COMPRESS)
|
|
111
|
+
ENCRYPT_NO_COMPRESS = GPGME_ENCRYPT_NO_COMPRESS
|
|
112
|
+
end
|
|
113
|
+
if defined?(GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK)
|
|
114
|
+
ENCRYPT_UNSIGNED_INTEGRITY_CHECK = GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK
|
|
115
|
+
end
|
|
116
|
+
if defined?(GPGME_ENCRYPT_SYMMETRIC)
|
|
117
|
+
ENCRYPT_SYMMETRIC = GPGME_ENCRYPT_SYMMETRIC
|
|
118
|
+
end
|
|
119
|
+
if defined?(GPGME_ENCRYPT_THROW_KEYIDS)
|
|
120
|
+
ENCRYPT_THROW_KEYIDS = GPGME_ENCRYPT_THROW_KEYIDS
|
|
121
|
+
end
|
|
104
122
|
IMPORT_NEW = GPGME_IMPORT_NEW
|
|
105
123
|
IMPORT_SECRET = GPGME_IMPORT_SECRET
|
|
106
124
|
IMPORT_SIG = GPGME_IMPORT_SIG
|
data/lib/gpgme/crypto.rb
CHANGED
|
@@ -91,7 +91,9 @@ module GPGME
|
|
|
91
91
|
begin
|
|
92
92
|
if options[:sign]
|
|
93
93
|
if options[:signers]
|
|
94
|
-
|
|
94
|
+
# Optimization: reuse recipient Key objects if signers match
|
|
95
|
+
# to avoid redundant key lookups
|
|
96
|
+
signers = resolve_keys_for_signing(options[:signers], keys)
|
|
95
97
|
ctx.add_signer(*signers)
|
|
96
98
|
end
|
|
97
99
|
ctx.encrypt_sign(keys, plain_data, cipher_data, flags)
|
|
@@ -353,5 +355,58 @@ module GPGME
|
|
|
353
355
|
end
|
|
354
356
|
end
|
|
355
357
|
|
|
358
|
+
private
|
|
359
|
+
|
|
360
|
+
# Resolves keys for signing, reusing already-fetched recipient keys when possible
|
|
361
|
+
# to avoid redundant key lookups which can be slow.
|
|
362
|
+
#
|
|
363
|
+
# @param signers_input [Array, String, Key] The signer identifiers
|
|
364
|
+
# @param recipient_keys [Array<Key>, nil] Already-fetched recipient keys to check for reuse
|
|
365
|
+
# @return [Array<Key>] Keys suitable for signing
|
|
366
|
+
def resolve_keys_for_signing(signers_input, recipient_keys)
|
|
367
|
+
signers_input = [signers_input].flatten
|
|
368
|
+
recipient_keys ||= []
|
|
369
|
+
|
|
370
|
+
# Build a lookup hash of recipient keys by various identifiers
|
|
371
|
+
recipient_lookup = {}
|
|
372
|
+
recipient_keys.each do |key|
|
|
373
|
+
next unless key.is_a?(Key)
|
|
374
|
+
# Index by fingerprint, short key ID, and email
|
|
375
|
+
recipient_lookup[key.fingerprint] = key if key.fingerprint
|
|
376
|
+
recipient_lookup[key.fingerprint[-16..]] = key if key.fingerprint && key.fingerprint.length >= 16
|
|
377
|
+
recipient_lookup[key.fingerprint[-8..]] = key if key.fingerprint && key.fingerprint.length >= 8
|
|
378
|
+
if key.uids && !key.uids.empty?
|
|
379
|
+
key.uids.each do |uid|
|
|
380
|
+
recipient_lookup[uid.email] = key if uid.email && !uid.email.empty?
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
result = []
|
|
386
|
+
needs_lookup = []
|
|
387
|
+
|
|
388
|
+
signers_input.each do |signer|
|
|
389
|
+
case signer
|
|
390
|
+
when Key
|
|
391
|
+
# Already a key object, use directly if it can sign
|
|
392
|
+
result << signer if signer.usable_for?([:sign])
|
|
393
|
+
when String
|
|
394
|
+
# Check if we already have this key from recipients
|
|
395
|
+
if (existing = recipient_lookup[signer]) && existing.usable_for?([:sign])
|
|
396
|
+
result << existing
|
|
397
|
+
else
|
|
398
|
+
needs_lookup << signer
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Only do a Key.find for signers we couldn't resolve from recipients
|
|
404
|
+
unless needs_lookup.empty?
|
|
405
|
+
result += Key.find(:public, needs_lookup, :sign)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
result
|
|
409
|
+
end
|
|
410
|
+
|
|
356
411
|
end # module Crypto
|
|
357
412
|
end # module GPGME
|
data/lib/gpgme/ctx.rb
CHANGED
|
@@ -76,10 +76,12 @@ module GPGME
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
if block_given?
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
GPGME.synchronize do
|
|
80
|
+
begin
|
|
81
|
+
yield ctx
|
|
82
|
+
ensure
|
|
83
|
+
GPGME::gpgme_release(ctx)
|
|
84
|
+
end
|
|
83
85
|
end
|
|
84
86
|
else
|
|
85
87
|
ctx
|
|
@@ -464,6 +466,9 @@ module GPGME
|
|
|
464
466
|
alias delete delete_key
|
|
465
467
|
|
|
466
468
|
# Edit attributes of the key in the local key ring.
|
|
469
|
+
#
|
|
470
|
+
# The callback receives (hook_value, status, args, fd) where status is
|
|
471
|
+
# a numeric status code (e.g., GPGME::GPGME_STATUS_GET_BOOL).
|
|
467
472
|
def edit_key(key, editfunc, hook_value = nil, out = Data.new)
|
|
468
473
|
err = GPGME::gpgme_op_edit(self, key, editfunc, hook_value, out)
|
|
469
474
|
exc = GPGME::error_to_exception(err)
|
|
@@ -472,6 +477,9 @@ module GPGME
|
|
|
472
477
|
alias edit edit_key
|
|
473
478
|
|
|
474
479
|
# Edit attributes of the key on the card.
|
|
480
|
+
#
|
|
481
|
+
# The callback receives (hook_value, status, args, fd) where status is
|
|
482
|
+
# a numeric status code (e.g., GPGME::GPGME_STATUS_GET_BOOL).
|
|
475
483
|
def edit_card_key(key, editfunc, hook_value = nil, out = Data.new)
|
|
476
484
|
err = GPGME::gpgme_op_card_edit(self, key, editfunc, hook_value, out)
|
|
477
485
|
exc = GPGME::error_to_exception(err)
|
|
@@ -612,10 +620,21 @@ keylist_mode=#{KEYLIST_MODE_NAMES[keylist_mode]}>"
|
|
|
612
620
|
private
|
|
613
621
|
|
|
614
622
|
def self.pass_function(pass, uid_hint, passphrase_info, prev_was_bad, fd)
|
|
623
|
+
# Write the passphrase directly using IO.for_fd. We set autoclose=false
|
|
624
|
+
# to prevent Ruby from closing the fd (which belongs to GPGME/gpg-agent).
|
|
625
|
+
# The IO object is used only within this method scope and not stored,
|
|
626
|
+
# so we also ensure it isn't prematurely collected by keeping a strong
|
|
627
|
+
# reference until we're done.
|
|
615
628
|
io = IO.for_fd(fd, 'w')
|
|
616
629
|
io.autoclose = false
|
|
617
|
-
|
|
618
|
-
|
|
630
|
+
begin
|
|
631
|
+
io.write "#{pass}\n"
|
|
632
|
+
io.flush
|
|
633
|
+
rescue => e
|
|
634
|
+
# If the fd has become invalid (e.g. agent communication error),
|
|
635
|
+
# re-raise as a more descriptive error.
|
|
636
|
+
raise e
|
|
637
|
+
end
|
|
619
638
|
end
|
|
620
639
|
|
|
621
640
|
end
|
data/lib/gpgme/io_callbacks.rb
CHANGED
|
@@ -9,7 +9,12 @@ module GPGME
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def write(hook, buffer, length)
|
|
12
|
-
|
|
12
|
+
data = buffer[0 .. length]
|
|
13
|
+
# Handle encoding conversion if the IO has a different encoding
|
|
14
|
+
if @io.respond_to?(:external_encoding) && @io.external_encoding
|
|
15
|
+
data = data.encode(@io.external_encoding, invalid: :replace, undef: :replace)
|
|
16
|
+
end
|
|
17
|
+
@io.write(data)
|
|
13
18
|
end
|
|
14
19
|
|
|
15
20
|
def seek(hook, offset, whence)
|
data/lib/gpgme/version.rb
CHANGED
data/lib/gpgme.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require 'gpgme_n'
|
|
2
|
+
require 'monitor'
|
|
2
3
|
|
|
3
|
-
#
|
|
4
|
+
# This call initializes the GPGME library and must happen before
|
|
5
|
+
# any GPGME operations (e.g. Ctx.new) can succeed.
|
|
4
6
|
GPGME::gpgme_check_version(nil)
|
|
5
7
|
|
|
6
8
|
require 'gpgme/constants'
|
|
@@ -19,8 +21,61 @@ require 'gpgme/engine'
|
|
|
19
21
|
require 'gpgme/crypto'
|
|
20
22
|
|
|
21
23
|
module GPGME
|
|
24
|
+
|
|
25
|
+
# Mutex for serializing GPGME operations when thread safety is enabled.
|
|
26
|
+
# While the underlying GPGME C library supports separate contexts in
|
|
27
|
+
# separate threads, the communication with gpg-agent over Unix domain
|
|
28
|
+
# sockets can produce "Bad file descriptor" errors under heavy concurrent
|
|
29
|
+
# load. Thread-safe mode is enabled by default and can be disabled
|
|
30
|
+
# if not needed (e.g. single-threaded applications).
|
|
31
|
+
#
|
|
32
|
+
# A Monitor is used instead of a Mutex because GPGME operations are
|
|
33
|
+
# reentrant — e.g. Crypto#sign calls Ctx.new, and within that block,
|
|
34
|
+
# Key.find calls Ctx.new again.
|
|
35
|
+
#
|
|
36
|
+
# @example Disable thread-safe mode for single-threaded apps
|
|
37
|
+
# GPGME.thread_safe = false
|
|
38
|
+
#
|
|
39
|
+
@thread_safe_mutex = Monitor.new
|
|
40
|
+
@thread_safe = true
|
|
41
|
+
|
|
22
42
|
class << self
|
|
23
43
|
|
|
44
|
+
# Enable or disable thread-safe mode. Enabled by default. When
|
|
45
|
+
# enabled, all high-level GPGME operations (encrypt, decrypt, sign,
|
|
46
|
+
# verify, key listing, etc.) are serialized through a global monitor
|
|
47
|
+
# to prevent concurrent access to gpg-agent from causing "Bad file
|
|
48
|
+
# descriptor" errors. Disable for single-threaded apps if the
|
|
49
|
+
# synchronization overhead is undesirable.
|
|
50
|
+
#
|
|
51
|
+
# @param [Boolean] value false to disable thread-safe mode
|
|
52
|
+
attr_writer :thread_safe
|
|
53
|
+
|
|
54
|
+
# Returns true if thread-safe mode is enabled.
|
|
55
|
+
def thread_safe?
|
|
56
|
+
@thread_safe
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The mutex used for thread-safe serialization. Can be used directly
|
|
60
|
+
# if you need finer-grained control over locking.
|
|
61
|
+
#
|
|
62
|
+
# @example manual locking
|
|
63
|
+
# GPGME.synchronize do
|
|
64
|
+
# # multiple GPGME operations atomically
|
|
65
|
+
# end
|
|
66
|
+
attr_reader :thread_safe_mutex
|
|
67
|
+
|
|
68
|
+
# Execute a block with the GPGME mutex held if thread-safe mode is
|
|
69
|
+
# enabled. If thread-safe mode is disabled, the block is executed
|
|
70
|
+
# directly without locking.
|
|
71
|
+
def synchronize(&block)
|
|
72
|
+
if @thread_safe
|
|
73
|
+
@thread_safe_mutex.synchronize(&block)
|
|
74
|
+
else
|
|
75
|
+
yield
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
24
79
|
# From the c extension
|
|
25
80
|
alias pubkey_algo_name gpgme_pubkey_algo_name
|
|
26
81
|
alias hash_algo_name gpgme_hash_algo_name
|
data/test/ctx_test.rb
CHANGED
|
@@ -70,9 +70,6 @@ describe GPGME::Ctx do
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
it "should not segfault" do
|
|
73
|
-
cipher = GPGME::Data.new(KEY_1_ENCRYPTED)
|
|
74
|
-
ouput = GPGME::Data.new
|
|
75
|
-
|
|
76
73
|
GPGME::Ctx.new do |ctx|
|
|
77
74
|
assert_raises ArgumentError do
|
|
78
75
|
ctx.decrypt_result
|
|
@@ -214,7 +211,7 @@ describe GPGME::Ctx do
|
|
|
214
211
|
|
|
215
212
|
it "doesn't allow just any value" do
|
|
216
213
|
assert_raises GPGME::Error::InvalidValue do
|
|
217
|
-
|
|
214
|
+
GPGME::Ctx.new(:protocol => -200)
|
|
218
215
|
end
|
|
219
216
|
end
|
|
220
217
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
|
|
4
|
+
describe 'GPGME Encryption Flags' do
|
|
5
|
+
it 'should expose ENCRYPT_ALWAYS_TRUST' do
|
|
6
|
+
assert_equal 1, GPGME::ENCRYPT_ALWAYS_TRUST
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it 'should expose ENCRYPT_NO_ENCRYPT_TO if available' do
|
|
10
|
+
if defined?(GPGME::ENCRYPT_NO_ENCRYPT_TO)
|
|
11
|
+
assert GPGME::ENCRYPT_NO_ENCRYPT_TO.is_a?(Integer)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should expose ENCRYPT_PREPARE if available' do
|
|
16
|
+
if defined?(GPGME::ENCRYPT_PREPARE)
|
|
17
|
+
assert GPGME::ENCRYPT_PREPARE.is_a?(Integer)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should expose ENCRYPT_EXPECT_SIGN if available' do
|
|
22
|
+
if defined?(GPGME::ENCRYPT_EXPECT_SIGN)
|
|
23
|
+
assert GPGME::ENCRYPT_EXPECT_SIGN.is_a?(Integer)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should expose ENCRYPT_NO_COMPRESS if available' do
|
|
28
|
+
if defined?(GPGME::ENCRYPT_NO_COMPRESS)
|
|
29
|
+
assert GPGME::ENCRYPT_NO_COMPRESS.is_a?(Integer)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'should expose ENCRYPT_UNSIGNED_INTEGRITY_CHECK if available' do
|
|
34
|
+
if defined?(GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK)
|
|
35
|
+
assert GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK.is_a?(Integer)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should expose ENCRYPT_SYMMETRIC if available' do
|
|
40
|
+
if defined?(GPGME::ENCRYPT_SYMMETRIC)
|
|
41
|
+
assert GPGME::ENCRYPT_SYMMETRIC.is_a?(Integer)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should expose ENCRYPT_THROW_KEYIDS if available' do
|
|
46
|
+
if defined?(GPGME::ENCRYPT_THROW_KEYIDS)
|
|
47
|
+
assert GPGME::ENCRYPT_THROW_KEYIDS.is_a?(Integer)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should use different flag values for different flags' do
|
|
52
|
+
flags = []
|
|
53
|
+
flags << GPGME::ENCRYPT_ALWAYS_TRUST
|
|
54
|
+
flags << GPGME::ENCRYPT_NO_ENCRYPT_TO if defined?(GPGME::ENCRYPT_NO_ENCRYPT_TO)
|
|
55
|
+
flags << GPGME::ENCRYPT_PREPARE if defined?(GPGME::ENCRYPT_PREPARE)
|
|
56
|
+
flags << GPGME::ENCRYPT_EXPECT_SIGN if defined?(GPGME::ENCRYPT_EXPECT_SIGN)
|
|
57
|
+
flags << GPGME::ENCRYPT_NO_COMPRESS if defined?(GPGME::ENCRYPT_NO_COMPRESS)
|
|
58
|
+
flags << GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK if defined?(GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK)
|
|
59
|
+
flags << GPGME::ENCRYPT_SYMMETRIC if defined?(GPGME::ENCRYPT_SYMMETRIC)
|
|
60
|
+
flags << GPGME::ENCRYPT_THROW_KEYIDS if defined?(GPGME::ENCRYPT_THROW_KEYIDS)
|
|
61
|
+
|
|
62
|
+
# All flags should be unique
|
|
63
|
+
assert_equal flags.length, flags.uniq.length
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
5
|
+
describe GPGME::IOCallbacks do
|
|
6
|
+
describe "encoding handling" do
|
|
7
|
+
it "writes binary data to binary IO without error" do
|
|
8
|
+
io = StringIO.new
|
|
9
|
+
io.set_encoding(Encoding::ASCII_8BIT)
|
|
10
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
11
|
+
|
|
12
|
+
# Binary data with bytes that aren't valid UTF-8
|
|
13
|
+
binary_data = "\xC3\x28".b # Invalid UTF-8 sequence
|
|
14
|
+
|
|
15
|
+
callbacks.write(nil, binary_data, binary_data.bytesize)
|
|
16
|
+
io.rewind
|
|
17
|
+
assert_equal binary_data, io.read
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "writes UTF-8 data to UTF-8 IO without error" do
|
|
21
|
+
io = StringIO.new
|
|
22
|
+
io.set_encoding(Encoding::UTF_8)
|
|
23
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
24
|
+
|
|
25
|
+
utf8_data = "Héllo Wörld! 日本語"
|
|
26
|
+
|
|
27
|
+
callbacks.write(nil, utf8_data.encode(Encoding::UTF_8), utf8_data.bytesize)
|
|
28
|
+
io.rewind
|
|
29
|
+
assert_equal utf8_data, io.read
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "handles encoding conversion when IO has different encoding" do
|
|
33
|
+
io = StringIO.new
|
|
34
|
+
io.set_encoding(Encoding::UTF_8)
|
|
35
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
36
|
+
|
|
37
|
+
# ASCII-8BIT string with valid UTF-8 bytes
|
|
38
|
+
data = "Hello World".b
|
|
39
|
+
|
|
40
|
+
# Should not raise Encoding::UndefinedConversionError
|
|
41
|
+
callbacks.write(nil, data, data.bytesize)
|
|
42
|
+
io.rewind
|
|
43
|
+
assert_equal "Hello World", io.read
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "replaces invalid characters when converting encodings" do
|
|
47
|
+
io = StringIO.new
|
|
48
|
+
io.set_encoding(Encoding::UTF_8)
|
|
49
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
50
|
+
|
|
51
|
+
# Invalid UTF-8 sequence in ASCII-8BIT string
|
|
52
|
+
invalid_data = "Hello\xC3\x28World".b
|
|
53
|
+
|
|
54
|
+
# Should not raise, should replace invalid chars
|
|
55
|
+
callbacks.write(nil, invalid_data, invalid_data.bytesize)
|
|
56
|
+
io.rewind
|
|
57
|
+
result = io.read
|
|
58
|
+
# The invalid sequence should be replaced
|
|
59
|
+
refute_nil result
|
|
60
|
+
assert result.valid_encoding?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "reads data from IO" do
|
|
64
|
+
io = StringIO.new("test data")
|
|
65
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
66
|
+
|
|
67
|
+
result = callbacks.read(nil, 9)
|
|
68
|
+
assert_equal "test data", result
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "seeks in IO" do
|
|
72
|
+
io = StringIO.new("test data")
|
|
73
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
74
|
+
|
|
75
|
+
callbacks.read(nil, 4) # read "test"
|
|
76
|
+
pos = callbacks.seek(nil, 0, IO::SEEK_SET)
|
|
77
|
+
assert_equal 0, pos
|
|
78
|
+
|
|
79
|
+
result = callbacks.read(nil, 4)
|
|
80
|
+
assert_equal "test", result
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "returns current position for seek with offset 0 and SEEK_CUR" do
|
|
84
|
+
io = StringIO.new("test data")
|
|
85
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
86
|
+
|
|
87
|
+
callbacks.read(nil, 5) # read "test "
|
|
88
|
+
pos = callbacks.seek(nil, 0, IO::SEEK_CUR)
|
|
89
|
+
assert_equal 5, pos
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "integration with GPGME signing" do
|
|
94
|
+
before do
|
|
95
|
+
skip unless ensure_keys GPGME::PROTOCOL_OpenPGP
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "clearsigns UTF-8 data without encoding errors" do
|
|
99
|
+
utf8_text = "Héllo Wörld! Ünïcödé tëxt 日本語"
|
|
100
|
+
|
|
101
|
+
crypto = GPGME::Crypto.new
|
|
102
|
+
output = StringIO.new
|
|
103
|
+
output.set_encoding(Encoding::UTF_8)
|
|
104
|
+
|
|
105
|
+
# This should not raise Encoding::UndefinedConversionError
|
|
106
|
+
crypto.sign(utf8_text, mode: GPGME::SIG_MODE_CLEAR, output: output)
|
|
107
|
+
|
|
108
|
+
output.rewind
|
|
109
|
+
result = output.read
|
|
110
|
+
refute_empty result
|
|
111
|
+
assert result.include?("BEGIN PGP SIGNED MESSAGE")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "signs UTF-8 data and outputs to default buffer without errors" do
|
|
115
|
+
utf8_text = "Ünïcödé tëxt: äöü ÄÖÜ ß"
|
|
116
|
+
|
|
117
|
+
crypto = GPGME::Crypto.new
|
|
118
|
+
signed = crypto.sign(utf8_text)
|
|
119
|
+
|
|
120
|
+
result = signed.read
|
|
121
|
+
refute_empty result
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "encrypts and decrypts UTF-8 data correctly" do
|
|
125
|
+
utf8_text = "Sëcrét mëssägé with spëcïäl chäräctërs: 日本語"
|
|
126
|
+
|
|
127
|
+
crypto = GPGME::Crypto.new(always_trust: true)
|
|
128
|
+
encrypted = crypto.encrypt(utf8_text, recipients: KEYS.first[:sha])
|
|
129
|
+
decrypted = crypto.decrypt(encrypted)
|
|
130
|
+
|
|
131
|
+
result = decrypted.read
|
|
132
|
+
# Force UTF-8 encoding since GPGME returns binary data
|
|
133
|
+
result.force_encoding(Encoding::UTF_8)
|
|
134
|
+
assert_equal utf8_text, result
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
describe "default internal encoding support" do
|
|
139
|
+
it "respects Encoding.default_internal when set" do
|
|
140
|
+
# Save original setting
|
|
141
|
+
original_internal = Encoding.default_internal
|
|
142
|
+
original_verbose = $VERBOSE
|
|
143
|
+
|
|
144
|
+
begin
|
|
145
|
+
# Suppress warning about setting Encoding.default_internal
|
|
146
|
+
$VERBOSE = nil
|
|
147
|
+
Encoding.default_internal = Encoding::UTF_8
|
|
148
|
+
$VERBOSE = original_verbose
|
|
149
|
+
|
|
150
|
+
io = StringIO.new
|
|
151
|
+
io.set_encoding(Encoding::UTF_8)
|
|
152
|
+
callbacks = GPGME::IOCallbacks.new(io)
|
|
153
|
+
|
|
154
|
+
# Valid UTF-8 data
|
|
155
|
+
utf8_data = "Tëst dätä"
|
|
156
|
+
callbacks.write(nil, utf8_data, utf8_data.bytesize)
|
|
157
|
+
|
|
158
|
+
io.rewind
|
|
159
|
+
result = io.read
|
|
160
|
+
assert_equal utf8_data, result
|
|
161
|
+
ensure
|
|
162
|
+
# Restore original settings
|
|
163
|
+
$VERBOSE = nil
|
|
164
|
+
Encoding.default_internal = original_internal
|
|
165
|
+
$VERBOSE = original_verbose
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
metadata
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gpgme
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daiki Ueno
|
|
8
8
|
- Albert Llop
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
11
|
date: 2025-07-26 00:00:00.000000000 Z
|
|
@@ -137,10 +136,12 @@ files:
|
|
|
137
136
|
- test/crypto_test.rb
|
|
138
137
|
- test/ctx_test.rb
|
|
139
138
|
- test/data_test.rb
|
|
139
|
+
- test/encryption_flags_test.rb
|
|
140
140
|
- test/files/testkey_pub.gpg
|
|
141
141
|
- test/files/testkey_pub_invalid.gpg
|
|
142
142
|
- test/files/testkey_sec.gpg
|
|
143
143
|
- test/gpgme_test.rb
|
|
144
|
+
- test/io_callbacks_test.rb
|
|
144
145
|
- test/key_test.rb
|
|
145
146
|
- test/pinentry
|
|
146
147
|
- test/signature_test.rb
|
|
@@ -151,7 +152,6 @@ homepage: http://github.com/ueno/ruby-gpgme
|
|
|
151
152
|
licenses:
|
|
152
153
|
- LGPL-2.1+
|
|
153
154
|
metadata: {}
|
|
154
|
-
post_install_message:
|
|
155
155
|
rdoc_options: []
|
|
156
156
|
require_paths:
|
|
157
157
|
- lib
|
|
@@ -167,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
167
167
|
- !ruby/object:Gem::Version
|
|
168
168
|
version: '0'
|
|
169
169
|
requirements: []
|
|
170
|
-
rubygems_version: 3.
|
|
171
|
-
signing_key:
|
|
170
|
+
rubygems_version: 3.7.2
|
|
172
171
|
specification_version: 4
|
|
173
172
|
summary: Ruby binding of GPGME.
|
|
174
173
|
test_files: []
|