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