python: Fix raising stashed exceptions.
[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   /* We now have references for the extracted items.  */
154   Py_DECREF(excinfo);
155
156   /* Clear the exception information.  It is important to do this
157      before setting the error, because setting the attribute may
158      execute python code, and the runtime system raises a SystemError
159      if an exception is set but values are returned.  */
160   Py_INCREF(Py_None);
161   PyObject_SetAttrString(self, EXCINFO, Py_None);
162
163   /* Restore exception.  */
164   PyErr_Restore(ptype, pvalue, ptraceback);
165   return NULL; /* Raise exception.  */
166
167  leave:
168   Py_INCREF(Py_None);
169   return Py_None;
170 }
171 #undef EXCINFO
172 \f
173 /* Argument conversion.  */
174
175 /* Convert object to a pointer to gpgme type, generic version.  */
176 PyObject *
177 object_to_gpgme_t(PyObject *input, const char *objtype, int argnum)
178 {
179   PyObject *pyname = NULL, *pypointer = NULL;
180   pyname = PyObject_GetAttrString(input, "_ctype");
181   if (pyname && PyUnicode_Check(pyname))
182     {
183       if (strcmp(PyUnicode_AsUTF8(pyname), objtype) != 0)
184         {
185           PyErr_Format(PyExc_TypeError,
186                        "arg %d: Expected value of type %s, but got %s",
187                        argnum, objtype, PyUnicode_AsUTF8(pyname));
188           Py_DECREF(pyname);
189           return NULL;
190         }
191     }
192   else
193     return NULL;
194
195   Py_DECREF(pyname);
196   pypointer = PyObject_GetAttrString(input, "wrapped");
197   if (pypointer == NULL) {
198     PyErr_Format(PyExc_TypeError,
199                  "arg %d: Use of uninitialized Python object %s",
200                  argnum, objtype);
201     return NULL;
202   }
203   return pypointer;
204 }
205
206 /* Convert object to a pointer to gpgme type, version for data
207    objects.  Constructs a wrapper Python on the fly e.g. for file-like
208    objects with a fileno method, returning it in WRAPPER.  This object
209    must be de-referenced when no longer needed.  */
210 PyObject *
211 object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
212                        PyObject **bytesio, Py_buffer *view)
213 {
214   gpgme_error_t err;
215   PyObject *data;
216   PyObject *fd;
217
218   /* See if it is a file-like object with file number.  */
219   fd = PyObject_CallMethod(input, "fileno", NULL);
220   if (fd) {
221     err = gpgme_data_new_from_fd(wrapper, (int) PyLong_AsLong(fd));
222     Py_DECREF(fd);
223     if (err)
224       return pygpgme_raise_exception (err);
225
226     return pygpgme_wrap_gpgme_data_t(*wrapper);
227   }
228   else
229     PyErr_Clear();
230
231   /* No?  Maybe it implements the buffer protocol.  */
232   data = PyObject_CallMethod(input, "getbuffer", NULL);
233   if (data)
234     {
235       /* Save a reference to input, which seems to be a BytesIO
236          object.  */
237       Py_INCREF(input);
238       *bytesio = input;
239     }
240   else
241     {
242       PyErr_Clear();
243
244       /* No, but maybe the user supplied a buffer object?  */
245       data = input;
246     }
247
248   /* Do we have a buffer object?  */
249   if (PyObject_CheckBuffer(data))
250     {
251       if (PyObject_GetBuffer(data, view, PyBUF_SIMPLE) < 0)
252         return NULL;
253
254       if (data != input)
255         Py_DECREF(data);
256
257       assert (view->ndim == 1);
258       assert (view->shape == NULL);
259       assert (view->strides == NULL);
260       assert (view->suboffsets == NULL);
261
262       err = gpgme_data_new_from_mem(wrapper, view->buf, (size_t) view->len, 0);
263       if (err)
264         return pygpgme_raise_exception (err);
265
266       return pygpgme_wrap_gpgme_data_t(*wrapper);
267     }
268
269   /* As last resort we assume it is a wrapped data object.  */
270   if (PyObject_HasAttrString(data, "_ctype"))
271     return object_to_gpgme_t(data, "gpgme_data_t", argnum);
272
273   return PyErr_Format(PyExc_TypeError,
274                       "arg %d: expected pyme.Data, file, or an object "
275                       "implementing the buffer protocol, got %s",
276                       argnum, data->ob_type->tp_name);
277 }
278
279 \f
280
281 PyObject *
282 pygpgme_wrap_fragile_result(PyObject *fragile, const char *classname)
283 {
284   static PyObject *results;
285   PyObject *class;
286   PyObject *replacement;
287
288   if (results == NULL)
289     {
290       PyObject *from_list = PyList_New(0);
291       if (from_list == NULL)
292         return NULL;
293
294       results = PyImport_ImportModuleLevel("results", PyEval_GetGlobals(),
295                                            PyEval_GetLocals(), from_list, 1);
296       Py_DECREF(from_list);
297
298       if (results == NULL)
299         return NULL;
300     }
301
302   class = PyMapping_GetItemString(PyModule_GetDict(results), classname);
303   if (class == NULL)
304     return NULL;
305
306   replacement = PyObject_CallFunctionObjArgs(class, fragile, NULL);
307   Py_DECREF(class);
308   return replacement;
309 }
310
311 \f
312
313 /* Callback support.  */
314 static gpgme_error_t pyPassphraseCb(void *hook,
315                                     const char *uid_hint,
316                                     const char *passphrase_info,
317                                     int prev_was_bad,
318                                     int fd) {
319   PyObject *pyhook = (PyObject *) hook;
320   PyObject *self = NULL;
321   PyObject *func = NULL;
322   PyObject *args = NULL;
323   PyObject *retval = NULL;
324   PyObject *dataarg = NULL;
325   gpgme_error_t err_status = 0;
326
327   pygpgme_exception_init();
328
329   assert (PyTuple_Check(pyhook));
330   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
331   self = PyTuple_GetItem(pyhook, 0);
332   func = PyTuple_GetItem(pyhook, 1);
333   if (PyTuple_Size(pyhook) == 3) {
334     dataarg = PyTuple_GetItem(pyhook, 2);
335     args = PyTuple_New(4);
336   } else {
337     args = PyTuple_New(3);
338   }
339
340   if (uid_hint == NULL)
341     {
342       Py_INCREF(Py_None);
343       PyTuple_SetItem(args, 0, Py_None);
344     }
345   else
346     PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
347                                                   "strict"));
348   if (PyErr_Occurred()) {
349     Py_DECREF(args);
350     err_status = gpg_error(GPG_ERR_GENERAL);
351     goto leave;
352   }
353
354   PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
355   PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
356   if (dataarg) {
357     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
358     PyTuple_SetItem(args, 3, dataarg);
359   }
360
361   retval = PyObject_CallObject(func, args);
362   Py_DECREF(args);
363   if (PyErr_Occurred()) {
364     err_status = pygpgme_exception2code();
365   } else {
366     if (!retval) {
367       if (write(fd, "\n", 1) < 0) {
368         err_status = gpgme_error_from_syserror ();
369         pygpgme_raise_exception (err_status);
370       }
371     } else {
372       char *buf;
373       size_t len;
374       if (PyBytes_Check(retval))
375         buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
376       else if (PyUnicode_Check(retval))
377         {
378           Py_ssize_t ssize;
379           buf = PyUnicode_AsUTF8AndSize(retval, &ssize);
380           assert (! buf || ssize >= 0);
381           len = (size_t) ssize;
382         }
383       else
384         {
385           PyErr_Format(PyExc_TypeError,
386                        "expected str or bytes from passphrase callback, got %s",
387                        retval->ob_type->tp_name);
388           err_status = gpg_error(GPG_ERR_GENERAL);
389           goto leave;
390         }
391
392       if (write(fd, buf, len) < 0) {
393         err_status = gpgme_error_from_syserror ();
394         pygpgme_raise_exception (err_status);
395       }
396       if (! err_status && write(fd, "\n", 1) < 0) {
397         err_status = gpgme_error_from_syserror ();
398         pygpgme_raise_exception (err_status);
399       }
400
401       Py_DECREF(retval);
402     }
403   }
404
405  leave:
406   if (err_status)
407     pygpgme_stash_callback_exception(self);
408
409   return err_status;
410 }
411
412 PyObject *
413 pygpgme_set_passphrase_cb(PyObject *self, PyObject *cb) {
414   PyObject *wrapped;
415   gpgme_ctx_t ctx;
416
417   wrapped = PyObject_GetAttrString(self, "wrapped");
418   if (wrapped == NULL)
419     {
420       assert (PyErr_Occurred ());
421       return NULL;
422     }
423
424   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
425   Py_DECREF(wrapped);
426   if (ctx == NULL)
427     {
428       if (cb == Py_None)
429         goto out;
430       else
431         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
432     }
433
434   if (cb == Py_None) {
435     gpgme_set_passphrase_cb(ctx, NULL, NULL);
436     PyObject_SetAttrString(self, "_passphrase_cb", Py_None);
437     goto out;
438   }
439
440   if (! PyTuple_Check(cb))
441     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
442   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
443     return PyErr_Format(PyExc_TypeError,
444                         "cb must be a tuple of size 2 or 3");
445
446   gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t) pyPassphraseCb,
447                           (void *) cb);
448   PyObject_SetAttrString(self, "_passphrase_cb", cb);
449
450  out:
451   Py_INCREF(Py_None);
452   return Py_None;
453 }
454
455 static void pyProgressCb(void *hook, const char *what, int type, int current,
456                          int total) {
457   PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
458   PyObject *pyhook = (PyObject *) hook;
459   PyObject *self = NULL;
460
461   assert (PyTuple_Check(pyhook));
462   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
463   self = PyTuple_GetItem(pyhook, 0);
464   func = PyTuple_GetItem(pyhook, 1);
465   if (PyTuple_Size(pyhook) == 3) {
466     dataarg = PyTuple_GetItem(pyhook, 2);
467     args = PyTuple_New(5);
468   } else {
469     args = PyTuple_New(4);
470   }
471
472   PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
473                                                 "strict"));
474   if (PyErr_Occurred()) {
475     pygpgme_stash_callback_exception(self);
476     Py_DECREF(args);
477     return;
478   }
479   PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
480   PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
481   PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
482   if (dataarg) {
483     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
484     PyTuple_SetItem(args, 4, dataarg);
485   }
486
487   retval = PyObject_CallObject(func, args);
488   if (PyErr_Occurred())
489     pygpgme_stash_callback_exception(self);
490   Py_DECREF(args);
491   Py_XDECREF(retval);
492 }
493
494 PyObject *
495 pygpgme_set_progress_cb(PyObject *self, PyObject *cb) {
496   PyObject *wrapped;
497   gpgme_ctx_t ctx;
498
499   wrapped = PyObject_GetAttrString(self, "wrapped");
500   if (wrapped == NULL)
501     {
502       assert (PyErr_Occurred ());
503       return NULL;
504     }
505
506   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
507   Py_DECREF(wrapped);
508   if (ctx == NULL)
509     {
510       if (cb == Py_None)
511         goto out;
512       else
513         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
514     }
515
516   if (cb == Py_None) {
517     gpgme_set_progress_cb(ctx, NULL, NULL);
518     PyObject_SetAttrString(self, "_progress_cb", Py_None);
519     goto out;
520   }
521
522   if (! PyTuple_Check(cb))
523     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
524   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
525     return PyErr_Format(PyExc_TypeError,
526                         "cb must be a tuple of size 2 or 3");
527
528   gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
529   PyObject_SetAttrString(self, "_progress_cb", cb);
530
531  out:
532   Py_INCREF(Py_None);
533   return Py_None;
534 }
535 \f
536 /* Status callbacks.  */
537 static gpgme_error_t pyStatusCb(void *hook, const char *keyword,
538                                 const char *args) {
539   gpgme_error_t err = 0;
540   PyObject *pyhook = (PyObject *) hook;
541   PyObject *self = NULL;
542   PyObject *func = NULL;
543   PyObject *dataarg = NULL;
544   PyObject *pyargs = NULL;
545   PyObject *retval = NULL;
546
547   assert (PyTuple_Check(pyhook));
548   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
549   self = PyTuple_GetItem(pyhook, 0);
550   func = PyTuple_GetItem(pyhook, 1);
551   if (PyTuple_Size(pyhook) == 3) {
552     dataarg = PyTuple_GetItem(pyhook, 2);
553     pyargs = PyTuple_New(3);
554   } else {
555     pyargs = PyTuple_New(2);
556   }
557
558   if (keyword)
559     PyTuple_SetItem(pyargs, 0, PyUnicode_DecodeUTF8(keyword, strlen (keyword),
560                                                     "strict"));
561   else
562     {
563       Py_INCREF(Py_None);
564       PyTuple_SetItem(pyargs, 0, Py_None);
565     }
566   PyTuple_SetItem(pyargs, 1, PyUnicode_DecodeUTF8(args, strlen (args),
567                                                 "strict"));
568   if (PyErr_Occurred()) {
569     err = gpg_error(GPG_ERR_GENERAL);
570     Py_DECREF(pyargs);
571     goto leave;
572   }
573
574   if (dataarg) {
575     Py_INCREF(dataarg);
576     PyTuple_SetItem(pyargs, 2, dataarg);
577   }
578
579   retval = PyObject_CallObject(func, pyargs);
580   if (PyErr_Occurred())
581     err = pygpgme_exception2code();
582   Py_DECREF(pyargs);
583   Py_XDECREF(retval);
584
585  leave:
586   if (err)
587     pygpgme_stash_callback_exception(self);
588   return err;
589 }
590
591 PyObject *
592 pygpgme_set_status_cb(PyObject *self, PyObject *cb) {
593   PyObject *wrapped;
594   gpgme_ctx_t ctx;
595
596   wrapped = PyObject_GetAttrString(self, "wrapped");
597   if (wrapped == NULL)
598     {
599       assert (PyErr_Occurred ());
600       return NULL;
601     }
602
603   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
604   Py_DECREF(wrapped);
605   if (ctx == NULL)
606     {
607       if (cb == Py_None)
608         goto out;
609       else
610         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
611     }
612
613   if (cb == Py_None) {
614     gpgme_set_status_cb(ctx, NULL, NULL);
615     PyObject_SetAttrString(self, "_status_cb", Py_None);
616     goto out;
617   }
618
619   if (! PyTuple_Check(cb))
620     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
621   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
622     return PyErr_Format(PyExc_TypeError,
623                         "cb must be a tuple of size 2 or 3");
624
625   gpgme_set_status_cb(ctx, (gpgme_status_cb_t) pyStatusCb, (void *) cb);
626   PyObject_SetAttrString(self, "_status_cb", cb);
627
628  out:
629   Py_INCREF(Py_None);
630   return Py_None;
631 }
632 \f
633 /* Edit callbacks.  */
634 gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
635                        const char *args, int fd) {
636   PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
637   PyObject *pyopaque = (PyObject *) opaque;
638   gpgme_error_t err_status = 0;
639   PyObject *self = NULL;
640
641   pygpgme_exception_init();
642
643   assert (PyTuple_Check(pyopaque));
644   assert (PyTuple_Size(pyopaque) == 2 || PyTuple_Size(pyopaque) == 3);
645   self = PyTuple_GetItem(pyopaque, 0);
646   func = PyTuple_GetItem(pyopaque, 1);
647   if (PyTuple_Size(pyopaque) == 3) {
648     dataarg = PyTuple_GetItem(pyopaque, 2);
649     pyargs = PyTuple_New(3);
650   } else {
651     pyargs = PyTuple_New(2);
652   }
653
654   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
655   PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
656   if (dataarg) {
657     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
658     PyTuple_SetItem(pyargs, 2, dataarg);
659   }
660
661   retval = PyObject_CallObject(func, pyargs);
662   Py_DECREF(pyargs);
663   if (PyErr_Occurred()) {
664     err_status = pygpgme_exception2code();
665   } else {
666     if (fd>=0 && retval && PyUnicode_Check(retval)) {
667       const char *buffer;
668       Py_ssize_t size;
669
670       buffer = PyUnicode_AsUTF8AndSize(retval, &size);
671       if (write(fd, buffer, size) < 0) {
672         err_status = gpgme_error_from_syserror ();
673         pygpgme_raise_exception (err_status);
674       }
675       if (! err_status && write(fd, "\n", 1) < 0) {
676         err_status = gpgme_error_from_syserror ();
677         pygpgme_raise_exception (err_status);
678       }
679     }
680   }
681   if (err_status)
682     pygpgme_stash_callback_exception(self);
683
684   Py_XDECREF(retval);
685   return err_status;
686 }
687 \f
688 /* Data callbacks.  */
689
690 /* Read up to SIZE bytes into buffer BUFFER from the data object with
691    the handle HOOK.  Return the number of characters read, 0 on EOF
692    and -1 on error.  If an error occurs, errno is set.  */
693 static ssize_t pyDataReadCb(void *hook, void *buffer, size_t size)
694 {
695   ssize_t result;
696   PyObject *pyhook = (PyObject *) hook;
697   PyObject *self = NULL;
698   PyObject *func = NULL;
699   PyObject *dataarg = NULL;
700   PyObject *pyargs = NULL;
701   PyObject *retval = NULL;
702
703   assert (PyTuple_Check(pyhook));
704   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
705
706   self = PyTuple_GetItem(pyhook, 0);
707   func = PyTuple_GetItem(pyhook, 1);
708   if (PyTuple_Size(pyhook) == 6) {
709     dataarg = PyTuple_GetItem(pyhook, 5);
710     pyargs = PyTuple_New(2);
711   } else {
712     pyargs = PyTuple_New(1);
713   }
714
715   PyTuple_SetItem(pyargs, 0, PyLong_FromSize_t(size));
716   if (dataarg) {
717     Py_INCREF(dataarg);
718     PyTuple_SetItem(pyargs, 1, dataarg);
719   }
720
721   retval = PyObject_CallObject(func, pyargs);
722   Py_DECREF(pyargs);
723   if (PyErr_Occurred()) {
724     pygpgme_stash_callback_exception(self);
725     result = -1;
726     goto leave;
727   }
728
729   if (! PyBytes_Check(retval)) {
730     PyErr_Format(PyExc_TypeError,
731                  "expected bytes from read callback, got %s",
732                  retval->ob_type->tp_name);
733     pygpgme_stash_callback_exception(self);
734     result = -1;
735     goto leave;
736   }
737
738   if (PyBytes_Size(retval) > size) {
739     PyErr_Format(PyExc_TypeError,
740                  "expected %zu bytes from read callback, got %zu",
741                  size, PyBytes_Size(retval));
742     pygpgme_stash_callback_exception(self);
743     result = -1;
744     goto leave;
745   }
746
747   memcpy(buffer, PyBytes_AsString(retval), PyBytes_Size(retval));
748   result = PyBytes_Size(retval);
749
750  leave:
751   Py_XDECREF(retval);
752   return result;
753 }
754
755 /* Write up to SIZE bytes from buffer BUFFER to the data object with
756    the handle HOOK.  Return the number of characters written, or -1
757    on error.  If an error occurs, errno is set.  */
758 static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
759 {
760   ssize_t result;
761   PyObject *pyhook = (PyObject *) hook;
762   PyObject *self = NULL;
763   PyObject *func = NULL;
764   PyObject *dataarg = NULL;
765   PyObject *pyargs = NULL;
766   PyObject *retval = NULL;
767
768   assert (PyTuple_Check(pyhook));
769   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
770
771   self = PyTuple_GetItem(pyhook, 0);
772   func = PyTuple_GetItem(pyhook, 2);
773   if (PyTuple_Size(pyhook) == 6) {
774     dataarg = PyTuple_GetItem(pyhook, 5);
775     pyargs = PyTuple_New(2);
776   } else {
777     pyargs = PyTuple_New(1);
778   }
779
780   PyTuple_SetItem(pyargs, 0, PyBytes_FromStringAndSize(buffer, size));
781   if (dataarg) {
782     Py_INCREF(dataarg);
783     PyTuple_SetItem(pyargs, 1, dataarg);
784   }
785
786   retval = PyObject_CallObject(func, pyargs);
787   Py_DECREF(pyargs);
788   if (PyErr_Occurred()) {
789     pygpgme_stash_callback_exception(self);
790     result = -1;
791     goto leave;
792   }
793
794   if (! PyLong_Check(retval)) {
795     PyErr_Format(PyExc_TypeError,
796                  "expected int from read callback, got %s",
797                  retval->ob_type->tp_name);
798     pygpgme_stash_callback_exception(self);
799     result = -1;
800     goto leave;
801   }
802
803   result = PyLong_AsSsize_t(retval);
804
805  leave:
806   Py_XDECREF(retval);
807   return result;
808 }
809
810 /* Set the current position from where the next read or write starts
811    in the data object with the handle HOOK to OFFSET, relativ to
812    WHENCE.  Returns the new offset in bytes from the beginning of the
813    data object.  */
814 static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
815 {
816   off_t result;
817   PyObject *pyhook = (PyObject *) hook;
818   PyObject *self = NULL;
819   PyObject *func = NULL;
820   PyObject *dataarg = NULL;
821   PyObject *pyargs = NULL;
822   PyObject *retval = NULL;
823
824   assert (PyTuple_Check(pyhook));
825   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
826
827   self = PyTuple_GetItem(pyhook, 0);
828   func = PyTuple_GetItem(pyhook, 3);
829   if (PyTuple_Size(pyhook) == 6) {
830     dataarg = PyTuple_GetItem(pyhook, 5);
831     pyargs = PyTuple_New(3);
832   } else {
833     pyargs = PyTuple_New(2);
834   }
835
836 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
837   PyTuple_SetItem(pyargs, 0, PyLong_FromLongLong((long long) offset));
838 #else
839   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) offset));
840 #endif
841   PyTuple_SetItem(pyargs, 1, PyLong_FromLong((long) whence));
842   if (dataarg) {
843     Py_INCREF(dataarg);
844     PyTuple_SetItem(pyargs, 2, dataarg);
845   }
846
847   retval = PyObject_CallObject(func, pyargs);
848   Py_DECREF(pyargs);
849   if (PyErr_Occurred()) {
850     pygpgme_stash_callback_exception(self);
851     result = -1;
852     goto leave;
853   }
854
855   if (! PyLong_Check(retval)) {
856     PyErr_Format(PyExc_TypeError,
857                  "expected int from read callback, got %s",
858                  retval->ob_type->tp_name);
859     pygpgme_stash_callback_exception(self);
860     result = -1;
861     goto leave;
862   }
863
864 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
865   result = PyLong_AsLongLong(retval);
866 #else
867   result = PyLong_AsLong(retval);
868 #endif
869
870  leave:
871   Py_XDECREF(retval);
872   return result;
873 }
874
875 /* Close the data object with the handle HOOK.  */
876 static void pyDataReleaseCb(void *hook)
877 {
878   PyObject *pyhook = (PyObject *) hook;
879   PyObject *self = NULL;
880   PyObject *func = NULL;
881   PyObject *dataarg = NULL;
882   PyObject *pyargs = NULL;
883   PyObject *retval = NULL;
884
885   assert (PyTuple_Check(pyhook));
886   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
887
888   self = PyTuple_GetItem(pyhook, 0);
889   func = PyTuple_GetItem(pyhook, 4);
890   if (PyTuple_Size(pyhook) == 6) {
891     dataarg = PyTuple_GetItem(pyhook, 5);
892     pyargs = PyTuple_New(1);
893   } else {
894     pyargs = PyTuple_New(0);
895   }
896
897   if (dataarg) {
898     Py_INCREF(dataarg);
899     PyTuple_SetItem(pyargs, 0, dataarg);
900   }
901
902   retval = PyObject_CallObject(func, pyargs);
903   Py_XDECREF(retval);
904   Py_DECREF(pyargs);
905   if (PyErr_Occurred())
906     pygpgme_stash_callback_exception(self);
907 }
908
909 PyObject *
910 pygpgme_data_new_from_cbs(PyObject *self,
911                           PyObject *pycbs,
912                           gpgme_data_t *r_data)
913 {
914   static struct gpgme_data_cbs cbs = {
915     pyDataReadCb,
916     pyDataWriteCb,
917     pyDataSeekCb,
918     pyDataReleaseCb,
919   };
920   gpgme_error_t err;
921
922   if (! PyTuple_Check(pycbs))
923     return PyErr_Format(PyExc_TypeError, "pycbs must be a tuple");
924   if (PyTuple_Size(pycbs) != 5 && PyTuple_Size(pycbs) != 6)
925     return PyErr_Format(PyExc_TypeError,
926                         "pycbs must be a tuple of size 5 or 6");
927
928   err = gpgme_data_new_from_cbs(r_data, &cbs, (void *) pycbs);
929   if (err)
930     return pygpgme_raise_exception(err);
931
932   PyObject_SetAttrString(self, "_data_cbs", pycbs);
933
934   Py_INCREF(Py_None);
935   return Py_None;
936 }