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