python: Support the Assuan engine.
[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
28 #include "helpers.h"
29 #include "private.h"
30
31 static PyObject *GPGMEError = NULL;
32
33 void pygpgme_exception_init(void) {
34   if (GPGMEError == NULL) {
35     PyObject *errors;
36     PyObject *from_list = PyList_New(0);
37     errors = PyImport_ImportModuleLevel("errors", PyEval_GetGlobals(),
38                                         PyEval_GetLocals(), from_list, 1);
39     Py_XDECREF(from_list);
40     if (errors) {
41       GPGMEError=PyDict_GetItemString(PyModule_GetDict(errors), "GPGMEError");
42       Py_XINCREF(GPGMEError);
43     }
44   }
45 }
46
47 static PyObject *
48 pygpgme_raise_exception(gpgme_error_t err)
49 {
50   PyObject *e;
51
52   pygpgme_exception_init();
53   if (GPGMEError == NULL)
54     return PyErr_Format(PyExc_RuntimeError, "Got gpgme_error_t %d", err);
55
56   e = PyObject_CallFunction(GPGMEError, "l", (long) err);
57   if (e == NULL)
58     return NULL;
59
60   PyErr_SetObject(GPGMEError, e);
61   Py_DECREF(e);
62
63   return NULL;  /* raise */
64 }
65
66 gpgme_error_t pygpgme_exception2code(void) {
67   gpgme_error_t err_status = gpg_error(GPG_ERR_GENERAL);
68   if (GPGMEError && PyErr_ExceptionMatches(GPGMEError)) {
69     PyObject *type = 0, *value = 0, *traceback = 0;
70     PyObject *error = 0;
71     PyErr_Fetch(&type, &value, &traceback);
72     PyErr_NormalizeException(&type, &value, &traceback);
73     error = PyObject_GetAttrString(value, "error");
74     err_status = PyLong_AsLong(error);
75     Py_DECREF(error);
76     PyErr_Restore(type, value, traceback);
77   }
78   return err_status;
79 }
80
81 /* Exception support for callbacks.  */
82 #define EXCINFO "_callback_excinfo"
83
84 static void pygpgme_stash_callback_exception(PyObject *weak_self)
85 {
86   PyObject *self, *ptype, *pvalue, *ptraceback, *excinfo;
87
88   PyErr_Fetch(&ptype, &pvalue, &ptraceback);
89   excinfo = PyTuple_New(3);
90   PyTuple_SetItem(excinfo, 0, ptype);
91
92   if (pvalue)
93     PyTuple_SetItem(excinfo, 1, pvalue);
94   else {
95     Py_INCREF(Py_None);
96     PyTuple_SetItem(excinfo, 1, Py_None);
97   }
98
99   if (ptraceback)
100     PyTuple_SetItem(excinfo, 2, ptraceback);
101   else {
102     Py_INCREF(Py_None);
103     PyTuple_SetItem(excinfo, 2, Py_None);
104   }
105
106   self = PyWeakref_GetObject(weak_self);
107   /* self only has a borrowed reference.  */
108   if (self == Py_None) {
109     /* This should not happen, as even if we're called from the data
110        release callback triggered from the wrappers destructor, the
111        object is still alive and hence the weak reference still refers
112        to the object.  However, in case this ever changes, not seeing
113        any exceptions is worse than having a little extra code, so
114        here we go.  */
115       fprintf(stderr,
116               "Error occurred in callback, but the wrapper object "
117               "has been deallocated.\n");
118       PyErr_Restore(ptype, pvalue, ptraceback);
119       PyErr_Print();
120     }
121   else
122     PyObject_SetAttrString(self, EXCINFO, excinfo);
123   Py_DECREF(excinfo);
124 }
125
126 PyObject *pygpgme_raise_callback_exception(PyObject *self)
127 {
128   PyObject *ptype, *pvalue, *ptraceback, *excinfo;
129
130   if (! PyObject_HasAttrString(self, EXCINFO))
131     goto leave;
132
133   excinfo = PyObject_GetAttrString(self, EXCINFO);
134   if (! PyTuple_Check(excinfo))
135     {
136       Py_DECREF(excinfo);
137       goto leave;
138     }
139
140   ptype = PyTuple_GetItem(excinfo, 0);
141   Py_INCREF(excinfo);
142
143   pvalue = PyTuple_GetItem(excinfo, 1);
144   if (pvalue == Py_None)
145     pvalue = NULL;
146   else
147     Py_INCREF(pvalue);
148
149   ptraceback = PyTuple_GetItem(excinfo, 2);
150   if (ptraceback == Py_None)
151     ptraceback = NULL;
152   else
153     Py_INCREF(ptraceback);
154
155   /* We now have references for the extracted items.  */
156   Py_DECREF(excinfo);
157
158   /* Clear the exception information.  It is important to do this
159      before setting the error, because setting the attribute may
160      execute python code, and the runtime system raises a SystemError
161      if an exception is set but values are returned.  */
162   Py_INCREF(Py_None);
163   PyObject_SetAttrString(self, EXCINFO, Py_None);
164
165   /* Restore exception.  */
166   PyErr_Restore(ptype, pvalue, ptraceback);
167   return NULL; /* Raise exception.  */
168
169  leave:
170   Py_INCREF(Py_None);
171   return Py_None;
172 }
173 #undef EXCINFO
174 \f
175 /* Argument conversion.  */
176
177 /* Convert object to a pointer to gpgme type, generic version.  */
178 PyObject *
179 object_to_gpgme_t(PyObject *input, const char *objtype, int argnum)
180 {
181   PyObject *pyname = NULL, *pypointer = NULL;
182   pyname = PyObject_GetAttrString(input, "_ctype");
183   if (pyname && PyUnicode_Check(pyname))
184     {
185       if (strcmp(PyUnicode_AsUTF8(pyname), objtype) != 0)
186         {
187           PyErr_Format(PyExc_TypeError,
188                        "arg %d: Expected value of type %s, but got %s",
189                        argnum, objtype, PyUnicode_AsUTF8(pyname));
190           Py_DECREF(pyname);
191           return NULL;
192         }
193     }
194   else
195     return NULL;
196
197   Py_DECREF(pyname);
198   pypointer = PyObject_GetAttrString(input, "wrapped");
199   if (pypointer == NULL) {
200     PyErr_Format(PyExc_TypeError,
201                  "arg %d: Use of uninitialized Python object %s",
202                  argnum, objtype);
203     return NULL;
204   }
205   return pypointer;
206 }
207
208 /* Convert object to a pointer to gpgme type, version for data
209    objects.  Constructs a wrapper Python on the fly e.g. for file-like
210    objects with a fileno method, returning it in WRAPPER.  This object
211    must be de-referenced when no longer needed.  */
212 PyObject *
213 object_to_gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
214                        PyObject **bytesio, Py_buffer *view)
215 {
216   gpgme_error_t err;
217   PyObject *data;
218   PyObject *fd;
219
220   /* See if it is a file-like object with file number.  */
221   fd = PyObject_CallMethod(input, "fileno", NULL);
222   if (fd) {
223     err = gpgme_data_new_from_fd(wrapper, (int) PyLong_AsLong(fd));
224     Py_DECREF(fd);
225     if (err)
226       return pygpgme_raise_exception (err);
227
228     return pygpgme_wrap_gpgme_data_t(*wrapper);
229   }
230   else
231     PyErr_Clear();
232
233   /* No?  Maybe it implements the buffer protocol.  */
234   data = PyObject_CallMethod(input, "getbuffer", NULL);
235   if (data)
236     {
237       /* Save a reference to input, which seems to be a BytesIO
238          object.  */
239       Py_INCREF(input);
240       *bytesio = input;
241     }
242   else
243     {
244       PyErr_Clear();
245
246       /* No, but maybe the user supplied a buffer object?  */
247       data = input;
248     }
249
250   /* Do we have a buffer object?  */
251   if (PyObject_CheckBuffer(data))
252     {
253       if (PyObject_GetBuffer(data, view, PyBUF_SIMPLE) < 0)
254         return NULL;
255
256       if (data != input)
257         Py_DECREF(data);
258
259       assert (view->obj);
260       assert (view->ndim == 1);
261       assert (view->shape == NULL);
262       assert (view->strides == NULL);
263       assert (view->suboffsets == NULL);
264
265       err = gpgme_data_new_from_mem(wrapper, view->buf, (size_t) view->len, 0);
266       if (err)
267         return pygpgme_raise_exception (err);
268
269       return pygpgme_wrap_gpgme_data_t(*wrapper);
270     }
271
272   /* As last resort we assume it is a wrapped data object.  */
273   if (PyObject_HasAttrString(data, "_ctype"))
274     return object_to_gpgme_t(data, "gpgme_data_t", argnum);
275
276   return PyErr_Format(PyExc_TypeError,
277                       "arg %d: expected pyme.Data, file, or an object "
278                       "implementing the buffer protocol, got %s",
279                       argnum, data->ob_type->tp_name);
280 }
281
282 \f
283
284 PyObject *
285 pygpgme_wrap_fragile_result(PyObject *fragile, const char *classname)
286 {
287   static PyObject *results;
288   PyObject *class;
289   PyObject *replacement;
290
291   if (results == NULL)
292     {
293       PyObject *from_list = PyList_New(0);
294       if (from_list == NULL)
295         return NULL;
296
297       results = PyImport_ImportModuleLevel("results", PyEval_GetGlobals(),
298                                            PyEval_GetLocals(), from_list, 1);
299       Py_DECREF(from_list);
300
301       if (results == NULL)
302         return NULL;
303     }
304
305   class = PyMapping_GetItemString(PyModule_GetDict(results), classname);
306   if (class == NULL)
307     return NULL;
308
309   replacement = PyObject_CallFunctionObjArgs(class, fragile, NULL);
310   Py_DECREF(class);
311   return replacement;
312 }
313
314 \f
315
316 /* Callback support.  */
317 static gpgme_error_t pyPassphraseCb(void *hook,
318                                     const char *uid_hint,
319                                     const char *passphrase_info,
320                                     int prev_was_bad,
321                                     int fd) {
322   PyObject *pyhook = (PyObject *) hook;
323   PyObject *self = NULL;
324   PyObject *func = NULL;
325   PyObject *args = NULL;
326   PyObject *retval = NULL;
327   PyObject *dataarg = NULL;
328   gpgme_error_t err_status = 0;
329
330   pygpgme_exception_init();
331
332   assert (PyTuple_Check(pyhook));
333   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
334   self = PyTuple_GetItem(pyhook, 0);
335   func = PyTuple_GetItem(pyhook, 1);
336   if (PyTuple_Size(pyhook) == 3) {
337     dataarg = PyTuple_GetItem(pyhook, 2);
338     args = PyTuple_New(4);
339   } else {
340     args = PyTuple_New(3);
341   }
342
343   if (uid_hint == NULL)
344     {
345       Py_INCREF(Py_None);
346       PyTuple_SetItem(args, 0, Py_None);
347     }
348   else
349     PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
350                                                   "strict"));
351   if (PyErr_Occurred()) {
352     Py_DECREF(args);
353     err_status = gpg_error(GPG_ERR_GENERAL);
354     goto leave;
355   }
356
357   PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
358   PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
359   if (dataarg) {
360     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
361     PyTuple_SetItem(args, 3, dataarg);
362   }
363
364   retval = PyObject_CallObject(func, args);
365   Py_DECREF(args);
366   if (PyErr_Occurred()) {
367     err_status = pygpgme_exception2code();
368   } else {
369     if (!retval) {
370       if (write(fd, "\n", 1) < 0) {
371         err_status = gpgme_error_from_syserror ();
372         pygpgme_raise_exception (err_status);
373       }
374     } else {
375       char *buf;
376       size_t len;
377       if (PyBytes_Check(retval))
378         buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
379       else if (PyUnicode_Check(retval))
380         {
381           Py_ssize_t ssize;
382           buf = PyUnicode_AsUTF8AndSize(retval, &ssize);
383           assert (! buf || ssize >= 0);
384           len = (size_t) ssize;
385         }
386       else
387         {
388           PyErr_Format(PyExc_TypeError,
389                        "expected str or bytes from passphrase callback, got %s",
390                        retval->ob_type->tp_name);
391           err_status = gpg_error(GPG_ERR_GENERAL);
392           goto leave;
393         }
394
395       if (write(fd, buf, len) < 0) {
396         err_status = gpgme_error_from_syserror ();
397         pygpgme_raise_exception (err_status);
398       }
399       if (! err_status && write(fd, "\n", 1) < 0) {
400         err_status = gpgme_error_from_syserror ();
401         pygpgme_raise_exception (err_status);
402       }
403
404       Py_DECREF(retval);
405     }
406   }
407
408  leave:
409   if (err_status)
410     pygpgme_stash_callback_exception(self);
411
412   return err_status;
413 }
414
415 PyObject *
416 pygpgme_set_passphrase_cb(PyObject *self, PyObject *cb) {
417   PyObject *wrapped;
418   gpgme_ctx_t ctx;
419
420   wrapped = PyObject_GetAttrString(self, "wrapped");
421   if (wrapped == NULL)
422     {
423       assert (PyErr_Occurred ());
424       return NULL;
425     }
426
427   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
428   Py_DECREF(wrapped);
429   if (ctx == NULL)
430     {
431       if (cb == Py_None)
432         goto out;
433       else
434         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
435     }
436
437   if (cb == Py_None) {
438     gpgme_set_passphrase_cb(ctx, NULL, NULL);
439     PyObject_SetAttrString(self, "_passphrase_cb", Py_None);
440     goto out;
441   }
442
443   if (! PyTuple_Check(cb))
444     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
445   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
446     return PyErr_Format(PyExc_TypeError,
447                         "cb must be a tuple of size 2 or 3");
448
449   gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t) pyPassphraseCb,
450                           (void *) cb);
451   PyObject_SetAttrString(self, "_passphrase_cb", cb);
452
453  out:
454   Py_INCREF(Py_None);
455   return Py_None;
456 }
457
458 static void pyProgressCb(void *hook, const char *what, int type, int current,
459                          int total) {
460   PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
461   PyObject *pyhook = (PyObject *) hook;
462   PyObject *self = NULL;
463
464   assert (PyTuple_Check(pyhook));
465   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
466   self = PyTuple_GetItem(pyhook, 0);
467   func = PyTuple_GetItem(pyhook, 1);
468   if (PyTuple_Size(pyhook) == 3) {
469     dataarg = PyTuple_GetItem(pyhook, 2);
470     args = PyTuple_New(5);
471   } else {
472     args = PyTuple_New(4);
473   }
474
475   PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
476                                                 "strict"));
477   if (PyErr_Occurred()) {
478     pygpgme_stash_callback_exception(self);
479     Py_DECREF(args);
480     return;
481   }
482   PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
483   PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
484   PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
485   if (dataarg) {
486     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
487     PyTuple_SetItem(args, 4, dataarg);
488   }
489
490   retval = PyObject_CallObject(func, args);
491   if (PyErr_Occurred())
492     pygpgme_stash_callback_exception(self);
493   Py_DECREF(args);
494   Py_XDECREF(retval);
495 }
496
497 PyObject *
498 pygpgme_set_progress_cb(PyObject *self, PyObject *cb) {
499   PyObject *wrapped;
500   gpgme_ctx_t ctx;
501
502   wrapped = PyObject_GetAttrString(self, "wrapped");
503   if (wrapped == NULL)
504     {
505       assert (PyErr_Occurred ());
506       return NULL;
507     }
508
509   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
510   Py_DECREF(wrapped);
511   if (ctx == NULL)
512     {
513       if (cb == Py_None)
514         goto out;
515       else
516         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
517     }
518
519   if (cb == Py_None) {
520     gpgme_set_progress_cb(ctx, NULL, NULL);
521     PyObject_SetAttrString(self, "_progress_cb", Py_None);
522     goto out;
523   }
524
525   if (! PyTuple_Check(cb))
526     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
527   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
528     return PyErr_Format(PyExc_TypeError,
529                         "cb must be a tuple of size 2 or 3");
530
531   gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
532   PyObject_SetAttrString(self, "_progress_cb", cb);
533
534  out:
535   Py_INCREF(Py_None);
536   return Py_None;
537 }
538 \f
539 /* Status callbacks.  */
540 static gpgme_error_t pyStatusCb(void *hook, const char *keyword,
541                                 const char *args) {
542   gpgme_error_t err = 0;
543   PyObject *pyhook = (PyObject *) hook;
544   PyObject *self = NULL;
545   PyObject *func = NULL;
546   PyObject *dataarg = NULL;
547   PyObject *pyargs = NULL;
548   PyObject *retval = NULL;
549
550   assert (PyTuple_Check(pyhook));
551   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
552   self = PyTuple_GetItem(pyhook, 0);
553   func = PyTuple_GetItem(pyhook, 1);
554   if (PyTuple_Size(pyhook) == 3) {
555     dataarg = PyTuple_GetItem(pyhook, 2);
556     pyargs = PyTuple_New(3);
557   } else {
558     pyargs = PyTuple_New(2);
559   }
560
561   if (keyword)
562     PyTuple_SetItem(pyargs, 0, PyUnicode_DecodeUTF8(keyword, strlen (keyword),
563                                                     "strict"));
564   else
565     {
566       Py_INCREF(Py_None);
567       PyTuple_SetItem(pyargs, 0, Py_None);
568     }
569   PyTuple_SetItem(pyargs, 1, PyUnicode_DecodeUTF8(args, strlen (args),
570                                                 "strict"));
571   if (PyErr_Occurred()) {
572     err = gpg_error(GPG_ERR_GENERAL);
573     Py_DECREF(pyargs);
574     goto leave;
575   }
576
577   if (dataarg) {
578     Py_INCREF(dataarg);
579     PyTuple_SetItem(pyargs, 2, dataarg);
580   }
581
582   retval = PyObject_CallObject(func, pyargs);
583   if (PyErr_Occurred())
584     err = pygpgme_exception2code();
585   Py_DECREF(pyargs);
586   Py_XDECREF(retval);
587
588  leave:
589   if (err)
590     pygpgme_stash_callback_exception(self);
591   return err;
592 }
593
594 PyObject *
595 pygpgme_set_status_cb(PyObject *self, PyObject *cb) {
596   PyObject *wrapped;
597   gpgme_ctx_t ctx;
598
599   wrapped = PyObject_GetAttrString(self, "wrapped");
600   if (wrapped == NULL)
601     {
602       assert (PyErr_Occurred ());
603       return NULL;
604     }
605
606   ctx = pygpgme_unwrap_gpgme_ctx_t(wrapped);
607   Py_DECREF(wrapped);
608   if (ctx == NULL)
609     {
610       if (cb == Py_None)
611         goto out;
612       else
613         return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
614     }
615
616   if (cb == Py_None) {
617     gpgme_set_status_cb(ctx, NULL, NULL);
618     PyObject_SetAttrString(self, "_status_cb", Py_None);
619     goto out;
620   }
621
622   if (! PyTuple_Check(cb))
623     return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
624   if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
625     return PyErr_Format(PyExc_TypeError,
626                         "cb must be a tuple of size 2 or 3");
627
628   gpgme_set_status_cb(ctx, (gpgme_status_cb_t) pyStatusCb, (void *) cb);
629   PyObject_SetAttrString(self, "_status_cb", cb);
630
631  out:
632   Py_INCREF(Py_None);
633   return Py_None;
634 }
635 \f
636 /* Edit callbacks.  */
637 gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
638                        const char *args, int fd) {
639   PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
640   PyObject *pyopaque = (PyObject *) opaque;
641   gpgme_error_t err_status = 0;
642   PyObject *self = NULL;
643
644   pygpgme_exception_init();
645
646   assert (PyTuple_Check(pyopaque));
647   assert (PyTuple_Size(pyopaque) == 2 || PyTuple_Size(pyopaque) == 3);
648   self = PyTuple_GetItem(pyopaque, 0);
649   func = PyTuple_GetItem(pyopaque, 1);
650   if (PyTuple_Size(pyopaque) == 3) {
651     dataarg = PyTuple_GetItem(pyopaque, 2);
652     pyargs = PyTuple_New(3);
653   } else {
654     pyargs = PyTuple_New(2);
655   }
656
657   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
658   PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
659   if (dataarg) {
660     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
661     PyTuple_SetItem(pyargs, 2, dataarg);
662   }
663
664   retval = PyObject_CallObject(func, pyargs);
665   Py_DECREF(pyargs);
666   if (PyErr_Occurred()) {
667     err_status = pygpgme_exception2code();
668   } else {
669     if (fd>=0 && retval && PyUnicode_Check(retval)) {
670       const char *buffer;
671       Py_ssize_t size;
672
673       buffer = PyUnicode_AsUTF8AndSize(retval, &size);
674       if (write(fd, buffer, size) < 0) {
675         err_status = gpgme_error_from_syserror ();
676         pygpgme_raise_exception (err_status);
677       }
678       if (! err_status && write(fd, "\n", 1) < 0) {
679         err_status = gpgme_error_from_syserror ();
680         pygpgme_raise_exception (err_status);
681       }
682     }
683   }
684   if (err_status)
685     pygpgme_stash_callback_exception(self);
686
687   Py_XDECREF(retval);
688   return err_status;
689 }
690 \f
691 /* Data callbacks.  */
692
693 /* Read up to SIZE bytes into buffer BUFFER from the data object with
694    the handle HOOK.  Return the number of characters read, 0 on EOF
695    and -1 on error.  If an error occurs, errno is set.  */
696 static ssize_t pyDataReadCb(void *hook, void *buffer, size_t size)
697 {
698   ssize_t result;
699   PyObject *pyhook = (PyObject *) hook;
700   PyObject *self = NULL;
701   PyObject *func = NULL;
702   PyObject *dataarg = NULL;
703   PyObject *pyargs = NULL;
704   PyObject *retval = NULL;
705
706   assert (PyTuple_Check(pyhook));
707   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
708
709   self = PyTuple_GetItem(pyhook, 0);
710   func = PyTuple_GetItem(pyhook, 1);
711   if (PyTuple_Size(pyhook) == 6) {
712     dataarg = PyTuple_GetItem(pyhook, 5);
713     pyargs = PyTuple_New(2);
714   } else {
715     pyargs = PyTuple_New(1);
716   }
717
718   PyTuple_SetItem(pyargs, 0, PyLong_FromSize_t(size));
719   if (dataarg) {
720     Py_INCREF(dataarg);
721     PyTuple_SetItem(pyargs, 1, dataarg);
722   }
723
724   retval = PyObject_CallObject(func, pyargs);
725   Py_DECREF(pyargs);
726   if (PyErr_Occurred()) {
727     pygpgme_stash_callback_exception(self);
728     result = -1;
729     goto leave;
730   }
731
732   if (! PyBytes_Check(retval)) {
733     PyErr_Format(PyExc_TypeError,
734                  "expected bytes from read callback, got %s",
735                  retval->ob_type->tp_name);
736     pygpgme_stash_callback_exception(self);
737     result = -1;
738     goto leave;
739   }
740
741   if (PyBytes_Size(retval) > size) {
742     PyErr_Format(PyExc_TypeError,
743                  "expected %zu bytes from read callback, got %zu",
744                  size, PyBytes_Size(retval));
745     pygpgme_stash_callback_exception(self);
746     result = -1;
747     goto leave;
748   }
749
750   memcpy(buffer, PyBytes_AsString(retval), PyBytes_Size(retval));
751   result = PyBytes_Size(retval);
752
753  leave:
754   Py_XDECREF(retval);
755   return result;
756 }
757
758 /* Write up to SIZE bytes from buffer BUFFER to the data object with
759    the handle HOOK.  Return the number of characters written, or -1
760    on error.  If an error occurs, errno is set.  */
761 static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
762 {
763   ssize_t result;
764   PyObject *pyhook = (PyObject *) hook;
765   PyObject *self = NULL;
766   PyObject *func = NULL;
767   PyObject *dataarg = NULL;
768   PyObject *pyargs = NULL;
769   PyObject *retval = NULL;
770
771   assert (PyTuple_Check(pyhook));
772   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
773
774   self = PyTuple_GetItem(pyhook, 0);
775   func = PyTuple_GetItem(pyhook, 2);
776   if (PyTuple_Size(pyhook) == 6) {
777     dataarg = PyTuple_GetItem(pyhook, 5);
778     pyargs = PyTuple_New(2);
779   } else {
780     pyargs = PyTuple_New(1);
781   }
782
783   PyTuple_SetItem(pyargs, 0, PyBytes_FromStringAndSize(buffer, size));
784   if (dataarg) {
785     Py_INCREF(dataarg);
786     PyTuple_SetItem(pyargs, 1, dataarg);
787   }
788
789   retval = PyObject_CallObject(func, pyargs);
790   Py_DECREF(pyargs);
791   if (PyErr_Occurred()) {
792     pygpgme_stash_callback_exception(self);
793     result = -1;
794     goto leave;
795   }
796
797   if (! PyLong_Check(retval)) {
798     PyErr_Format(PyExc_TypeError,
799                  "expected int from read callback, got %s",
800                  retval->ob_type->tp_name);
801     pygpgme_stash_callback_exception(self);
802     result = -1;
803     goto leave;
804   }
805
806   result = PyLong_AsSsize_t(retval);
807
808  leave:
809   Py_XDECREF(retval);
810   return result;
811 }
812
813 /* Set the current position from where the next read or write starts
814    in the data object with the handle HOOK to OFFSET, relativ to
815    WHENCE.  Returns the new offset in bytes from the beginning of the
816    data object.  */
817 static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
818 {
819   off_t result;
820   PyObject *pyhook = (PyObject *) hook;
821   PyObject *self = NULL;
822   PyObject *func = NULL;
823   PyObject *dataarg = NULL;
824   PyObject *pyargs = NULL;
825   PyObject *retval = NULL;
826
827   assert (PyTuple_Check(pyhook));
828   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
829
830   self = PyTuple_GetItem(pyhook, 0);
831   func = PyTuple_GetItem(pyhook, 3);
832   if (PyTuple_Size(pyhook) == 6) {
833     dataarg = PyTuple_GetItem(pyhook, 5);
834     pyargs = PyTuple_New(3);
835   } else {
836     pyargs = PyTuple_New(2);
837   }
838
839 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
840   PyTuple_SetItem(pyargs, 0, PyLong_FromLongLong((long long) offset));
841 #else
842   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) offset));
843 #endif
844   PyTuple_SetItem(pyargs, 1, PyLong_FromLong((long) whence));
845   if (dataarg) {
846     Py_INCREF(dataarg);
847     PyTuple_SetItem(pyargs, 2, dataarg);
848   }
849
850   retval = PyObject_CallObject(func, pyargs);
851   Py_DECREF(pyargs);
852   if (PyErr_Occurred()) {
853     pygpgme_stash_callback_exception(self);
854     result = -1;
855     goto leave;
856   }
857
858   if (! PyLong_Check(retval)) {
859     PyErr_Format(PyExc_TypeError,
860                  "expected int from read callback, got %s",
861                  retval->ob_type->tp_name);
862     pygpgme_stash_callback_exception(self);
863     result = -1;
864     goto leave;
865   }
866
867 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
868   result = PyLong_AsLongLong(retval);
869 #else
870   result = PyLong_AsLong(retval);
871 #endif
872
873  leave:
874   Py_XDECREF(retval);
875   return result;
876 }
877
878 /* Close the data object with the handle HOOK.  */
879 static void pyDataReleaseCb(void *hook)
880 {
881   PyObject *pyhook = (PyObject *) hook;
882   PyObject *self = NULL;
883   PyObject *func = NULL;
884   PyObject *dataarg = NULL;
885   PyObject *pyargs = NULL;
886   PyObject *retval = NULL;
887
888   assert (PyTuple_Check(pyhook));
889   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
890
891   self = PyTuple_GetItem(pyhook, 0);
892   func = PyTuple_GetItem(pyhook, 4);
893   if (PyTuple_Size(pyhook) == 6) {
894     dataarg = PyTuple_GetItem(pyhook, 5);
895     pyargs = PyTuple_New(1);
896   } else {
897     pyargs = PyTuple_New(0);
898   }
899
900   if (dataarg) {
901     Py_INCREF(dataarg);
902     PyTuple_SetItem(pyargs, 0, dataarg);
903   }
904
905   retval = PyObject_CallObject(func, pyargs);
906   Py_XDECREF(retval);
907   Py_DECREF(pyargs);
908   if (PyErr_Occurred())
909     pygpgme_stash_callback_exception(self);
910 }
911
912 PyObject *
913 pygpgme_data_new_from_cbs(PyObject *self,
914                           PyObject *pycbs,
915                           gpgme_data_t *r_data)
916 {
917   static struct gpgme_data_cbs cbs = {
918     pyDataReadCb,
919     pyDataWriteCb,
920     pyDataSeekCb,
921     pyDataReleaseCb,
922   };
923   gpgme_error_t err;
924
925   if (! PyTuple_Check(pycbs))
926     return PyErr_Format(PyExc_TypeError, "pycbs must be a tuple");
927   if (PyTuple_Size(pycbs) != 5 && PyTuple_Size(pycbs) != 6)
928     return PyErr_Format(PyExc_TypeError,
929                         "pycbs must be a tuple of size 5 or 6");
930
931   err = gpgme_data_new_from_cbs(r_data, &cbs, (void *) pycbs);
932   if (err)
933     return pygpgme_raise_exception(err);
934
935   PyObject_SetAttrString(self, "_data_cbs", pycbs);
936
937   Py_INCREF(Py_None);
938   return Py_None;
939 }
940
941 \f
942
943 /* The assuan callbacks.  */
944
945 gpgme_error_t
946 _pyme_assuan_data_cb (void *hook, const void *data, size_t datalen)
947 {
948   gpgme_error_t err = 0;
949   PyObject *pyhook = (PyObject *) hook;
950   PyObject *self = NULL;
951   PyObject *func = NULL;
952   PyObject *py_data = NULL;
953   PyObject *retval = NULL;
954
955   assert (PyTuple_Check(pyhook));
956   assert (PyTuple_Size(pyhook) == 2);
957   self = PyTuple_GetItem(pyhook, 0);
958   func = PyTuple_GetItem(pyhook, 1);
959   assert (PyCallable_Check(func));
960
961   py_data = PyBytes_FromStringAndSize(data, datalen);
962   if (py_data == NULL)
963     return NULL;        /* raise */
964
965   retval = PyObject_CallFunctionObjArgs(func, py_data, NULL);
966   if (PyErr_Occurred())
967     err = pygpgme_exception2code();
968   Py_DECREF(py_data);
969   Py_XDECREF(retval);
970
971  leave:
972   if (err)
973     pygpgme_stash_callback_exception(self);
974   return err;
975 }
976
977 gpgme_error_t
978 _pyme_assuan_inquire_cb (void *hook, const char *name, const char *args,
979                          gpgme_data_t *r_data)
980 {
981   gpgme_error_t err = 0;
982   PyObject *pyhook = (PyObject *) hook;
983   PyObject *self = NULL;
984   PyObject *func = NULL;
985   PyObject *py_name = NULL;
986   PyObject *py_args = NULL;
987   PyObject *retval = NULL;
988
989   assert (PyTuple_Check(pyhook));
990   assert (PyTuple_Size(pyhook) == 2);
991   self = PyTuple_GetItem(pyhook, 0);
992   func = PyTuple_GetItem(pyhook, 1);
993   assert (PyCallable_Check(func));
994
995   py_name = PyUnicode_FromString(name);
996   if (py_name == NULL)
997     return NULL;        /* raise */
998
999   py_args = PyUnicode_FromString(args);
1000   if (py_args == NULL)
1001     return NULL;        /* raise */
1002
1003   retval = PyObject_CallFunctionObjArgs(func, py_name, py_args, NULL);
1004   if (PyErr_Occurred())
1005     err = pygpgme_exception2code();
1006   Py_DECREF(py_name);
1007   Py_DECREF(py_args);
1008   Py_XDECREF(retval);
1009
1010   /* FIXME: Returning data is not yet implemented.  */
1011   r_data = NULL;
1012
1013  leave:
1014   if (err)
1015     pygpgme_stash_callback_exception(self);
1016   return err;
1017 }
1018
1019 gpgme_error_t
1020 _pyme_assuan_status_cb (void *hook, const char *status, const char *args)
1021 {
1022   gpgme_error_t err = 0;
1023   PyObject *pyhook = (PyObject *) hook;
1024   PyObject *self = NULL;
1025   PyObject *func = NULL;
1026   PyObject *py_status = NULL;
1027   PyObject *py_args = NULL;
1028   PyObject *retval = NULL;
1029
1030   assert (PyTuple_Check(pyhook));
1031   assert (PyTuple_Size(pyhook) == 2);
1032   self = PyTuple_GetItem(pyhook, 0);
1033   func = PyTuple_GetItem(pyhook, 1);
1034   assert (PyCallable_Check(func));
1035
1036   py_status = PyUnicode_FromString(status);
1037   if (py_status == NULL)
1038     return NULL;        /* raise */
1039
1040   py_args = PyUnicode_FromString(args);
1041   if (py_args == NULL)
1042     return NULL;        /* raise */
1043
1044   retval = PyObject_CallFunctionObjArgs(func, py_status, py_args, NULL);
1045   if (PyErr_Occurred())
1046     err = pygpgme_exception2code();
1047   Py_DECREF(py_status);
1048   Py_DECREF(py_args);
1049   Py_XDECREF(retval);
1050
1051  leave:
1052   if (err)
1053     pygpgme_stash_callback_exception(self);
1054   return err;
1055 }