5380ff26ea14bc22e5a66f82259dd56b49a5595a
[gpgme.git] / lang / python / helpers.c
1 /*
2 # Copyright (C) 2016 g10 Code GmbH
3 # Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
4 # Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
5 #
6 #    This library is free software; you can redistribute it and/or
7 #    modify it under the terms of the GNU Lesser General Public
8 #    License as published by the Free Software Foundation; either
9 #    version 2.1 of the License, or (at your option) any later version.
10 #
11 #    This library is distributed in the hope that it will be useful,
12 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 #    Lesser General Public License for more details.
15 #
16 #    You should have received a copy of the GNU Lesser General Public
17 #    License along with this library; if not, write to the Free Software
18 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19 */
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <gpgme.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include "Python.h"
27 #include "helpers.h"
28
29 static PyObject *GPGMEError = NULL;
30
31 void pygpgme_exception_init(void) {
32   if (GPGMEError == NULL) {
33     PyObject *errors;
34     PyObject *from_list = PyList_New(0);
35     errors = PyImport_ImportModuleLevel("errors", PyEval_GetGlobals(),
36                                         PyEval_GetLocals(), from_list, 1);
37     Py_XDECREF(from_list);
38     if (errors) {
39       GPGMEError=PyDict_GetItemString(PyModule_GetDict(errors), "GPGMEError");
40       Py_XINCREF(GPGMEError);
41     }
42   }
43 }
44
45 static PyObject *
46 pygpgme_raise_exception(gpgme_error_t err)
47 {
48   PyObject *e;
49
50   pygpgme_exception_init();
51   if (GPGMEError == NULL)
52     return PyErr_Format(PyExc_RuntimeError, "Got gpgme_error_t %d", err);
53
54   e = PyObject_CallFunction(GPGMEError, "l", (long) err);
55   if (e == NULL)
56     return NULL;
57
58   PyErr_SetObject(GPGMEError, e);
59   Py_DECREF(e);
60
61   return NULL;  /* raise */
62 }
63
64 gpgme_error_t pygpgme_exception2code(void) {
65   gpgme_error_t err_status = gpg_error(GPG_ERR_GENERAL);
66   if (GPGMEError && PyErr_ExceptionMatches(GPGMEError)) {
67     PyObject *type = 0, *value = 0, *traceback = 0;
68     PyObject *error = 0;
69     PyErr_Fetch(&type, &value, &traceback);
70     PyErr_NormalizeException(&type, &value, &traceback);
71     error = PyObject_GetAttrString(value, "error");
72     err_status = PyLong_AsLong(error);
73     Py_DECREF(error);
74     PyErr_Restore(type, value, traceback);
75   }
76   return err_status;
77 }
78
79 /* Exception support for callbacks.  */
80 #define EXCINFO "_callback_excinfo"
81
82 static void pygpgme_stash_callback_exception(PyObject *weak_self)
83 {
84   PyObject *self, *ptype, *pvalue, *ptraceback, *excinfo;
85
86   PyErr_Fetch(&ptype, &pvalue, &ptraceback);
87   excinfo = PyTuple_New(3);
88   PyTuple_SetItem(excinfo, 0, ptype);
89
90   if (pvalue)
91     PyTuple_SetItem(excinfo, 1, pvalue);
92   else {
93     Py_INCREF(Py_None);
94     PyTuple_SetItem(excinfo, 1, Py_None);
95   }
96
97   if (ptraceback)
98     PyTuple_SetItem(excinfo, 2, ptraceback);
99   else {
100     Py_INCREF(Py_None);
101     PyTuple_SetItem(excinfo, 2, Py_None);
102   }
103
104   self = PyWeakref_GetObject(weak_self);
105   /* self only has a borrowed reference.  */
106   if (self == Py_None) {
107     /* This should not happen, as even if we're called from the data
108        release callback triggered from the wrappers destructor, the
109        object is still alive and hence the weak reference still refers
110        to the object.  However, in case this ever changes, not seeing
111        any exceptions is worse than having a little extra code, so
112        here we go.  */
113       fprintf(stderr,
114               "Error occurred in callback, but the wrapper object "
115               "has been deallocated.\n");
116       PyErr_Restore(ptype, pvalue, ptraceback);
117       PyErr_Print();
118     }
119   else
120     PyObject_SetAttrString(self, EXCINFO, excinfo);
121   Py_DECREF(excinfo);
122 }
123
124 PyObject *pygpgme_raise_callback_exception(PyObject *self)
125 {
126   PyObject *ptype, *pvalue, *ptraceback, *excinfo;
127
128   if (! PyObject_HasAttrString(self, EXCINFO))
129     goto leave;
130
131   excinfo = PyObject_GetAttrString(self, EXCINFO);
132   if (! PyTuple_Check(excinfo))
133     {
134       Py_DECREF(excinfo);
135       goto leave;
136     }
137
138   ptype = PyTuple_GetItem(excinfo, 0);
139   Py_INCREF(excinfo);
140
141   pvalue = PyTuple_GetItem(excinfo, 1);
142   if (pvalue == Py_None)
143     pvalue = NULL;
144   else
145     Py_INCREF(pvalue);
146
147   ptraceback = PyTuple_GetItem(excinfo, 2);
148   if (ptraceback == Py_None)
149     ptraceback = NULL;
150   else
151     Py_INCREF(ptraceback);
152
153   Py_DECREF(excinfo);
154   PyErr_Restore(ptype, pvalue, ptraceback);
155
156   Py_INCREF(Py_None);
157   PyObject_SetAttrString(self, EXCINFO, Py_None);
158
159   return NULL; /* Raise exception.  */
160
161  leave:
162   Py_INCREF(Py_None);
163   return Py_None;
164 }
165 #undef EXCINFO
166 \f
167 /* Argument conversion.  */
168
169 /* Convert object to a pointer to gpgme type, generic version.  */
170 PyObject *
171 object_to_gpgme_t(PyObject *input, const char *objtype, int argnum)
172 {
173   PyObject *pyname = NULL, *pypointer = NULL;
174   pyname = PyObject_CallMethod(input, "_getctype", NULL);
175   if (pyname && PyUnicode_Check(pyname))
176     {
177       if (strcmp(PyUnicode_AsUTF8(pyname), objtype) != 0)
178         {
179           PyErr_Format(PyExc_TypeError,
180                        "arg %d: Expected value of type %s, but got %s",
181                        argnum, objtype, PyUnicode_AsUTF8(pyname));
182           Py_DECREF(pyname);
183           return NULL;
184         }
185     }
186   else
187     return NULL;
188
189   Py_DECREF(pyname);
190   pypointer = PyObject_GetAttrString(input, "wrapped");
191   if (pypointer == NULL) {
192     PyErr_Format(PyExc_TypeError,
193                  "arg %d: Use of uninitialized Python object %s",
194                  argnum, objtype);
195     return NULL;
196   }
197   return pypointer;
198 }
199
200 /* Convert object to a pointer to gpgme type, version for data
201    objects.  Constructs a wrapper Python on the fly e.g. for file-like
202    objects with a fileno method, returning it in WRAPPER.  This object
203    must be de-referenced when no longer needed.  */
204 PyObject *
205 object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
206                        PyObject **bytesio, Py_buffer *view)
207 {
208   gpgme_error_t err;
209   PyObject *data;
210   PyObject *fd;
211
212   /* See if it is a file-like object with file number.  */
213   fd = PyObject_CallMethod(input, "fileno", NULL);
214   if (fd) {
215     err = gpgme_data_new_from_fd(wrapper, (int) PyLong_AsLong(fd));
216     Py_DECREF(fd);
217     if (err)
218       return pygpgme_raise_exception (err);
219
220     return pygpgme_wrap_gpgme_data_t(*wrapper);
221   }
222   else
223     PyErr_Clear();
224
225   /* No?  Maybe it implements the buffer protocol.  */
226   data = PyObject_CallMethod(input, "getbuffer", NULL);
227   if (data)
228     {
229       /* Save a reference to input, which seems to be a BytesIO
230          object.  */
231       Py_INCREF(input);
232       *bytesio = input;
233     }
234   else
235     {
236       PyErr_Clear();
237
238       /* No, but maybe the user supplied a buffer object?  */
239       data = input;
240     }
241
242   /* Do we have a buffer object?  */
243   if (PyObject_CheckBuffer(data))
244     {
245       if (PyObject_GetBuffer(data, view, PyBUF_SIMPLE) < 0)
246         return NULL;
247
248       if (data != input)
249         Py_DECREF(data);
250
251       assert (view->ndim == 1);
252       assert (view->shape == NULL);
253       assert (view->strides == NULL);
254       assert (view->suboffsets == NULL);
255
256       err = gpgme_data_new_from_mem(wrapper, view->buf, (size_t) view->len, 0);
257       if (err)
258         return pygpgme_raise_exception (err);
259
260       return pygpgme_wrap_gpgme_data_t(*wrapper);
261     }
262
263   /* As last resort we assume it is a wrapped data object.  */
264   if (PyObject_HasAttrString(data, "_getctype"))
265     return object_to_gpgme_t(data, "gpgme_data_t", argnum);
266
267   return PyErr_Format(PyExc_TypeError,
268                       "arg %d: expected pyme.Data, file, or an object "
269                       "implementing the buffer protocol, got %s",
270                       argnum, data->ob_type->tp_name);
271 }
272
273 \f
274
275 /* Callback support.  */
276 static gpgme_error_t pyPassphraseCb(void *hook,
277                                     const char *uid_hint,
278                                     const char *passphrase_info,
279                                     int prev_was_bad,
280                                     int fd) {
281   PyObject *pyhook = (PyObject *) hook;
282   PyObject *self = NULL;
283   PyObject *func = NULL;
284   PyObject *args = NULL;
285   PyObject *retval = NULL;
286   PyObject *dataarg = NULL;
287   gpgme_error_t err_status = 0;
288
289   pygpgme_exception_init();
290
291   assert (PyTuple_Check(pyhook));
292   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
293   self = PyTuple_GetItem(pyhook, 0);
294   func = PyTuple_GetItem(pyhook, 1);
295   if (PyTuple_Size(pyhook) == 3) {
296     dataarg = PyTuple_GetItem(pyhook, 2);
297     args = PyTuple_New(4);
298   } else {
299     args = PyTuple_New(3);
300   }
301
302   if (uid_hint == NULL)
303     {
304       Py_INCREF(Py_None);
305       PyTuple_SetItem(args, 0, Py_None);
306     }
307   else
308     PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
309                                                   "strict"));
310   if (PyErr_Occurred()) {
311     Py_DECREF(args);
312     err_status = gpg_error(GPG_ERR_GENERAL);
313     goto leave;
314   }
315
316   PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
317   PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
318   if (dataarg) {
319     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
320     PyTuple_SetItem(args, 3, dataarg);
321   }
322
323   retval = PyObject_CallObject(func, args);
324   Py_DECREF(args);
325   if (PyErr_Occurred()) {
326     err_status = pygpgme_exception2code();
327   } else {
328     if (!retval) {
329       if (write(fd, "\n", 1) < 0) {
330         err_status = gpgme_error_from_syserror ();
331         pygpgme_raise_exception (err_status);
332       }
333     } else {
334       char *buf;
335       size_t len;
336       if (PyBytes_Check(retval))
337         buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
338       else if (PyUnicode_Check(retval))
339         {
340           Py_ssize_t ssize;
341           buf = PyUnicode_AsUTF8AndSize(retval, &ssize);
342           assert (! buf || ssize >= 0);
343           len = (size_t) ssize;
344         }
345       else
346         {
347           PyErr_Format(PyExc_TypeError,
348                        "expected str or bytes from passphrase callback, got %s",
349                        retval->ob_type->tp_name);
350           err_status = gpg_error(GPG_ERR_GENERAL);
351           goto leave;
352         }
353
354       if (write(fd, buf, len) < 0) {
355         err_status = gpgme_error_from_syserror ();
356         pygpgme_raise_exception (err_status);
357       }
358       if (! err_status && write(fd, "\n", 1) < 0) {
359         err_status = gpgme_error_from_syserror ();
360         pygpgme_raise_exception (err_status);
361       }
362
363       Py_DECREF(retval);
364     }
365   }
366
367  leave:
368   if (err_status)
369     pygpgme_stash_callback_exception(self);
370
371   return err_status;
372 }
373
374 PyObject *
375 pygpgme_set_passphrase_cb(PyObject *self, PyObject *cb) {
376   PyObject *wrapped;
377   gpgme_ctx_t ctx;
378
379   wrapped = PyObject_GetAttrString(self, "wrapped");
380   if (wrapped == NULL)
381     {
382       assert (PyErr_Occurred ());
383       return NULL;
384     }
385
386   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
387   Py_DECREF(wrapped);
388   if (ctx == NULL)
389     {
390       if (cb == Py_None)
391         goto out;
392       else
393         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
394     }
395
396   if (cb == Py_None) {
397     gpgme_set_passphrase_cb(ctx, NULL, NULL);
398     PyObject_SetAttrString(self, "_passphrase_cb", Py_None);
399     goto out;
400   }
401
402   if (! PyTuple_Check(cb))
403     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
404   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
405     return PyErr_Format(PyExc_TypeError,
406                         "cb must be a tuple of size 2 or 3");
407
408   gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t) pyPassphraseCb,
409                           (void *) cb);
410   PyObject_SetAttrString(self, "_passphrase_cb", cb);
411
412  out:
413   Py_INCREF(Py_None);
414   return Py_None;
415 }
416
417 static void pyProgressCb(void *hook, const char *what, int type, int current,
418                          int total) {
419   PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
420   PyObject *pyhook = (PyObject *) hook;
421   PyObject *self = NULL;
422
423   assert (PyTuple_Check(pyhook));
424   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
425   self = PyTuple_GetItem(pyhook, 0);
426   func = PyTuple_GetItem(pyhook, 1);
427   if (PyTuple_Size(pyhook) == 3) {
428     dataarg = PyTuple_GetItem(pyhook, 2);
429     args = PyTuple_New(5);
430   } else {
431     args = PyTuple_New(4);
432   }
433
434   PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
435                                                 "strict"));
436   if (PyErr_Occurred()) {
437     pygpgme_stash_callback_exception(self);
438     Py_DECREF(args);
439     return;
440   }
441   PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
442   PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
443   PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
444   if (dataarg) {
445     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
446     PyTuple_SetItem(args, 4, dataarg);
447   }
448
449   retval = PyObject_CallObject(func, args);
450   if (PyErr_Occurred())
451     pygpgme_stash_callback_exception(self);
452   Py_DECREF(args);
453   Py_XDECREF(retval);
454 }
455
456 PyObject *
457 pygpgme_set_progress_cb(PyObject *self, PyObject *cb) {
458   PyObject *wrapped;
459   gpgme_ctx_t ctx;
460
461   wrapped = PyObject_GetAttrString(self, "wrapped");
462   if (wrapped == NULL)
463     {
464       assert (PyErr_Occurred ());
465       return NULL;
466     }
467
468   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
469   Py_DECREF(wrapped);
470   if (ctx == NULL)
471     {
472       if (cb == Py_None)
473         goto out;
474       else
475         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
476     }
477
478   if (cb == Py_None) {
479     gpgme_set_progress_cb(ctx, NULL, NULL);
480     PyObject_SetAttrString(self, "_progress_cb", Py_None);
481     goto out;
482   }
483
484   if (! PyTuple_Check(cb))
485     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
486   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
487     return PyErr_Format(PyExc_TypeError,
488                         "cb must be a tuple of size 2 or 3");
489
490   gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
491   PyObject_SetAttrString(self, "_progress_cb", cb);
492
493  out:
494   Py_INCREF(Py_None);
495   return Py_None;
496 }
497 \f
498 /* Status callbacks.  */
499 static gpgme_error_t pyStatusCb(void *hook, const char *keyword,
500                                 const char *args) {
501   gpgme_error_t err = 0;
502   PyObject *pyhook = (PyObject *) hook;
503   PyObject *self = NULL;
504   PyObject *func = NULL;
505   PyObject *dataarg = NULL;
506   PyObject *pyargs = NULL;
507   PyObject *retval = NULL;
508
509   assert (PyTuple_Check(pyhook));
510   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
511   self = PyTuple_GetItem(pyhook, 0);
512   func = PyTuple_GetItem(pyhook, 1);
513   if (PyTuple_Size(pyhook) == 3) {
514     dataarg = PyTuple_GetItem(pyhook, 2);
515     pyargs = PyTuple_New(3);
516   } else {
517     pyargs = PyTuple_New(2);
518   }
519
520   if (keyword)
521     PyTuple_SetItem(pyargs, 0, PyUnicode_DecodeUTF8(keyword, strlen (keyword),
522                                                     "strict"));
523   else
524     {
525       Py_INCREF(Py_None);
526       PyTuple_SetItem(pyargs, 0, Py_None);
527     }
528   PyTuple_SetItem(pyargs, 1, PyUnicode_DecodeUTF8(args, strlen (args),
529                                                 "strict"));
530   if (PyErr_Occurred()) {
531     err = gpg_error(GPG_ERR_GENERAL);
532     Py_DECREF(pyargs);
533     goto leave;
534   }
535
536   if (dataarg) {
537     Py_INCREF(dataarg);
538     PyTuple_SetItem(pyargs, 2, dataarg);
539   }
540
541   retval = PyObject_CallObject(func, pyargs);
542   if (PyErr_Occurred())
543     err = pygpgme_exception2code();
544   Py_DECREF(pyargs);
545   Py_XDECREF(retval);
546
547  leave:
548   if (err)
549     pygpgme_stash_callback_exception(self);
550   return err;
551 }
552
553 PyObject *
554 pygpgme_set_status_cb(PyObject *self, PyObject *cb) {
555   PyObject *wrapped;
556   gpgme_ctx_t ctx;
557
558   wrapped = PyObject_GetAttrString(self, "wrapped");
559   if (wrapped == NULL)
560     {
561       assert (PyErr_Occurred ());
562       return NULL;
563     }
564
565   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
566   Py_DECREF(wrapped);
567   if (ctx == NULL)
568     {
569       if (cb == Py_None)
570         goto out;
571       else
572         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
573     }
574
575   if (cb == Py_None) {
576     gpgme_set_status_cb(ctx, NULL, NULL);
577     PyObject_SetAttrString(self, "_status_cb", Py_None);
578     goto out;
579   }
580
581   if (! PyTuple_Check(cb))
582     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
583   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
584     return PyErr_Format(PyExc_TypeError,
585                         "cb must be a tuple of size 2 or 3");
586
587   gpgme_set_status_cb(ctx, (gpgme_status_cb_t) pyStatusCb, (void *) cb);
588   PyObject_SetAttrString(self, "_status_cb", cb);
589
590  out:
591   Py_INCREF(Py_None);
592   return Py_None;
593 }
594 \f
595 /* Edit callbacks.  */
596 gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
597                        const char *args, int fd) {
598   PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
599   PyObject *pyopaque = (PyObject *) opaque;
600   gpgme_error_t err_status = 0;
601   PyObject *self = NULL;
602
603   pygpgme_exception_init();
604
605   assert (PyTuple_Check(pyopaque));
606   assert (PyTuple_Size(pyopaque) == 2 || PyTuple_Size(pyopaque) == 3);
607   self = PyTuple_GetItem(pyopaque, 0);
608   func = PyTuple_GetItem(pyopaque, 1);
609   if (PyTuple_Size(pyopaque) == 3) {
610     dataarg = PyTuple_GetItem(pyopaque, 2);
611     pyargs = PyTuple_New(3);
612   } else {
613     pyargs = PyTuple_New(2);
614   }
615
616   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
617   PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
618   if (dataarg) {
619     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
620     PyTuple_SetItem(pyargs, 2, dataarg);
621   }
622
623   retval = PyObject_CallObject(func, pyargs);
624   Py_DECREF(pyargs);
625   if (PyErr_Occurred()) {
626     err_status = pygpgme_exception2code();
627   } else {
628     if (fd>=0 && retval && PyUnicode_Check(retval)) {
629       const char *buffer;
630       Py_ssize_t size;
631
632       buffer = PyUnicode_AsUTF8AndSize(retval, &size);
633       if (write(fd, buffer, size) < 0) {
634         err_status = gpgme_error_from_syserror ();
635         pygpgme_raise_exception (err_status);
636       }
637       if (! err_status && write(fd, "\n", 1) < 0) {
638         err_status = gpgme_error_from_syserror ();
639         pygpgme_raise_exception (err_status);
640       }
641     }
642   }
643   if (err_status)
644     pygpgme_stash_callback_exception(self);
645
646   Py_XDECREF(retval);
647   return err_status;
648 }
649 \f
650 /* Data callbacks.  */
651
652 /* Read up to SIZE bytes into buffer BUFFER from the data object with
653    the handle HOOK.  Return the number of characters read, 0 on EOF
654    and -1 on error.  If an error occurs, errno is set.  */
655 static ssize_t pyDataReadCb(void *hook, void *buffer, size_t size)
656 {
657   ssize_t result;
658   PyObject *pyhook = (PyObject *) hook;
659   PyObject *self = NULL;
660   PyObject *func = NULL;
661   PyObject *dataarg = NULL;
662   PyObject *pyargs = NULL;
663   PyObject *retval = NULL;
664
665   assert (PyTuple_Check(pyhook));
666   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
667
668   self = PyTuple_GetItem(pyhook, 0);
669   func = PyTuple_GetItem(pyhook, 1);
670   if (PyTuple_Size(pyhook) == 6) {
671     dataarg = PyTuple_GetItem(pyhook, 5);
672     pyargs = PyTuple_New(2);
673   } else {
674     pyargs = PyTuple_New(1);
675   }
676
677   PyTuple_SetItem(pyargs, 0, PyLong_FromSize_t(size));
678   if (dataarg) {
679     Py_INCREF(dataarg);
680     PyTuple_SetItem(pyargs, 1, dataarg);
681   }
682
683   retval = PyObject_CallObject(func, pyargs);
684   Py_DECREF(pyargs);
685   if (PyErr_Occurred()) {
686     pygpgme_stash_callback_exception(self);
687     result = -1;
688     goto leave;
689   }
690
691   if (! PyBytes_Check(retval)) {
692     PyErr_Format(PyExc_TypeError,
693                  "expected bytes from read callback, got %s",
694                  retval->ob_type->tp_name);
695     pygpgme_stash_callback_exception(self);
696     result = -1;
697     goto leave;
698   }
699
700   if (PyBytes_Size(retval) > size) {
701     PyErr_Format(PyExc_TypeError,
702                  "expected %zu bytes from read callback, got %zu",
703                  size, PyBytes_Size(retval));
704     pygpgme_stash_callback_exception(self);
705     result = -1;
706     goto leave;
707   }
708
709   memcpy(buffer, PyBytes_AsString(retval), PyBytes_Size(retval));
710   result = PyBytes_Size(retval);
711
712  leave:
713   Py_XDECREF(retval);
714   return result;
715 }
716
717 /* Write up to SIZE bytes from buffer BUFFER to the data object with
718    the handle HOOK.  Return the number of characters written, or -1
719    on error.  If an error occurs, errno is set.  */
720 static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
721 {
722   ssize_t result;
723   PyObject *pyhook = (PyObject *) hook;
724   PyObject *self = NULL;
725   PyObject *func = NULL;
726   PyObject *dataarg = NULL;
727   PyObject *pyargs = NULL;
728   PyObject *retval = NULL;
729
730   assert (PyTuple_Check(pyhook));
731   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
732
733   self = PyTuple_GetItem(pyhook, 0);
734   func = PyTuple_GetItem(pyhook, 2);
735   if (PyTuple_Size(pyhook) == 6) {
736     dataarg = PyTuple_GetItem(pyhook, 5);
737     pyargs = PyTuple_New(2);
738   } else {
739     pyargs = PyTuple_New(1);
740   }
741
742   PyTuple_SetItem(pyargs, 0, PyBytes_FromStringAndSize(buffer, size));
743   if (dataarg) {
744     Py_INCREF(dataarg);
745     PyTuple_SetItem(pyargs, 1, dataarg);
746   }
747
748   retval = PyObject_CallObject(func, pyargs);
749   Py_DECREF(pyargs);
750   if (PyErr_Occurred()) {
751     pygpgme_stash_callback_exception(self);
752     result = -1;
753     goto leave;
754   }
755
756   if (! PyLong_Check(retval)) {
757     PyErr_Format(PyExc_TypeError,
758                  "expected int from read callback, got %s",
759                  retval->ob_type->tp_name);
760     pygpgme_stash_callback_exception(self);
761     result = -1;
762     goto leave;
763   }
764
765   result = PyLong_AsSsize_t(retval);
766
767  leave:
768   Py_XDECREF(retval);
769   return result;
770 }
771
772 /* Set the current position from where the next read or write starts
773    in the data object with the handle HOOK to OFFSET, relativ to
774    WHENCE.  Returns the new offset in bytes from the beginning of the
775    data object.  */
776 static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
777 {
778   off_t result;
779   PyObject *pyhook = (PyObject *) hook;
780   PyObject *self = NULL;
781   PyObject *func = NULL;
782   PyObject *dataarg = NULL;
783   PyObject *pyargs = NULL;
784   PyObject *retval = NULL;
785
786   assert (PyTuple_Check(pyhook));
787   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
788
789   self = PyTuple_GetItem(pyhook, 0);
790   func = PyTuple_GetItem(pyhook, 3);
791   if (PyTuple_Size(pyhook) == 6) {
792     dataarg = PyTuple_GetItem(pyhook, 5);
793     pyargs = PyTuple_New(3);
794   } else {
795     pyargs = PyTuple_New(2);
796   }
797
798 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
799   PyTuple_SetItem(pyargs, 0, PyLong_FromLongLong((long long) offset));
800 #else
801   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) offset));
802 #endif
803   PyTuple_SetItem(pyargs, 1, PyLong_FromLong((long) whence));
804   if (dataarg) {
805     Py_INCREF(dataarg);
806     PyTuple_SetItem(pyargs, 2, dataarg);
807   }
808
809   retval = PyObject_CallObject(func, pyargs);
810   Py_DECREF(pyargs);
811   if (PyErr_Occurred()) {
812     pygpgme_stash_callback_exception(self);
813     result = -1;
814     goto leave;
815   }
816
817   if (! PyLong_Check(retval)) {
818     PyErr_Format(PyExc_TypeError,
819                  "expected int from read callback, got %s",
820                  retval->ob_type->tp_name);
821     pygpgme_stash_callback_exception(self);
822     result = -1;
823     goto leave;
824   }
825
826 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
827   result = PyLong_AsLongLong(retval);
828 #else
829   result = PyLong_AsLong(retval);
830 #endif
831
832  leave:
833   Py_XDECREF(retval);
834   return result;
835 }
836
837 /* Close the data object with the handle HOOK.  */
838 static void pyDataReleaseCb(void *hook)
839 {
840   PyObject *pyhook = (PyObject *) hook;
841   PyObject *self = NULL;
842   PyObject *func = NULL;
843   PyObject *dataarg = NULL;
844   PyObject *pyargs = NULL;
845   PyObject *retval = NULL;
846
847   assert (PyTuple_Check(pyhook));
848   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
849
850   self = PyTuple_GetItem(pyhook, 0);
851   func = PyTuple_GetItem(pyhook, 4);
852   if (PyTuple_Size(pyhook) == 6) {
853     dataarg = PyTuple_GetItem(pyhook, 5);
854     pyargs = PyTuple_New(1);
855   } else {
856     pyargs = PyTuple_New(0);
857   }
858
859   if (dataarg) {
860     Py_INCREF(dataarg);
861     PyTuple_SetItem(pyargs, 0, dataarg);
862   }
863
864   retval = PyObject_CallObject(func, pyargs);
865   Py_XDECREF(retval);
866   Py_DECREF(pyargs);
867   if (PyErr_Occurred())
868     pygpgme_stash_callback_exception(self);
869 }
870
871 PyObject *
872 pygpgme_data_new_from_cbs(PyObject *self,
873                           PyObject *pycbs,
874                           gpgme_data_t *r_data)
875 {
876   static struct gpgme_data_cbs cbs = {
877     pyDataReadCb,
878     pyDataWriteCb,
879     pyDataSeekCb,
880     pyDataReleaseCb,
881   };
882   gpgme_error_t err;
883
884   if (! PyTuple_Check(pycbs))
885     return PyErr_Format(PyExc_TypeError, "pycbs must be a tuple");
886   if (PyTuple_Size(pycbs) != 5 && PyTuple_Size(pycbs) != 6)
887     return PyErr_Format(PyExc_TypeError,
888                         "pycbs must be a tuple of size 5 or 6");
889
890   err = gpgme_data_new_from_cbs(r_data, &cbs, (void *) pycbs);
891   if (err)
892     return pygpgme_raise_exception(err);
893
894   PyObject_SetAttrString(self, "_data_cbs", pycbs);
895
896   Py_INCREF(Py_None);
897   return Py_None;
898 }