diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i index 73533d0c..c7d89dba 100644 --- a/lang/python/gpgme.i +++ b/lang/python/gpgme.i @@ -1,632 +1,641 @@ /* # Copyright (C) 2016 g10 Code GmbH # Copyright (C) 2004,2008 Igor Belyi # Copyright (C) 2002 John Goerzen # # This library 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. # # This library 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ %module gpgme %include "cpointer.i" %include "cstring.i" /* Generate doc strings for all methods. This will generate docstrings of the form gpgme_op_encrypt(ctx, recp, flags, plain, cipher) -> gpgme_error_t which we transform into ctx.op_encrypt(recp, flags, plain, cipher) -> gpgme_error_t for automagically wrapped functions. */ %feature("autodoc", "0"); /* Allow use of Unicode objects, bytes, and None for strings. */ %typemap(in) const char *(PyObject *encodedInput = NULL) { if ($input == Py_None) $1 = NULL; else if (PyUnicode_Check($input)) { encodedInput = PyUnicode_AsUTF8String($input); if (encodedInput == NULL) return NULL; $1 = PyBytes_AsString(encodedInput); } else if (PyBytes_Check($input)) $1 = PyBytes_AsString($input); else { PyErr_Format(PyExc_TypeError, "arg %d: expected str, bytes, or None, got %s", $argnum, $input->ob_type->tp_name); return NULL; } } %typemap(freearg) const char * { Py_XDECREF(encodedInput$argnum); } /* Likewise for a list of strings. */ %typemap(in) const char *[] (void *vector = NULL, size_t size, PyObject **pyVector = NULL) { /* Check if is a list */ if (PyList_Check($input)) { size_t i, j; size = PyList_Size($input); $1 = (char **) (vector = malloc((size+1) * sizeof(char *))); pyVector = calloc(sizeof *pyVector, size); for (i = 0; i < size; i++) { PyObject *o = PyList_GetItem($input,i); if (PyUnicode_Check(o)) { pyVector[i] = PyUnicode_AsUTF8String(o); if (pyVector[i] == NULL) { free(vector); for (j = 0; j < i; j++) Py_XDECREF(pyVector[j]); return NULL; } $1[i] = PyBytes_AsString(pyVector[i]); } else if (PyString_Check(o)) $1[i] = PyString_AsString(o); else { PyErr_Format(PyExc_TypeError, "arg %d: list must contain only str or bytes, got %s " "at position %d", $argnum, o->ob_type->tp_name, i); free($1); return NULL; } } $1[i] = NULL; } else { PyErr_Format(PyExc_TypeError, "arg %d: expected a list of str or bytes, got %s", $argnum, $input->ob_type->tp_name); return NULL; } } %typemap(freearg) const char *[] { size_t i; free(vector$argnum); for (i = 0; i < size$argnum; i++) Py_XDECREF(pyVector$argnum[i]); } /* Release returned buffers as necessary. */ %typemap(newfree) char * "free($1);"; %newobject gpgme_data_release_and_get_mem; %typemap(arginit) gpgme_key_t [] { $1 = NULL; } %typemap(in) gpgme_key_t [] { int i, numb = 0; if (!PySequence_Check($input)) { PyErr_Format(PyExc_ValueError, "arg %d: Expected a list of gpgme_key_t", $argnum); return NULL; } if((numb = PySequence_Length($input)) != 0) { $1 = (gpgme_key_t*)malloc((numb+1)*sizeof(gpgme_key_t)); for(i=0; idata.mem.buffer; new_size = wrapper$argnum->data.mem.length; dirty = new_data != NULL; #else new_data = gpgme_data_release_and_get_mem (wrapper$argnum, &new_size); wrapper$argnum = NULL; dirty = new_size != view$argnum.len || memcmp (new_data, view$argnum.buf, view$argnum.len); #endif if (dirty) { /* The buffer is dirty. */ if (view$argnum.readonly) { Py_XDECREF(resultobj); resultobj = NULL; PyErr_SetString(PyExc_ValueError, "cannot update read-only buffer"); } /* See if we need to truncate the buffer. */ if (resultobj && view$argnum.len != new_size) { if (bytesio$argnum == NULL) { Py_XDECREF(resultobj); resultobj = NULL; PyErr_SetString(PyExc_ValueError, "cannot resize buffer"); } else { PyObject *retval; PyBuffer_Release(&view$argnum); assert(view$argnum.obj == NULL); retval = PyObject_CallMethod(bytesio$argnum, "truncate", "l", (long) new_size); if (retval == NULL) { Py_XDECREF(resultobj); resultobj = NULL; } else { Py_DECREF(retval); retval = PyObject_CallMethod(bytesio$argnum, "getbuffer", NULL); if (retval == NULL || PyObject_GetBuffer(retval, &view$argnum, PyBUF_SIMPLE|PyBUF_WRITABLE) < 0) { Py_XDECREF(resultobj); resultobj = NULL; } Py_XDECREF(retval); if (resultobj && view$argnum.len != new_size) { Py_XDECREF(resultobj); resultobj = NULL; PyErr_Format(PyExc_ValueError, "Expected buffer of length %zu, got %zi", new_size, view$argnum.len); } } } } if (resultobj) memcpy(view$argnum.buf, new_data, new_size); } #if ! HAVE_DATA_H free (new_data); #endif } /* Free the temporary wrapper, if any. */ if (wrapper$argnum) gpgme_data_release(wrapper$argnum); Py_XDECREF (bytesio$argnum); if (have_view$argnum && view$argnum.buf) PyBuffer_Release(&view$argnum); } %apply gpgme_data_t DATAIN {gpgme_data_t plain, gpgme_data_t cipher, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext, gpgme_data_t keydata, gpgme_data_t pubkey, gpgme_data_t seckey, gpgme_data_t out}; /* SWIG has problems interpreting ssize_t, off_t or gpgme_error_t in gpgme.h. */ %typemap(out) ssize_t, gpgme_error_t, gpgme_err_code_t, gpgme_err_source_t, gpg_error_t { $result = PyLong_FromLong($1); } %typemap(in) ssize_t, gpgme_error_t, gpgme_err_code_t, gpgme_err_source_t, gpg_error_t { if (PyLong_Check($input)) $1 = PyLong_AsLong($input); #if PY_MAJOR_VERSION < 3 else if (PyInt_Check($input)) $1 = PyInt_AsLong($input); #endif else PyErr_SetString(PyExc_TypeError, "Numeric argument expected"); } %typemap(out) off_t { #if _FILE_OFFSET_BITS == 64 $result = PyLong_FromLongLong($1); #else $result = PyLong_FromLong($1); #endif } %typemap(in) off_t { if (PyLong_Check($input)) #if _FILE_OFFSET_BITS == 64 $1 = PyLong_AsLongLong($input); #else $1 = PyLong_AsLong($input); #endif #if PY_MAJOR_VERSION < 3 else if (PyInt_Check($input)) $1 = PyInt_AsLong($input); #endif else PyErr_SetString(PyExc_TypeError, "Numeric argument expected"); } /* Those are for gpgme_data_read() and gpgme_strerror_r(). */ %typemap(in) (void *buffer, size_t size), (char *buf, size_t buflen) { { long tmp$argnum; if (PyLong_Check($input)) tmp$argnum = PyLong_AsLong($input); #if PY_MAJOR_VERSION < 3 else if (PyInt_Check($input)) tmp$argnum = PyInt_AsLong($input); #endif else { PyErr_SetString(PyExc_TypeError, "Numeric argument expected"); return NULL; } if (tmp$argnum < 0) { PyErr_SetString(PyExc_ValueError, "Positive integer expected"); return NULL; } $2 = (size_t) tmp$argnum; $1 = ($1_ltype) malloc($2+1); } } %typemap(argout) (void *buffer, size_t size), (char *buf, size_t buflen) { Py_XDECREF($result); /* Blow away any previous result */ if (result < 0) { /* Check for I/O error */ free($1); return PyErr_SetFromErrno(PyExc_RuntimeError); } $result = PyBytes_FromStringAndSize($1,result); free($1); } /* For gpgme_data_write, but should be universal. */ %typemap(in) (const void *buffer, size_t size)(PyObject *encodedInput = NULL) { Py_ssize_t ssize; if ($input == Py_None) $1 = NULL, $2 = 0; else if (PyUnicode_Check($input)) { encodedInput = PyUnicode_AsUTF8String($input); if (encodedInput == NULL) return NULL; if (PyBytes_AsStringAndSize(encodedInput, (char **) &$1, &ssize) == -1) { Py_DECREF(encodedInput); return NULL; } } else if (PyBytes_Check($input)) PyBytes_AsStringAndSize($input, (char **) &$1, &ssize); else { PyErr_Format(PyExc_TypeError, "arg %d: expected str, bytes, or None, got %s", $argnum, $input->ob_type->tp_name); return NULL; } if (! $1) $2 = 0; else { assert (ssize >= 0); $2 = (size_t) ssize; } } %typemap(freearg) (const void *buffer, size_t size) { Py_XDECREF(encodedInput$argnum); } /* Make types containing 'next' field to be lists. */ %ignore next; %typemap(out) gpgme_sig_notation_t, gpgme_subkey_t, gpgme_key_sig_t, gpgme_user_id_t, gpgme_invalid_key_t, gpgme_recipient_t, gpgme_new_signature_t, gpgme_signature_t, gpgme_import_status_t, gpgme_conf_arg_t, gpgme_conf_opt_t, gpgme_conf_comp_t, gpgme_tofu_info_t { int i; int size = 0; $1_ltype curr; for (curr = $1; curr != NULL; curr = curr->next) { size++; } $result = PyList_New(size); for (i=0,curr=$1; inext) { PyObject *o = SWIG_NewPointerObj(SWIG_as_voidptr(curr), $1_descriptor, %newpointer_flags); PyList_SetItem($result, i, o); } } /* Wrap the fragile result objects into robust Python ones. */ %define wrapresult(cls, name) %typemap(out) cls { PyObject *fragile; fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, %newpointer_flags); $result = _gpg_wrap_result(fragile, name); Py_DECREF(fragile); } %enddef wrapresult(gpgme_encrypt_result_t, "EncryptResult") wrapresult(gpgme_decrypt_result_t, "DecryptResult") wrapresult(gpgme_sign_result_t, "SignResult") wrapresult(gpgme_verify_result_t, "VerifyResult") wrapresult(gpgme_import_result_t, "ImportResult") wrapresult(gpgme_genkey_result_t, "GenkeyResult") wrapresult(gpgme_keylist_result_t, "KeylistResult") wrapresult(gpgme_vfs_mount_result_t, "VFSMountResult") %typemap(out) gpgme_engine_info_t { int i; int size = 0; $1_ltype curr; for (curr = $1; curr != NULL; curr = curr->next) { size++; } $result = PyList_New(size); if ($result == NULL) return NULL; /* raise */ for (i=0,curr=$1; inext) { PyObject *fragile, *o; fragile = SWIG_NewPointerObj(SWIG_as_voidptr(curr), $1_descriptor, %newpointer_flags); if (fragile == NULL) { Py_DECREF($result); return NULL; /* raise */ } o = _gpg_wrap_result(fragile, "EngineInfo"); Py_DECREF(fragile); if (o == NULL) { Py_DECREF($result); return NULL; /* raise */ } PyList_SetItem($result, i, o); } } /* Include mapper for interact callbacks. */ %typemap(in) (gpgme_interact_cb_t fnc, void *fnc_value) { if (! PyTuple_Check($input)) return PyErr_Format(PyExc_TypeError, "interact callback must be a tuple"); if (PyTuple_Size($input) != 2 && PyTuple_Size($input) != 3) return PyErr_Format(PyExc_TypeError, "interact callback must be a tuple of size 2 or 3"); $1 = (gpgme_interact_cb_t) _gpg_interact_cb; $2 = $input; } /* The assuan protocol callbacks. */ %typemap(in) (gpgme_assuan_data_cb_t data_cb, void *data_cb_value) { if ($input == Py_None) $1 = $2 = NULL; else { if (! PyTuple_Check($input)) return PyErr_Format(PyExc_TypeError, "callback must be a tuple"); if (PyTuple_Size($input) != 2) return PyErr_Format(PyExc_TypeError, "callback must be a tuple of size 2"); if (! PyCallable_Check(PyTuple_GetItem($input, 1))) return PyErr_Format(PyExc_TypeError, "second item must be callable"); $1 = _gpg_assuan_data_cb; $2 = $input; } } %typemap(in) (gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value) { if ($input == Py_None) $1 = $2 = NULL; else { if (! PyTuple_Check($input)) return PyErr_Format(PyExc_TypeError, "callback must be a tuple"); if (PyTuple_Size($input) != 2) return PyErr_Format(PyExc_TypeError, "callback must be a tuple of size 2"); if (! PyCallable_Check(PyTuple_GetItem($input, 1))) return PyErr_Format(PyExc_TypeError, "second item must be callable"); $1 = _gpg_assuan_inquire_cb; $2 = $input; } } %typemap(in) (gpgme_assuan_status_cb_t stat_cb, void *stat_cb_value) { if ($input == Py_None) $1 = $2 = NULL; else { if (! PyTuple_Check($input)) return PyErr_Format(PyExc_TypeError, "callback must be a tuple"); if (PyTuple_Size($input) != 2) return PyErr_Format(PyExc_TypeError, "callback must be a tuple of size 2"); if (! PyCallable_Check(PyTuple_GetItem($input, 1))) return PyErr_Format(PyExc_TypeError, "second item must be callable"); $1 = _gpg_assuan_status_cb; $2 = $input; } } + +/* With SWIG, you can define default arguments for parameters. + * While it's legal in C++ it is not in C, so we cannot change the + * already existing gpgme.h. We need, however, to declare the function + * *before* SWIG loads it from gpgme.h. Hence, we define it here. */ +gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx, + const char *pattern="", + int secret_only=0); + /* Include the unmodified for cc, and the cleaned-up local version for SWIG. We do, however, want to hide certain fields on some structs, which we provide prior to including the version for SWIG. */ %{ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include %} /* This is for notations, where we want to hide the length fields, and the unused bit field block. */ struct _gpgme_sig_notation { struct _gpgme_sig_notation *next; /* If NAME is a null pointer, then VALUE contains a policy URL rather than a notation. */ char *name; /* The value of the notation data. */ char *value; /* The accumulated flags. */ gpgme_sig_notation_flags_t flags; /* Notation data is human-readable. */ unsigned int human_readable : 1; /* Notation data is critical. */ unsigned int critical : 1; }; /* Now include our local modified version. Any structs defined above are ignored. */ #ifdef HAVE_CONFIG_H %include "config.h" #endif %include "gpgme.h" %include "errors.i" /* Generating and handling pointers-to-pointers. */ %pointer_functions(gpgme_ctx_t, gpgme_ctx_t_p); %pointer_functions(gpgme_data_t, gpgme_data_t_p); %pointer_functions(gpgme_key_t, gpgme_key_t_p); %pointer_functions(gpgme_error_t, gpgme_error_t_p); %pointer_functions(gpgme_trust_item_t, gpgme_trust_item_t_p); %pointer_functions(gpgme_engine_info_t, gpgme_engine_info_t_p); /* Helper functions. */ %{ #include %} FILE *fdopen(int fildes, const char *mode); /* We include both headers in the generated c code... */ %{ #include "helpers.h" #include "private.h" /* SWIG runtime support for helpers.c */ PyObject * _gpg_wrap_gpgme_data_t(gpgme_data_t data) { return SWIG_Python_NewPointerObj(NULL, data, SWIGTYPE_p_gpgme_data, 0); } gpgme_ctx_t _gpg_unwrap_gpgme_ctx_t(PyObject *wrapped) { gpgme_ctx_t result; if (SWIG_ConvertPtr(wrapped, (void **) &result, SWIGTYPE_p_gpgme_context, SWIG_POINTER_EXCEPTION) == -1) return NULL; return result; } %} /* ... but only the public definitions here. They will be exposed to the Python world, so let's be careful. */ %include "helpers.h" diff --git a/lang/python/tests/t-keylist.py b/lang/python/tests/t-keylist.py index ea2a7240..5077ca69 100755 --- a/lang/python/tests/t-keylist.py +++ b/lang/python/tests/t-keylist.py @@ -1,268 +1,280 @@ #!/usr/bin/env python # Copyright (C) 2016 g10 Code GmbH # # This file is part of GPGME. # # GPGME is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # GPGME 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 . from __future__ import absolute_import, print_function, unicode_literals del absolute_import, print_function, unicode_literals import gpg import support support.init_gpgme(gpg.constants.protocol.OpenPGP) c = gpg.Context() # Check expration of keys. This test assumes three subkeys of which # 2 are expired; it is used with the "Whisky" test key. It has # already been checked that these 3 subkeys are available. def check_whisky(name, key): sub1 = key.subkeys[2] sub2 = key.subkeys[3] assert sub1.expired and sub2.expired, \ "Subkey of `{}' not flagged as expired".format(name) assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \ "Subkey of `{}' has wrong expiration date".format(name) keys = [ [ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8", [ [ "Alfa Test", "demo key", "alfa@example.net" ], [ "Alpha Test", "demo key", "alpha@example.net" ], [ "Alice", "demo key", "" ] ], 1 ], [ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F", [ [ "Bob", "demo key", "" ], [ "Bravo Test", "demo key", "bravo@example.net" ] ], 1 ], [ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60", [ [ "Charlie Test", "demo key", "charlie@example.net" ] ], 1 ], [ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424", [ [ "Delta Test", "demo key", "delta@example.net" ] ], 1 ], [ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D", [ [ "Echelon", "demo key", "" ], [ "Echo Test", "demo key", "echo@example.net" ], [ "Eve", "demo key", "" ] ], 1 ], [ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E", [ [ "Foxtrot Test", "demo key", "foxtrot@example.net" ] ], 1 ], [ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354", [ [ "Golf Test", "demo key", "golf@example.net" ] ], 1 ], [ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A", [ [ "Hotel Test", "demo key", "hotel@example.net" ] ], 1 ], [ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73", [ [ "India Test", "demo key", "india@example.net" ] ], 1 ], [ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136", [ [ "Juliet Test", "demo key", "juliet@example.net" ] ], 1 ], [ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02", [ [ "Kilo Test", "demo key", "kilo@example.net" ] ], 1 ], [ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C", [ [ "Lima Test", "demo key", "lima@example.net" ] ], 1 ], [ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8", [ [ "Mallory", "demo key", "" ], [ "Mike Test", "demo key", "mike@example.net" ] ], 1 ], [ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472", [ [ "November Test", "demo key", "november@example.net" ] ], 1 ], [ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F", [ [ "Oscar Test", "demo key", "oscar@example.net" ] ], 1 ], [ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C", [ [ "Papa test", "demo key", "papa@example.net" ] ], 1 ], [ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4", [ [ "Quebec Test", "demo key", "quebec@example.net" ] ], 1 ], [ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA", [ [ "Romeo Test", "demo key", "romeo@example.net" ] ], 1 ], [ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4", [ [ "Sierra Test", "demo key", "sierra@example.net" ] ], 1 ], [ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402", [ [ "Tango Test", "demo key", "tango@example.net" ] ], 1 ], [ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9", [ [ "Uniform Test", "demo key", "uniform@example.net" ] ], 1 ], [ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134", [ [ "Victor Test", "demo key", "victor@example.org" ] ], 1 ], [ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6", [ [ "Whisky Test", "demo key", "whisky@example.net" ] ], 3, check_whisky ], [ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE", [ [ "XRay Test", "demo key", "xray@example.net" ] ], 1 ], [ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD", [ [ "Yankee Test", "demo key", "yankee@example.net" ] ], 1 ], [ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881", [ [ "Zulu Test", "demo key", "zulu@example.net" ] ], 1 ], ] def check_global(key, uids, n_subkeys): assert not key.revoked, "Key unexpectedly revoked" assert not key.expired, "Key unexpectedly expired" assert not key.disabled, "Key unexpectedly disabled" assert not key.invalid, "Key unexpectedly invalid" assert key.can_sign, "Key unexpectedly unusable for signing" assert key.can_certify, "Key unexpectedly unusable for certifications" assert not key.secret, "Key unexpectedly secret" assert not key.protocol != gpg.constants.protocol.OpenPGP, \ "Key has unexpected protocol: {}".format(key.protocol) assert not key.issuer_serial, \ "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial) assert not key.issuer_name, \ "Key unexpectedly carries issuer name: {}".format(key.issuer_name) assert not key.chain_id, \ "Key unexpectedly carries chain ID: {}".format(key.chain_id) # Only key Alfa is trusted assert key.uids[0].name == 'Alfa Test' \ or key.owner_trust == gpg.constants.validity.UNKNOWN, \ "Key has unexpected owner trust: {}".format(key.owner_trust) assert key.uids[0].name != 'Alfa Test' \ or key.owner_trust == gpg.constants.validity.ULTIMATE, \ "Key has unexpected owner trust: {}".format(key.owner_trust) assert len(key.subkeys) - 1 == n_subkeys, \ "Key `{}' has unexpected number of subkeys".format(uids[0][0]) def check_subkey(fpr, which, subkey): assert not subkey.revoked, which + " key unexpectedly revoked" assert not subkey.expired, which + " key unexpectedly expired" assert not subkey.disabled, which + " key unexpectedly disabled" assert not subkey.invalid, which + " key unexpectedly invalid" if which == "Primary": assert not subkey.can_encrypt, \ which + " key unexpectedly usable for encryption" assert subkey.can_sign, \ which + " key unexpectedly unusable for signing" assert subkey.can_certify, \ which + " key unexpectedly unusable for certifications" else: assert subkey.can_encrypt, \ which + " key unexpectedly unusable for encryption" assert not subkey.can_sign, \ which + " key unexpectedly usable for signing" assert not subkey.can_certify, \ which + " key unexpectedly usable for certifications" assert not subkey.secret, which + " key unexpectedly secret" assert not subkey.is_cardkey, "Public key marked as card key" assert not subkey.card_number, "Public key with card number set" assert not subkey.pubkey_algo != (gpg.constants.pk.DSA if which == "Primary" else gpg.constants.pk.ELG_E), \ which + " key has unexpected public key algo: {}".\ format(subkey.pubkey_algo) assert subkey.length == 1024, \ which + " key has unexpected length: {}".format(subkey.length) assert fpr.endswith(subkey.keyid), \ which + " key has unexpected key ID: {}".format(subkey.keyid) assert which == "Secondary" or subkey.fpr == fpr, \ which + " key has unexpected fingerprint: {}".format(subkey.fpr) assert not subkey.expires, \ which + " key unexpectedly expires: {}".format(subkey.expires) def check_uid(which, ref, uid): assert not uid.revoked, which + " user ID unexpectedly revoked" assert not uid.invalid, which + " user ID unexpectedly invalid" assert uid.validity == (gpg.constants.validity.UNKNOWN if uid.name.split()[0] not in {'Alfa', 'Alpha', 'Alice'} else gpg.constants.validity.ULTIMATE), \ which + " user ID has unexpectedly validity: {}".format(uid.validity) assert not uid.signatures, which + " user ID unexpectedly signed" assert uid.name == ref[0], \ "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name) assert uid.comment == ref[1], \ "Unexpected comment in {} user ID: {!r}".format(which.lower(), uid.comment) assert uid.email == ref[2], \ "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email) i = 0 c.op_keylist_start(None, False) key = c.op_keylist_next () while key: try: if len(keys[i]) == 4: fpr, sec_keyid, uids, n_subkeys = keys[i] misc_check = None else: fpr, sec_keyid, uids, n_subkeys, misc_check = keys[i] except IndexError: # There are more keys. We don't check for that. break # Global key flags. check_global(key, uids, n_subkeys) check_subkey(fpr, "Primary", key.subkeys[0]) check_subkey(sec_keyid, "Secondary", key.subkeys[1]) assert len(key.uids) == len(uids) check_uid("First", uids[0], key.uids[0]) if len(key.uids) > 1: check_uid("Second", uids[1], key.uids[1]) if len(key.uids) > 2: check_uid("Third", uids[2], key.uids[2]) if misc_check: misc_check (uids[0][0], key) key = c.op_keylist_next () i += 1 c.op_keylist_end() result = c.op_keylist_result() assert not result.truncated, "Key listing unexpectedly truncated" +# We test for a parameter-less keylist +keyring_length = len(list(c.op_keylist_all())) +assert keyring_length > 1,\ + "Expected to find some keys, but got %r" % keyring_length + +# Then we do want to call with a pattern, only +# i.e. without giving secret=0 +alpha_keys = list(c.op_keylist_all(b"Alpha")) +assert len(alpha_keys) == 1, "Expected only one key for 'Alpha', got %r" % len(alpha_keys) + + + for i, key in enumerate(c.keylist()): try: if len(keys[i]) == 4: fpr, sec_keyid, uids, n_subkeys = keys[i] misc_check = None else: fpr, sec_keyid, uids, n_subkeys, misc_check = keys[i] except IndexError: # There are more keys. We don't check for that. break # Global key flags. check_global(key, uids, n_subkeys) check_subkey(fpr, "Primary", key.subkeys[0]) check_subkey(sec_keyid, "Secondary", key.subkeys[1]) assert len(key.uids) == len(uids) check_uid("First", uids[0], key.uids[0]) if len(key.uids) > 1: check_uid("Second", uids[1], key.uids[1]) if len(key.uids) > 2: check_uid("Third", uids[2], key.uids[2]) if misc_check: misc_check (uids[0][0], key) # check get_key() with gpg.Context() as c: c.get_key(support.alpha) c.get_key(support.alpha, secret=True) c.get_key(support.bob) try: c.get_key(support.bob, secret=True) except KeyError: pass else: assert False, "Expected KeyError" # Legacy error try: c.get_key(support.no_such_key) except gpg.errors.GPGMEError: pass else: assert False, "Expected GPGMEError"