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