python: Fix exception leak.
[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 void pygpgme_clear_generic_cb(PyObject **cb) {
80   Py_DECREF(*cb);
81 }
82
83 /* Exception support for callbacks.  */
84 #define EXCINFO "_callback_excinfo"
85
86 static void pygpgme_stash_callback_exception(PyObject *weak_self)
87 {
88   PyObject *self, *ptype, *pvalue, *ptraceback, *excinfo;
89
90   PyErr_Fetch(&ptype, &pvalue, &ptraceback);
91   excinfo = PyTuple_New(3);
92   PyTuple_SetItem(excinfo, 0, ptype);
93
94   if (pvalue)
95     PyTuple_SetItem(excinfo, 1, pvalue);
96   else {
97     Py_INCREF(Py_None);
98     PyTuple_SetItem(excinfo, 1, Py_None);
99   }
100
101   if (ptraceback)
102     PyTuple_SetItem(excinfo, 2, ptraceback);
103   else {
104     Py_INCREF(Py_None);
105     PyTuple_SetItem(excinfo, 2, Py_None);
106   }
107
108   self = PyWeakref_GetObject(weak_self);
109   /* self only has a borrowed reference.  */
110   if (self == Py_None) {
111     /* This should not happen, as even if we're called from the data
112        release callback triggered from the wrappers destructor, the
113        object is still alive and hence the weak reference still refers
114        to the object.  However, in case this ever changes, not seeing
115        any exceptions is worse than having a little extra code, so
116        here we go.  */
117       fprintf(stderr,
118               "Error occurred in callback, but the wrapper object "
119               "has been deallocated.\n");
120       PyErr_Restore(ptype, pvalue, ptraceback);
121       PyErr_Print();
122     }
123   else
124     PyObject_SetAttrString(self, EXCINFO, excinfo);
125   Py_DECREF(excinfo);
126 }
127
128 PyObject *pygpgme_raise_callback_exception(PyObject *self)
129 {
130   PyObject *ptype, *pvalue, *ptraceback, *excinfo;
131
132   if (! PyObject_HasAttrString(self, EXCINFO))
133     goto leave;
134
135   excinfo = PyObject_GetAttrString(self, EXCINFO);
136   if (! PyTuple_Check(excinfo))
137     {
138       Py_DECREF(excinfo);
139       goto leave;
140     }
141
142   ptype = PyTuple_GetItem(excinfo, 0);
143   Py_INCREF(excinfo);
144
145   pvalue = PyTuple_GetItem(excinfo, 1);
146   if (pvalue == Py_None)
147     pvalue = NULL;
148   else
149     Py_INCREF(pvalue);
150
151   ptraceback = PyTuple_GetItem(excinfo, 2);
152   if (ptraceback == Py_None)
153     ptraceback = NULL;
154   else
155     Py_INCREF(ptraceback);
156
157   Py_DECREF(excinfo);
158   PyErr_Restore(ptype, pvalue, ptraceback);
159
160   Py_INCREF(Py_None);
161   PyObject_SetAttrString(self, EXCINFO, Py_None);
162
163   return NULL; /* Raise exception.  */
164
165  leave:
166   Py_INCREF(Py_None);
167   return Py_None;
168 }
169 #undef EXCINFO
170 \f
171 /* Argument conversion.  */
172
173 /* Convert object to a pointer to gpgme type, generic version.  */
174 PyObject *
175 object_to_gpgme_t(PyObject *input, const char *objtype, int argnum)
176 {
177   PyObject *pyname = NULL, *pypointer = NULL;
178   pyname = PyObject_CallMethod(input, "_getctype", NULL);
179   if (pyname && PyUnicode_Check(pyname))
180     {
181       if (strcmp(PyUnicode_AsUTF8(pyname), objtype) != 0)
182         {
183           PyErr_Format(PyExc_TypeError,
184                        "arg %d: Expected value of type %s, but got %s",
185                        argnum, objtype, PyUnicode_AsUTF8(pyname));
186           Py_DECREF(pyname);
187           return NULL;
188         }
189     }
190   else
191     return NULL;
192
193   Py_DECREF(pyname);
194   pypointer = PyObject_GetAttrString(input, "wrapped");
195   if (pypointer == NULL) {
196     PyErr_Format(PyExc_TypeError,
197                  "arg %d: Use of uninitialized Python object %s",
198                  argnum, objtype);
199     return NULL;
200   }
201   return pypointer;
202 }
203
204 /* Convert object to a pointer to gpgme type, version for data
205    objects.  Constructs a wrapper Python on the fly e.g. for file-like
206    objects with a fileno method, returning it in WRAPPER.  This object
207    must be de-referenced when no longer needed.  */
208 PyObject *
209 object_to_gpgme_data_t(PyObject *input, int argnum, PyObject **wrapper)
210 {
211   static PyObject *Data = NULL;
212   PyObject *data = input;
213   PyObject *fd;
214   PyObject *result;
215   *wrapper = NULL;
216
217   if (Data == NULL) {
218     PyObject *core;
219     PyObject *from_list = PyList_New(0);
220     core = PyImport_ImportModuleLevel("core", PyEval_GetGlobals(),
221                                       PyEval_GetLocals(), from_list, 1);
222     Py_XDECREF(from_list);
223     if (core) {
224       Data = PyDict_GetItemString(PyModule_GetDict(core), "Data");
225       Py_XINCREF(Data);
226     }
227     else
228       return NULL;
229   }
230
231   fd = PyObject_CallMethod(input, "fileno", NULL);
232   if (fd) {
233     /* File-like object with file number.  */
234     PyObject *args = NULL;
235     PyObject *kw = NULL;
236
237     /* We don't need the fd, as we have no constructor accepting file
238        descriptors directly.  */
239     Py_DECREF(fd);
240
241     args = PyTuple_New(0);
242     kw = PyDict_New();
243     if (args == NULL || kw == NULL)
244       {
245       fail:
246         Py_XDECREF(args);
247         Py_XDECREF(kw);
248         return NULL;
249       }
250
251     if (PyDict_SetItemString(kw, "file", input) < 0)
252       goto fail;
253
254     *wrapper = PyObject_Call(Data, args, kw);
255     if (*wrapper == NULL)
256       goto fail;
257
258     Py_DECREF(args);
259     Py_DECREF(kw);
260     data = *wrapper;
261   }
262   else
263     PyErr_Clear();
264
265   result = object_to_gpgme_t(data, "gpgme_data_t", argnum);
266   return result;
267 }
268
269 \f
270
271 /* Callback support.  */
272 static gpgme_error_t pyPassphraseCb(void *hook,
273                                     const char *uid_hint,
274                                     const char *passphrase_info,
275                                     int prev_was_bad,
276                                     int fd) {
277   PyObject *pyhook = (PyObject *) hook;
278   PyObject *self = NULL;
279   PyObject *func = NULL;
280   PyObject *args = NULL;
281   PyObject *retval = NULL;
282   PyObject *dataarg = NULL;
283   gpgme_error_t err_status = 0;
284
285   pygpgme_exception_init();
286
287   assert (PyTuple_Check(pyhook));
288   self = PyTuple_GetItem(pyhook, 0);
289   func = PyTuple_GetItem(pyhook, 1);
290   if (PyTuple_Size(pyhook) == 3) {
291     dataarg = PyTuple_GetItem(pyhook, 2);
292     args = PyTuple_New(4);
293   } else {
294     args = PyTuple_New(3);
295   }
296
297   if (uid_hint == NULL)
298     {
299       Py_INCREF(Py_None);
300       PyTuple_SetItem(args, 0, Py_None);
301     }
302   else
303     PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
304                                                   "strict"));
305   if (PyErr_Occurred()) {
306     Py_DECREF(args);
307     err_status = gpg_error(GPG_ERR_GENERAL);
308     goto leave;
309   }
310
311   PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
312   PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
313   if (dataarg) {
314     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
315     PyTuple_SetItem(args, 3, dataarg);
316   }
317
318   retval = PyObject_CallObject(func, args);
319   Py_DECREF(args);
320   if (PyErr_Occurred()) {
321     err_status = pygpgme_exception2code();
322   } else {
323     if (!retval) {
324       if (write(fd, "\n", 1) < 0) {
325         err_status = gpgme_error_from_syserror ();
326         pygpgme_raise_exception (err_status);
327       }
328     } else {
329       char *buf;
330       size_t len;
331       if (PyBytes_Check(retval))
332         buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
333       else if (PyUnicode_Check(retval))
334         {
335           Py_ssize_t ssize;
336           buf = PyUnicode_AsUTF8AndSize(retval, &ssize);
337           assert (! buf || ssize >= 0);
338           len = (size_t) ssize;
339         }
340       else
341         {
342           PyErr_Format(PyExc_TypeError,
343                        "expected str or bytes from passphrase callback, got %s",
344                        retval->ob_type->tp_name);
345           err_status = gpg_error(GPG_ERR_GENERAL);
346           goto leave;
347         }
348
349       if (write(fd, buf, len) < 0) {
350         err_status = gpgme_error_from_syserror ();
351         pygpgme_raise_exception (err_status);
352       }
353       if (! err_status && write(fd, "\n", 1) < 0) {
354         err_status = gpgme_error_from_syserror ();
355         pygpgme_raise_exception (err_status);
356       }
357
358       Py_DECREF(retval);
359     }
360   }
361
362  leave:
363   if (err_status)
364     pygpgme_stash_callback_exception(self);
365
366   return err_status;
367 }
368
369 void pygpgme_set_passphrase_cb(gpgme_ctx_t ctx, PyObject *cb,
370                                PyObject **freelater) {
371   if (cb == Py_None) {
372     gpgme_set_passphrase_cb(ctx, NULL, NULL);
373     return;
374   }
375   Py_INCREF(cb);
376   *freelater = cb;
377   gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t)pyPassphraseCb, (void *) cb);
378 }
379
380 static void pyProgressCb(void *hook, const char *what, int type, int current,
381                          int total) {
382   PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
383   PyObject *pyhook = (PyObject *) hook;
384   PyObject *self = NULL;
385
386   assert (PyTuple_Check(pyhook));
387   self = PyTuple_GetItem(pyhook, 0);
388   func = PyTuple_GetItem(pyhook, 1);
389   if (PyTuple_Size(pyhook) == 3) {
390     dataarg = PyTuple_GetItem(pyhook, 2);
391     args = PyTuple_New(5);
392   } else {
393     args = PyTuple_New(4);
394   }
395
396   PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
397                                                 "strict"));
398   if (PyErr_Occurred()) {
399     pygpgme_stash_callback_exception(self);
400     Py_DECREF(args);
401     return;
402   }
403   PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
404   PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
405   PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
406   if (dataarg) {
407     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
408     PyTuple_SetItem(args, 4, dataarg);
409   }
410
411   retval = PyObject_CallObject(func, args);
412   if (PyErr_Occurred())
413     pygpgme_stash_callback_exception(self);
414   Py_DECREF(args);
415   Py_XDECREF(retval);
416 }
417
418 void pygpgme_set_progress_cb(gpgme_ctx_t ctx, PyObject *cb, PyObject **freelater){
419   if (cb == Py_None) {
420     gpgme_set_progress_cb(ctx, NULL, NULL);
421     return;
422   }
423   Py_INCREF(cb);
424   *freelater = cb;
425   gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
426 }
427 \f
428 /* Status callbacks.  */
429 static gpgme_error_t pyStatusCb(void *hook, const char *keyword,
430                                 const char *args) {
431   gpgme_error_t err = 0;
432   PyObject *pyhook = (PyObject *) hook;
433   PyObject *self = NULL;
434   PyObject *func = NULL;
435   PyObject *dataarg = NULL;
436   PyObject *pyargs = NULL;
437   PyObject *retval = NULL;
438
439   assert (PyTuple_Check(pyhook));
440   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
441   self = PyTuple_GetItem(pyhook, 0);
442   func = PyTuple_GetItem(pyhook, 1);
443   if (PyTuple_Size(pyhook) == 3) {
444     dataarg = PyTuple_GetItem(pyhook, 2);
445     pyargs = PyTuple_New(3);
446   } else {
447     pyargs = PyTuple_New(2);
448   }
449
450   if (keyword)
451     PyTuple_SetItem(pyargs, 0, PyUnicode_DecodeUTF8(keyword, strlen (keyword),
452                                                     "strict"));
453   else
454     {
455       Py_INCREF(Py_None);
456       PyTuple_SetItem(pyargs, 0, Py_None);
457     }
458   PyTuple_SetItem(pyargs, 1, PyUnicode_DecodeUTF8(args, strlen (args),
459                                                 "strict"));
460   if (PyErr_Occurred()) {
461     err = gpg_error(GPG_ERR_GENERAL);
462     Py_DECREF(pyargs);
463     goto leave;
464   }
465
466   if (dataarg) {
467     Py_INCREF(dataarg);
468     PyTuple_SetItem(pyargs, 2, dataarg);
469   }
470
471   retval = PyObject_CallObject(func, pyargs);
472   if (PyErr_Occurred())
473     err = pygpgme_exception2code();
474   Py_DECREF(pyargs);
475   Py_XDECREF(retval);
476
477  leave:
478   if (err)
479     pygpgme_stash_callback_exception(self);
480   return err;
481 }
482
483 void pygpgme_set_status_cb(gpgme_ctx_t ctx, PyObject *cb,
484                            PyObject **freelater) {
485   if (cb == Py_None) {
486     gpgme_set_status_cb(ctx, NULL, NULL);
487     return;
488   }
489   Py_INCREF(cb);
490   *freelater = cb;
491   gpgme_set_status_cb(ctx, (gpgme_status_cb_t) pyStatusCb, (void *) cb);
492 }
493 \f
494 /* Edit callbacks.  */
495 gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
496                        const char *args, int fd) {
497   PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
498   PyObject *pyopaque = (PyObject *) opaque;
499   gpgme_error_t err_status = 0;
500   PyObject *self = NULL;
501
502   pygpgme_exception_init();
503
504   assert (PyTuple_Check(pyopaque));
505   assert (PyTuple_Size(pyopaque) == 2 || PyTuple_Size(pyopaque) == 3);
506   self = PyTuple_GetItem(pyopaque, 0);
507   func = PyTuple_GetItem(pyopaque, 1);
508   if (PyTuple_Size(pyopaque) == 3) {
509     dataarg = PyTuple_GetItem(pyopaque, 2);
510     pyargs = PyTuple_New(3);
511   } else {
512     pyargs = PyTuple_New(2);
513   }
514
515   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
516   PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
517   if (dataarg) {
518     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
519     PyTuple_SetItem(pyargs, 2, dataarg);
520   }
521
522   retval = PyObject_CallObject(func, pyargs);
523   Py_DECREF(pyargs);
524   if (PyErr_Occurred()) {
525     err_status = pygpgme_exception2code();
526   } else {
527     if (fd>=0 && retval && PyUnicode_Check(retval)) {
528       const char *buffer;
529       Py_ssize_t size;
530
531       buffer = PyUnicode_AsUTF8AndSize(retval, &size);
532       if (write(fd, buffer, size) < 0) {
533         err_status = gpgme_error_from_syserror ();
534         pygpgme_raise_exception (err_status);
535       }
536       if (! err_status && write(fd, "\n", 1) < 0) {
537         err_status = gpgme_error_from_syserror ();
538         pygpgme_raise_exception (err_status);
539       }
540     }
541   }
542   if (err_status)
543     pygpgme_stash_callback_exception(self);
544
545   Py_XDECREF(retval);
546   return err_status;
547 }
548 \f
549 /* Data callbacks.  */
550
551 /* Read up to SIZE bytes into buffer BUFFER from the data object with
552    the handle HOOK.  Return the number of characters read, 0 on EOF
553    and -1 on error.  If an error occurs, errno is set.  */
554 static ssize_t pyDataReadCb(void *hook, void *buffer, size_t size)
555 {
556   ssize_t result;
557   PyObject *pyhook = (PyObject *) hook;
558   PyObject *self = NULL;
559   PyObject *func = NULL;
560   PyObject *dataarg = NULL;
561   PyObject *pyargs = NULL;
562   PyObject *retval = NULL;
563
564   assert (PyTuple_Check(pyhook));
565   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
566
567   self = PyTuple_GetItem(pyhook, 0);
568   func = PyTuple_GetItem(pyhook, 1);
569   if (PyTuple_Size(pyhook) == 6) {
570     dataarg = PyTuple_GetItem(pyhook, 5);
571     pyargs = PyTuple_New(2);
572   } else {
573     pyargs = PyTuple_New(1);
574   }
575
576   PyTuple_SetItem(pyargs, 0, PyLong_FromSize_t(size));
577   if (dataarg) {
578     Py_INCREF(dataarg);
579     PyTuple_SetItem(pyargs, 1, dataarg);
580   }
581
582   retval = PyObject_CallObject(func, pyargs);
583   Py_DECREF(pyargs);
584   if (PyErr_Occurred()) {
585     pygpgme_stash_callback_exception(self);
586     result = -1;
587     goto leave;
588   }
589
590   if (! PyBytes_Check(retval)) {
591     PyErr_Format(PyExc_TypeError,
592                  "expected bytes from read callback, got %s",
593                  retval->ob_type->tp_name);
594     pygpgme_stash_callback_exception(self);
595     result = -1;
596     goto leave;
597   }
598
599   if (PyBytes_Size(retval) > size) {
600     PyErr_Format(PyExc_TypeError,
601                  "expected %zu bytes from read callback, got %zu",
602                  size, PyBytes_Size(retval));
603     pygpgme_stash_callback_exception(self);
604     result = -1;
605     goto leave;
606   }
607
608   memcpy(buffer, PyBytes_AsString(retval), PyBytes_Size(retval));
609   result = PyBytes_Size(retval);
610
611  leave:
612   Py_XDECREF(retval);
613   return result;
614 }
615
616 /* Write up to SIZE bytes from buffer BUFFER to the data object with
617    the handle HOOK.  Return the number of characters written, or -1
618    on error.  If an error occurs, errno is set.  */
619 static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
620 {
621   ssize_t result;
622   PyObject *pyhook = (PyObject *) hook;
623   PyObject *self = NULL;
624   PyObject *func = NULL;
625   PyObject *dataarg = NULL;
626   PyObject *pyargs = NULL;
627   PyObject *retval = NULL;
628
629   assert (PyTuple_Check(pyhook));
630   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
631
632   self = PyTuple_GetItem(pyhook, 0);
633   func = PyTuple_GetItem(pyhook, 2);
634   if (PyTuple_Size(pyhook) == 6) {
635     dataarg = PyTuple_GetItem(pyhook, 5);
636     pyargs = PyTuple_New(2);
637   } else {
638     pyargs = PyTuple_New(1);
639   }
640
641   PyTuple_SetItem(pyargs, 0, PyBytes_FromStringAndSize(buffer, size));
642   if (dataarg) {
643     Py_INCREF(dataarg);
644     PyTuple_SetItem(pyargs, 1, dataarg);
645   }
646
647   retval = PyObject_CallObject(func, pyargs);
648   Py_DECREF(pyargs);
649   if (PyErr_Occurred()) {
650     pygpgme_stash_callback_exception(self);
651     result = -1;
652     goto leave;
653   }
654
655   if (! PyLong_Check(retval)) {
656     PyErr_Format(PyExc_TypeError,
657                  "expected int from read callback, got %s",
658                  retval->ob_type->tp_name);
659     pygpgme_stash_callback_exception(self);
660     result = -1;
661     goto leave;
662   }
663
664   result = PyLong_AsSsize_t(retval);
665
666  leave:
667   Py_XDECREF(retval);
668   return result;
669 }
670
671 /* Set the current position from where the next read or write starts
672    in the data object with the handle HOOK to OFFSET, relativ to
673    WHENCE.  Returns the new offset in bytes from the beginning of the
674    data object.  */
675 static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
676 {
677   off_t result;
678   PyObject *pyhook = (PyObject *) hook;
679   PyObject *self = NULL;
680   PyObject *func = NULL;
681   PyObject *dataarg = NULL;
682   PyObject *pyargs = NULL;
683   PyObject *retval = NULL;
684
685   assert (PyTuple_Check(pyhook));
686   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
687
688   self = PyTuple_GetItem(pyhook, 0);
689   func = PyTuple_GetItem(pyhook, 3);
690   if (PyTuple_Size(pyhook) == 6) {
691     dataarg = PyTuple_GetItem(pyhook, 5);
692     pyargs = PyTuple_New(3);
693   } else {
694     pyargs = PyTuple_New(2);
695   }
696
697 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
698   PyTuple_SetItem(pyargs, 0, PyLong_FromLongLong((long long) offset));
699 #else
700   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) offset));
701 #endif
702   PyTuple_SetItem(pyargs, 1, PyLong_FromLong((long) whence));
703   if (dataarg) {
704     Py_INCREF(dataarg);
705     PyTuple_SetItem(pyargs, 2, dataarg);
706   }
707
708   retval = PyObject_CallObject(func, pyargs);
709   Py_DECREF(pyargs);
710   if (PyErr_Occurred()) {
711     pygpgme_stash_callback_exception(self);
712     result = -1;
713     goto leave;
714   }
715
716   if (! PyLong_Check(retval)) {
717     PyErr_Format(PyExc_TypeError,
718                  "expected int from read callback, got %s",
719                  retval->ob_type->tp_name);
720     pygpgme_stash_callback_exception(self);
721     result = -1;
722     goto leave;
723   }
724
725 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
726   result = PyLong_AsLongLong(retval);
727 #else
728   result = PyLong_AsLong(retval);
729 #endif
730
731  leave:
732   Py_XDECREF(retval);
733   return result;
734 }
735
736 /* Close the data object with the handle HOOK.  */
737 static void pyDataReleaseCb(void *hook)
738 {
739   PyObject *pyhook = (PyObject *) hook;
740   PyObject *self = NULL;
741   PyObject *func = NULL;
742   PyObject *dataarg = NULL;
743   PyObject *pyargs = NULL;
744   PyObject *retval = NULL;
745
746   assert (PyTuple_Check(pyhook));
747   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
748
749   self = PyTuple_GetItem(pyhook, 0);
750   func = PyTuple_GetItem(pyhook, 4);
751   if (PyTuple_Size(pyhook) == 6) {
752     dataarg = PyTuple_GetItem(pyhook, 5);
753     pyargs = PyTuple_New(1);
754   } else {
755     pyargs = PyTuple_New(0);
756   }
757
758   if (dataarg) {
759     Py_INCREF(dataarg);
760     PyTuple_SetItem(pyargs, 0, dataarg);
761   }
762
763   retval = PyObject_CallObject(func, pyargs);
764   Py_XDECREF(retval);
765   Py_DECREF(pyargs);
766   if (PyErr_Occurred())
767     pygpgme_stash_callback_exception(self);
768 }
769
770 gpgme_error_t pygpgme_data_new_from_cbs(gpgme_data_t *r_data,
771                                         PyObject *pycbs,
772                                         PyObject **freelater)
773 {
774   static struct gpgme_data_cbs cbs = {
775     pyDataReadCb,
776     pyDataWriteCb,
777     pyDataSeekCb,
778     pyDataReleaseCb,
779   };
780
781   assert (PyTuple_Check(pycbs));
782   assert (PyTuple_Size(pycbs) == 5 || PyTuple_Size(pycbs) == 6);
783
784   Py_INCREF(pycbs);
785   *freelater = pycbs;
786
787   return gpgme_data_new_from_cbs(r_data, &cbs, (void *) pycbs);
788 }