python: Improve build system integration, fix warnings.
[gpgme.git] / lang / python / helpers.c
1 /*
2 # $Id$
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 gpgme_error_t pygpgme_exception2code(void) {
46   gpgme_error_t err_status = gpg_error(GPG_ERR_GENERAL);
47   if (GPGMEError && PyErr_ExceptionMatches(GPGMEError)) {
48     PyObject *type = 0, *value = 0, *traceback = 0;
49     PyObject *error = 0;
50     PyErr_Fetch(&type, &value, &traceback);
51     PyErr_NormalizeException(&type, &value, &traceback);
52     error = PyObject_GetAttrString(value, "error");
53     err_status = PyLong_AsLong(error);
54     Py_DECREF(error);
55     PyErr_Restore(type, value, traceback);
56   }
57   return err_status;
58 }
59
60 void pygpgme_clear_generic_cb(PyObject **cb) {
61   Py_DECREF(*cb);
62 }
63
64 /* Exception support for callbacks.  */
65 #define EXCINFO "_callback_excinfo"
66
67 static void pygpgme_stash_callback_exception(PyObject *weak_self)
68 {
69   PyObject *self, *ptype, *pvalue, *ptraceback, *excinfo;
70
71   PyErr_Fetch(&ptype, &pvalue, &ptraceback);
72   excinfo = PyTuple_New(3);
73   PyTuple_SetItem(excinfo, 0, ptype);
74
75   if (pvalue)
76     PyTuple_SetItem(excinfo, 1, pvalue);
77   else {
78     Py_INCREF(Py_None);
79     PyTuple_SetItem(excinfo, 1, Py_None);
80   }
81
82   if (ptraceback)
83     PyTuple_SetItem(excinfo, 2, ptraceback);
84   else {
85     Py_INCREF(Py_None);
86     PyTuple_SetItem(excinfo, 2, Py_None);
87   }
88
89   self = PyWeakref_GetObject(weak_self);
90   /* self only has a borrowed reference.  */
91   if (self == Py_None) {
92     /* This should not happen, as even if we're called from the data
93        release callback triggered from the wrappers destructor, the
94        object is still alive and hence the weak reference still refers
95        to the object.  However, in case this ever changes, not seeing
96        any exceptions is worse than having a little extra code, so
97        here we go.  */
98       fprintf(stderr,
99               "Error occurred in callback, but the wrapper object "
100               "has been deallocated.\n");
101       PyErr_Restore(ptype, pvalue, ptraceback);
102       PyErr_Print();
103     }
104   else
105     PyObject_SetAttrString(self, EXCINFO, excinfo);
106 }
107
108 PyObject *pygpgme_raise_callback_exception(PyObject *self)
109 {
110   PyObject *ptype, *pvalue, *ptraceback, *excinfo;
111
112   if (! PyObject_HasAttrString(self, EXCINFO))
113     goto leave;
114
115   excinfo = PyObject_GetAttrString(self, EXCINFO);
116   if (! PyTuple_Check(excinfo))
117     {
118       Py_DECREF(excinfo);
119       goto leave;
120     }
121
122   ptype = PyTuple_GetItem(excinfo, 0);
123   Py_INCREF(excinfo);
124
125   pvalue = PyTuple_GetItem(excinfo, 1);
126   if (pvalue == Py_None)
127     pvalue = NULL;
128   else
129     Py_INCREF(pvalue);
130
131   ptraceback = PyTuple_GetItem(excinfo, 2);
132   if (ptraceback == Py_None)
133     ptraceback = NULL;
134   else
135     Py_INCREF(ptraceback);
136
137   Py_DECREF(excinfo);
138   PyErr_Restore(ptype, pvalue, ptraceback);
139
140   Py_INCREF(Py_None);
141   PyObject_SetAttrString(self, EXCINFO, Py_None);
142
143   return NULL; /* Raise exception.  */
144
145  leave:
146   Py_INCREF(Py_None);
147   return Py_None;
148 }
149 #undef EXCINFO
150
151 static gpgme_error_t pyPassphraseCb(void *hook,
152                                     const char *uid_hint,
153                                     const char *passphrase_info,
154                                     int prev_was_bad,
155                                     int fd) {
156   PyObject *pyhook = (PyObject *) hook;
157   PyObject *self = NULL;
158   PyObject *func = NULL;
159   PyObject *args = NULL;
160   PyObject *retval = NULL;
161   PyObject *dataarg = NULL;
162   gpgme_error_t err_status = 0;
163
164   pygpgme_exception_init();
165
166   assert (PyTuple_Check(pyhook));
167   self = PyTuple_GetItem(pyhook, 0);
168   func = PyTuple_GetItem(pyhook, 1);
169   if (PyTuple_Size(pyhook) == 3) {
170     dataarg = PyTuple_GetItem(pyhook, 2);
171     args = PyTuple_New(4);
172   } else {
173     args = PyTuple_New(3);
174   }
175
176   if (uid_hint == NULL)
177     {
178       Py_INCREF(Py_None);
179       PyTuple_SetItem(args, 0, Py_None);
180     }
181   else
182     PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
183                                                   "strict"));
184   if (PyErr_Occurred()) {
185     Py_DECREF(args);
186     err_status = gpg_error(GPG_ERR_GENERAL);
187     goto leave;
188   }
189
190   PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
191   PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
192   if (dataarg) {
193     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
194     PyTuple_SetItem(args, 3, dataarg);
195   }
196
197   retval = PyObject_CallObject(func, args);
198   Py_DECREF(args);
199   if (PyErr_Occurred()) {
200     err_status = pygpgme_exception2code();
201   } else {
202     if (!retval) {
203       write(fd, "\n", 1);
204     } else {
205       char *buf;
206       size_t len;
207       if (PyBytes_Check(retval))
208         buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
209       else if (PyUnicode_Check(retval))
210         {
211           Py_ssize_t ssize;
212           buf = PyUnicode_AsUTF8AndSize(retval, &ssize);
213           assert (! buf || ssize >= 0);
214           len = (size_t) ssize;
215         }
216       else
217         {
218           PyErr_Format(PyExc_TypeError,
219                        "expected str or bytes from passphrase callback, got %s",
220                        retval->ob_type->tp_name);
221           err_status = gpg_error(GPG_ERR_GENERAL);
222           goto leave;
223         }
224
225       write(fd, buf, len);
226       write(fd, "\n", 1);
227       Py_DECREF(retval);
228     }
229   }
230
231  leave:
232   if (err_status)
233     pygpgme_stash_callback_exception(self);
234
235   return err_status;
236 }
237
238 void pygpgme_set_passphrase_cb(gpgme_ctx_t ctx, PyObject *cb,
239                                PyObject **freelater) {
240   if (cb == Py_None) {
241     gpgme_set_passphrase_cb(ctx, NULL, NULL);
242     return;
243   }
244   Py_INCREF(cb);
245   *freelater = cb;
246   gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t)pyPassphraseCb, (void *) cb);
247 }
248
249 static void pyProgressCb(void *hook, const char *what, int type, int current,
250                          int total) {
251   PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
252   PyObject *pyhook = (PyObject *) hook;
253   PyObject *self = NULL;
254
255   assert (PyTuple_Check(pyhook));
256   self = PyTuple_GetItem(pyhook, 0);
257   func = PyTuple_GetItem(pyhook, 1);
258   if (PyTuple_Size(pyhook) == 3) {
259     dataarg = PyTuple_GetItem(pyhook, 2);
260     args = PyTuple_New(5);
261   } else {
262     args = PyTuple_New(4);
263   }
264
265   PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
266                                                 "strict"));
267   if (PyErr_Occurred()) {
268     pygpgme_stash_callback_exception(self);
269     Py_DECREF(args);
270     return;
271   }
272   PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
273   PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
274   PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
275   if (dataarg) {
276     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
277     PyTuple_SetItem(args, 4, dataarg);
278   }
279
280   retval = PyObject_CallObject(func, args);
281   if (PyErr_Occurred())
282     pygpgme_stash_callback_exception(self);
283   Py_DECREF(args);
284   Py_XDECREF(retval);
285 }
286
287 void pygpgme_set_progress_cb(gpgme_ctx_t ctx, PyObject *cb, PyObject **freelater){
288   if (cb == Py_None) {
289     gpgme_set_progress_cb(ctx, NULL, NULL);
290     return;
291   }
292   Py_INCREF(cb);
293   *freelater = cb;
294   gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
295 }
296 \f
297 /* Status callbacks.  */
298 static gpgme_error_t pyStatusCb(void *hook, const char *keyword,
299                                 const char *args) {
300   gpgme_error_t err = 0;
301   PyObject *pyhook = (PyObject *) hook;
302   PyObject *self = NULL;
303   PyObject *func = NULL;
304   PyObject *dataarg = NULL;
305   PyObject *pyargs = NULL;
306   PyObject *retval = NULL;
307
308   assert (PyTuple_Check(pyhook));
309   assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
310   self = PyTuple_GetItem(pyhook, 0);
311   func = PyTuple_GetItem(pyhook, 1);
312   if (PyTuple_Size(pyhook) == 3) {
313     dataarg = PyTuple_GetItem(pyhook, 2);
314     pyargs = PyTuple_New(3);
315   } else {
316     pyargs = PyTuple_New(2);
317   }
318
319   if (keyword)
320     PyTuple_SetItem(pyargs, 0, PyUnicode_DecodeUTF8(keyword, strlen (keyword),
321                                                     "strict"));
322   else
323     {
324       Py_INCREF(Py_None);
325       PyTuple_SetItem(pyargs, 0, Py_None);
326     }
327   PyTuple_SetItem(pyargs, 1, PyUnicode_DecodeUTF8(args, strlen (args),
328                                                 "strict"));
329   if (PyErr_Occurred()) {
330     err = gpg_error(GPG_ERR_GENERAL);
331     Py_DECREF(pyargs);
332     goto leave;
333   }
334
335   if (dataarg) {
336     Py_INCREF(dataarg);
337     PyTuple_SetItem(pyargs, 2, dataarg);
338   }
339
340   retval = PyObject_CallObject(func, pyargs);
341   if (PyErr_Occurred())
342     err = pygpgme_exception2code();
343   Py_DECREF(pyargs);
344   Py_XDECREF(retval);
345
346  leave:
347   if (err)
348     pygpgme_stash_callback_exception(self);
349   return err;
350 }
351
352 void pygpgme_set_status_cb(gpgme_ctx_t ctx, PyObject *cb,
353                            PyObject **freelater) {
354   if (cb == Py_None) {
355     gpgme_set_status_cb(ctx, NULL, NULL);
356     return;
357   }
358   Py_INCREF(cb);
359   *freelater = cb;
360   gpgme_set_status_cb(ctx, (gpgme_status_cb_t) pyStatusCb, (void *) cb);
361 }
362 \f
363 /* Edit callbacks.  */
364 gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
365                        const char *args, int fd) {
366   PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
367   PyObject *pyopaque = (PyObject *) opaque;
368   gpgme_error_t err_status = 0;
369   PyObject *self = NULL;
370
371   pygpgme_exception_init();
372
373   assert (PyTuple_Check(pyopaque));
374   assert (PyTuple_Size(pyopaque) == 2 || PyTuple_Size(pyopaque) == 3);
375   self = PyTuple_GetItem(pyopaque, 0);
376   func = PyTuple_GetItem(pyopaque, 1);
377   if (PyTuple_Size(pyopaque) == 3) {
378     dataarg = PyTuple_GetItem(pyopaque, 2);
379     pyargs = PyTuple_New(3);
380   } else {
381     pyargs = PyTuple_New(2);
382   }
383
384   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
385   PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
386   if (dataarg) {
387     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
388     PyTuple_SetItem(pyargs, 2, dataarg);
389   }
390
391   retval = PyObject_CallObject(func, pyargs);
392   Py_DECREF(pyargs);
393   if (PyErr_Occurred()) {
394     err_status = pygpgme_exception2code();
395     pygpgme_stash_callback_exception(self);
396   } else {
397     if (fd>=0 && retval && PyUnicode_Check(retval)) {
398       const char *buffer;
399       Py_ssize_t size;
400
401       buffer = PyUnicode_AsUTF8AndSize(retval, &size);
402       write(fd, buffer, size);
403       write(fd, "\n", 1);
404     }
405   }
406
407   Py_XDECREF(retval);
408   return err_status;
409 }
410 \f
411 /* Data callbacks.  */
412
413 /* Read up to SIZE bytes into buffer BUFFER from the data object with
414    the handle HOOK.  Return the number of characters read, 0 on EOF
415    and -1 on error.  If an error occurs, errno is set.  */
416 static ssize_t pyDataReadCb(void *hook, void *buffer, size_t size)
417 {
418   ssize_t result;
419   PyObject *pyhook = (PyObject *) hook;
420   PyObject *self = NULL;
421   PyObject *func = NULL;
422   PyObject *dataarg = NULL;
423   PyObject *pyargs = NULL;
424   PyObject *retval = NULL;
425
426   assert (PyTuple_Check(pyhook));
427   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
428
429   self = PyTuple_GetItem(pyhook, 0);
430   func = PyTuple_GetItem(pyhook, 1);
431   if (PyTuple_Size(pyhook) == 6) {
432     dataarg = PyTuple_GetItem(pyhook, 5);
433     pyargs = PyTuple_New(2);
434   } else {
435     pyargs = PyTuple_New(1);
436   }
437
438   PyTuple_SetItem(pyargs, 0, PyLong_FromSize_t(size));
439   if (dataarg) {
440     Py_INCREF(dataarg);
441     PyTuple_SetItem(pyargs, 1, dataarg);
442   }
443
444   retval = PyObject_CallObject(func, pyargs);
445   Py_DECREF(pyargs);
446   if (PyErr_Occurred()) {
447     pygpgme_stash_callback_exception(self);
448     result = -1;
449     goto leave;
450   }
451
452   if (! PyBytes_Check(retval)) {
453     PyErr_Format(PyExc_TypeError,
454                  "expected bytes from read callback, got %s",
455                  retval->ob_type->tp_name);
456     pygpgme_stash_callback_exception(self);
457     result = -1;
458     goto leave;
459   }
460
461   if (PyBytes_Size(retval) > size) {
462     PyErr_Format(PyExc_TypeError,
463                  "expected %zu bytes from read callback, got %zu",
464                  size, PyBytes_Size(retval));
465     pygpgme_stash_callback_exception(self);
466     result = -1;
467     goto leave;
468   }
469
470   memcpy(buffer, PyBytes_AsString(retval), PyBytes_Size(retval));
471   result = PyBytes_Size(retval);
472
473  leave:
474   Py_XDECREF(retval);
475   return result;
476 }
477
478 /* Write up to SIZE bytes from buffer BUFFER to the data object with
479    the handle HOOK.  Return the number of characters written, or -1
480    on error.  If an error occurs, errno is set.  */
481 static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
482 {
483   ssize_t result;
484   PyObject *pyhook = (PyObject *) hook;
485   PyObject *self = NULL;
486   PyObject *func = NULL;
487   PyObject *dataarg = NULL;
488   PyObject *pyargs = NULL;
489   PyObject *retval = NULL;
490
491   assert (PyTuple_Check(pyhook));
492   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
493
494   self = PyTuple_GetItem(pyhook, 0);
495   func = PyTuple_GetItem(pyhook, 2);
496   if (PyTuple_Size(pyhook) == 6) {
497     dataarg = PyTuple_GetItem(pyhook, 5);
498     pyargs = PyTuple_New(2);
499   } else {
500     pyargs = PyTuple_New(1);
501   }
502
503   PyTuple_SetItem(pyargs, 0, PyBytes_FromStringAndSize(buffer, size));
504   if (dataarg) {
505     Py_INCREF(dataarg);
506     PyTuple_SetItem(pyargs, 1, dataarg);
507   }
508
509   retval = PyObject_CallObject(func, pyargs);
510   Py_DECREF(pyargs);
511   if (PyErr_Occurred()) {
512     pygpgme_stash_callback_exception(self);
513     result = -1;
514     goto leave;
515   }
516
517   if (! PyLong_Check(retval)) {
518     PyErr_Format(PyExc_TypeError,
519                  "expected int from read callback, got %s",
520                  retval->ob_type->tp_name);
521     pygpgme_stash_callback_exception(self);
522     result = -1;
523     goto leave;
524   }
525
526   result = PyLong_AsSsize_t(retval);
527
528  leave:
529   Py_XDECREF(retval);
530   return result;
531 }
532
533 /* Set the current position from where the next read or write starts
534    in the data object with the handle HOOK to OFFSET, relativ to
535    WHENCE.  Returns the new offset in bytes from the beginning of the
536    data object.  */
537 static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
538 {
539   off_t result;
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) == 5 || PyTuple_Size(pyhook) == 6);
549
550   self = PyTuple_GetItem(pyhook, 0);
551   func = PyTuple_GetItem(pyhook, 3);
552   if (PyTuple_Size(pyhook) == 6) {
553     dataarg = PyTuple_GetItem(pyhook, 5);
554     pyargs = PyTuple_New(3);
555   } else {
556     pyargs = PyTuple_New(2);
557   }
558
559 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
560   PyTuple_SetItem(pyargs, 0, PyLong_FromLongLong((long long) offset));
561 #else
562   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) offset));
563 #endif
564   PyTuple_SetItem(pyargs, 1, PyLong_FromLong((long) whence));
565   if (dataarg) {
566     Py_INCREF(dataarg);
567     PyTuple_SetItem(pyargs, 2, dataarg);
568   }
569
570   retval = PyObject_CallObject(func, pyargs);
571   Py_DECREF(pyargs);
572   if (PyErr_Occurred()) {
573     pygpgme_stash_callback_exception(self);
574     result = -1;
575     goto leave;
576   }
577
578   if (! PyLong_Check(retval)) {
579     PyErr_Format(PyExc_TypeError,
580                  "expected int from read callback, got %s",
581                  retval->ob_type->tp_name);
582     pygpgme_stash_callback_exception(self);
583     result = -1;
584     goto leave;
585   }
586
587 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
588   result = PyLong_AsLongLong(retval);
589 #else
590   result = PyLong_AsLong(retval);
591 #endif
592
593  leave:
594   Py_XDECREF(retval);
595   return result;
596 }
597
598 /* Close the data object with the handle HOOK.  */
599 static void pyDataReleaseCb(void *hook)
600 {
601   PyObject *pyhook = (PyObject *) hook;
602   PyObject *self = NULL;
603   PyObject *func = NULL;
604   PyObject *dataarg = NULL;
605   PyObject *pyargs = NULL;
606   PyObject *retval = NULL;
607
608   assert (PyTuple_Check(pyhook));
609   assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
610
611   self = PyTuple_GetItem(pyhook, 0);
612   func = PyTuple_GetItem(pyhook, 4);
613   if (PyTuple_Size(pyhook) == 6) {
614     dataarg = PyTuple_GetItem(pyhook, 5);
615     pyargs = PyTuple_New(1);
616   } else {
617     pyargs = PyTuple_New(0);
618   }
619
620   if (dataarg) {
621     Py_INCREF(dataarg);
622     PyTuple_SetItem(pyargs, 0, dataarg);
623   }
624
625   retval = PyObject_CallObject(func, pyargs);
626   Py_XDECREF(retval);
627   Py_DECREF(pyargs);
628   if (PyErr_Occurred())
629     pygpgme_stash_callback_exception(self);
630 }
631
632 gpgme_error_t pygpgme_data_new_from_cbs(gpgme_data_t *r_data,
633                                         PyObject *pycbs,
634                                         PyObject **freelater)
635 {
636   static struct gpgme_data_cbs cbs = {
637     pyDataReadCb,
638     pyDataWriteCb,
639     pyDataSeekCb,
640     pyDataReleaseCb,
641   };
642
643   assert (PyTuple_Check(pycbs));
644   assert (PyTuple_Size(pycbs) == 5 || PyTuple_Size(pycbs) == 6);
645
646   Py_INCREF(pycbs);
647   *freelater = pycbs;
648
649   return gpgme_data_new_from_cbs(r_data, &cbs, (void *) pycbs);
650 }