e3055741c9237609623866c6ce960074a275eb73
[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 *self)
68 {
69   PyObject *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   PyObject_SetAttrString(self, EXCINFO, excinfo);
90 }
91
92 PyObject *pygpgme_raise_callback_exception(PyObject *self)
93 {
94   PyObject *ptype, *pvalue, *ptraceback, *excinfo;
95
96   if (! PyObject_HasAttrString(self, EXCINFO))
97     goto leave;
98
99   excinfo = PyObject_GetAttrString(self, EXCINFO);
100   if (! PyTuple_Check(excinfo))
101     {
102       Py_DECREF(excinfo);
103       goto leave;
104     }
105
106   ptype = PyTuple_GetItem(excinfo, 0);
107   Py_INCREF(excinfo);
108
109   pvalue = PyTuple_GetItem(excinfo, 1);
110   if (pvalue == Py_None)
111     pvalue = NULL;
112   else
113     Py_INCREF(pvalue);
114
115   ptraceback = PyTuple_GetItem(excinfo, 2);
116   if (ptraceback == Py_None)
117     ptraceback = NULL;
118   else
119     Py_INCREF(ptraceback);
120
121   Py_DECREF(excinfo);
122   PyErr_Restore(ptype, pvalue, ptraceback);
123
124   Py_INCREF(Py_None);
125   PyObject_SetAttrString(self, EXCINFO, Py_None);
126
127   return NULL; /* Raise exception.  */
128
129  leave:
130   Py_INCREF(Py_None);
131   return Py_None;
132 }
133 #undef EXCINFO
134
135 static gpgme_error_t pyPassphraseCb(void *hook,
136                                     const char *uid_hint,
137                                     const char *passphrase_info,
138                                     int prev_was_bad,
139                                     int fd) {
140   PyObject *pyhook = (PyObject *) hook;
141   PyObject *self = NULL;
142   PyObject *func = NULL;
143   PyObject *args = NULL;
144   PyObject *retval = NULL;
145   PyObject *dataarg = NULL;
146   gpgme_error_t err_status = 0;
147
148   pygpgme_exception_init();
149
150   assert (PyTuple_Check(pyhook));
151   self = PyTuple_GetItem(pyhook, 0);
152   func = PyTuple_GetItem(pyhook, 1);
153   if (PyTuple_Size(pyhook) == 3) {
154     dataarg = PyTuple_GetItem(pyhook, 2);
155     args = PyTuple_New(4);
156   } else {
157     args = PyTuple_New(3);
158   }
159
160   if (uid_hint == NULL)
161     {
162       Py_INCREF(Py_None);
163       PyTuple_SetItem(args, 0, Py_None);
164     }
165   else
166     PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
167                                                   "strict"));
168   if (PyErr_Occurred()) {
169     Py_DECREF(args);
170     err_status = gpg_error(GPG_ERR_GENERAL);
171     goto leave;
172   }
173
174   PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
175   PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
176   if (dataarg) {
177     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
178     PyTuple_SetItem(args, 3, dataarg);
179   }
180
181   retval = PyObject_CallObject(func, args);
182   Py_DECREF(args);
183   if (PyErr_Occurred()) {
184     err_status = pygpgme_exception2code();
185   } else {
186     if (!retval) {
187       write(fd, "\n", 1);
188     } else {
189       char *buf;
190       size_t len;
191       if (PyBytes_Check(retval))
192         buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
193       else if (PyUnicode_Check(retval))
194         buf = PyUnicode_AsUTF8AndSize(retval, &len);
195       else
196         {
197           PyErr_Format(PyExc_TypeError,
198                        "expected str or bytes from passphrase callback, got %s",
199                        retval->ob_type->tp_name);
200           err_status = gpg_error(GPG_ERR_GENERAL);
201           goto leave;
202         }
203
204       write(fd, buf, len);
205       write(fd, "\n", 1);
206       Py_DECREF(retval);
207     }
208   }
209
210  leave:
211   if (err_status)
212     pygpgme_stash_callback_exception(self);
213
214   return err_status;
215 }
216
217 void pygpgme_set_passphrase_cb(gpgme_ctx_t ctx, PyObject *cb,
218                                PyObject **freelater) {
219   if (cb == Py_None) {
220     gpgme_set_passphrase_cb(ctx, NULL, NULL);
221     return;
222   }
223   Py_INCREF(cb);
224   *freelater = cb;
225   gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t)pyPassphraseCb, (void *) cb);
226 }
227
228 static void pyProgressCb(void *hook, const char *what, int type, int current,
229                          int total) {
230   PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
231   PyObject *pyhook = (PyObject *) hook;
232   PyObject *self = NULL;
233
234   assert (PyTuple_Check(pyhook));
235   self = PyTuple_GetItem(pyhook, 0);
236   func = PyTuple_GetItem(pyhook, 1);
237   if (PyTuple_Size(pyhook) == 3) {
238     dataarg = PyTuple_GetItem(pyhook, 1);
239     args = PyTuple_New(5);
240   } else {
241     args = PyTuple_New(4);
242   }
243
244   PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
245                                                 "strict"));
246   if (PyErr_Occurred()) {
247     pygpgme_stash_callback_exception(self);
248     Py_DECREF(args);
249     return;
250   }
251   PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
252   PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
253   PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
254   if (dataarg) {
255     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
256     PyTuple_SetItem(args, 4, dataarg);
257   }
258
259   retval = PyObject_CallObject(func, args);
260   if (PyErr_Occurred())
261     pygpgme_stash_callback_exception(self);
262   Py_DECREF(args);
263   Py_XDECREF(retval);
264 }
265
266 void pygpgme_set_progress_cb(gpgme_ctx_t ctx, PyObject *cb, PyObject **freelater){
267   if (cb == Py_None) {
268     gpgme_set_progress_cb(ctx, NULL, NULL);
269     return;
270   }
271   Py_INCREF(cb);
272   *freelater = cb;
273   gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
274 }
275
276 \f
277 /* Edit callbacks.  */
278 gpgme_error_t pyEditCb(void *opaque, gpgme_status_code_t status,
279                        const char *args, int fd) {
280   PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
281   PyObject *pyopaque = (PyObject *) opaque;
282   gpgme_error_t err_status = 0;
283
284   pygpgme_exception_init();
285
286   if (PyTuple_Check(pyopaque)) {
287     func = PyTuple_GetItem(pyopaque, 0);
288     dataarg = PyTuple_GetItem(pyopaque, 1);
289     pyargs = PyTuple_New(3);
290   } else {
291     func = pyopaque;
292     pyargs = PyTuple_New(2);
293   }
294
295   PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) status));
296   PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
297   if (dataarg) {
298     Py_INCREF(dataarg);         /* Because GetItem doesn't give a ref but SetItem taketh away */
299     PyTuple_SetItem(pyargs, 2, dataarg);
300   }
301
302   retval = PyObject_CallObject(func, pyargs);
303   Py_DECREF(pyargs);
304   if (PyErr_Occurred()) {
305     err_status = pygpgme_exception2code();
306   } else {
307     if (fd>=0 && retval && PyUnicode_Check(retval)) {
308       const char *buffer;
309       Py_ssize_t size;
310
311       buffer = PyUnicode_AsUTF8AndSize(retval, &size);
312       write(fd, buffer, size);
313       write(fd, "\n", 1);
314     }
315   }
316
317   Py_XDECREF(retval);
318   return err_status;
319 }