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