python: Rename bindings.
[gpgme.git] / lang / python / pyme / core.py
1 # $Id$
2 # Copyright (C) 2004,2008 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 # import generators for portability with python2.2
20
21
22 from . import pygpgme
23 from .errors import errorcheck
24 from . import errors
25 from .util import GpgmeWrapper
26
27 class Context(GpgmeWrapper):
28     """From the GPGME C documentation:
29
30     * All cryptographic operations in GPGME are performed within a
31     * context, which contains the internal state of the operation as well as
32     * configuration parameters.  By using several contexts you can run
33     * several cryptographic operations in parallel, with different
34     * configuration.
35
36     Thus, this is the place that you will usually start."""
37
38     def _getctype(self):
39         return 'gpgme_ctx_t'
40     
41     def _getnameprepend(self):
42         return 'gpgme_'
43
44     def _errorcheck(self, name):
45         """This function should list all functions returning gpgme_error_t"""
46         if (name.startswith('gpgme_op_') and \
47             not name.endswith('_result')) or \
48             name == 'gpgme_signers_add' or \
49             name == 'gpgme_set_locale' or \
50             name == 'gpgme_set_keylist_mode' or \
51             name == 'gpgme_set_protocol':
52             return 1
53         return 0
54
55     def __init__(self, wrapped=None):
56         if wrapped:
57             self.wrapped = wrapped
58             self.own = False
59         else:
60             tmp = pygpgme.new_gpgme_ctx_t_p()
61             errorcheck(pygpgme.gpgme_new(tmp))
62             self.wrapped = pygpgme.gpgme_ctx_t_p_value(tmp)
63             pygpgme.delete_gpgme_ctx_t_p(tmp)
64             self.own = True
65         self.last_passcb = None
66         self.last_progresscb = None
67
68     def __del__(self):
69         self._free_passcb()
70         self._free_progresscb()
71         if self.own:
72             pygpgme.gpgme_release(self.wrapped)
73
74     def _free_passcb(self):
75         if self.last_passcb != None:
76             pygpgme.pygpgme_clear_generic_cb(self.last_passcb)
77             pygpgme.delete_PyObject_p_p(self.last_passcb)
78             self.last_passcb = None
79
80     def _free_progresscb(self):
81         if self.last_progresscb != None:
82             pygpgme.pygpgme_clear_generic_cb(self.last_progresscb)
83             pygpgme.delete_PyObject_p_p(self.last_progresscb)
84             self.last_progresscb = None
85
86     def op_keylist_all(self, *args, **kwargs):
87         self.op_keylist_start(*args, **kwargs)
88         key = self.op_keylist_next()
89         while key:
90             yield key
91             key = self.op_keylist_next()
92
93     def op_keylist_next(self):
94         """Returns the next key in the list created
95         by a call to op_keylist_start().  The object returned
96         is of type Key."""
97         ptr = pygpgme.new_gpgme_key_t_p()
98         try:
99             errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr))
100             key = pygpgme.gpgme_key_t_p_value(ptr)
101         except errors.GPGMEError as excp:
102             key = None
103             if excp.getcode() != errors.EOF:
104                 raise excp
105         pygpgme.delete_gpgme_key_t_p(ptr)
106         if key:
107             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
108             return key
109     
110     def get_key(self, fpr, secret):
111         """Return the key corresponding to the fingerprint 'fpr'"""
112         ptr = pygpgme.new_gpgme_key_t_p()
113         errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
114         key = pygpgme.gpgme_key_t_p_value(ptr)
115         pygpgme.delete_gpgme_key_t_p(ptr)
116         if key:
117             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
118             return key
119
120     def op_trustlist_all(self, *args, **kwargs):
121         self.op_trustlist_start(*args, **kwargs)
122         trust = self.ctx.op_trustlist_next()
123         while trust:
124             yield trust
125             trust = self.ctx.op_trustlist_next()
126
127     def op_trustlist_next(self):
128         """Returns the next trust item in the list created
129         by a call to op_trustlist_start().  The object returned
130         is of type TrustItem."""
131         ptr = pygpgme.new_gpgme_trust_item_t_p()
132         try:
133             errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
134             trust = pygpgme.gpgme_trust_item_t_p_value(ptr)
135         except errors.GPGMEError as excp:
136             trust = None
137             if excp.getcode() != errors.EOF:
138                 raise
139         pygpgme.delete_gpgme_trust_item_t_p(ptr)
140         return trust
141
142     def set_passphrase_cb(self, func, hook=None):
143         """Sets the passphrase callback to the function specified by func.
144
145         When the system needs a passphrase, it will call func with three args:
146         hint, a string describing the key it needs the passphrase for;
147         desc, a string describing the passphrase it needs;
148         prev_bad, a boolean equal True if this is a call made after
149         unsuccessful previous attempt.
150         
151         If hook has a value other than None it will be passed into the func
152         as a forth argument.
153
154         Please see the GPGME manual for more information.
155         """
156         self._free_passcb()
157         if func == None:
158             hookdata = None
159         else:
160             self.last_passcb = pygpgme.new_PyObject_p_p()
161             if hook == None:
162                 hookdata = func
163             else:
164                 hookdata = (func, hook)
165         pygpgme.pygpgme_set_passphrase_cb(self.wrapped, hookdata, self.last_passcb)
166
167     def set_progress_cb(self, func, hook=None):
168         """Sets the progress meter callback to the function specified by
169
170         This function will be called to provide an interactive update of
171         the system's progress.
172
173         Please see the GPGME manual for more information."""
174         self._free_progresscb()
175         if func == None:
176             hookdata = None
177         else:
178             self.last_progresscb = pygpgme.new_PyObject_p_p()
179             if hook == None:
180                 hookdata = func
181             else:
182                 hookdata = (func, hook)
183         pygpgme.pygpgme_set_progress_cb(self.wrapped, hookdata, self.last_progresscb)
184
185     def get_engine_info(self):
186         """Returns this context specific engine info"""
187         return pygpgme.gpgme_ctx_get_engine_info(self.wrapped)
188
189     def set_engine_info(self, proto, file_name, home_dir=None):
190         """Changes the configuration of the crypto engine implementing the
191     protocol 'proto' for the context. 'file_name' is the file name of
192     the executable program implementing this protocol. 'home_dir' is the
193     directory name of the configuration directory (engine's default is
194     used if omitted)."""
195         errorcheck(pygpgme.gpgme_ctx_set_engine_info(self.wrapped, proto, file_name, home_dir))
196
197     def wait(self, hang):
198         """Wait for asynchronous call to finish. Wait forever if hang is True
199
200         Return:
201             On an async call completion its return status.
202             On timeout - None.
203
204         Please read the GPGME manual for more information."""
205         ptr = pygpgme.new_gpgme_error_t_p()
206         context = pygpgme.gpgme_wait(self.wrapped, ptr, hang)
207         status = pygpgme.gpgme_error_t_p_value(ptr)
208         pygpgme.delete_gpgme_error_t_p(ptr)
209         
210         if context == None:
211             errorcheck(status)
212             return None
213         else:
214             return status
215
216     def op_edit(self, key, func, fnc_value, out):
217         """Start key editing using supplied callback function"""
218         if key == None:
219             raise ValueError("op_edit: First argument cannot be None")
220         opaquedata = (func, fnc_value)
221         errorcheck(pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out))
222     
223 class Data(GpgmeWrapper):
224     """From the GPGME C manual:
225
226 * A lot of data has to be exchanged between the user and the crypto
227 * engine, like plaintext messages, ciphertext, signatures and information
228 * about the keys.  The technical details about exchanging the data
229 * information are completely abstracted by GPGME.  The user provides and
230 * receives the data via `gpgme_data_t' objects, regardless of the
231 * communication protocol between GPGME and the crypto engine in use.
232
233         This Data class is the implementation of the GpgmeData objects.
234
235         Please see the information about __init__ for instantiation."""
236
237     def _getctype(self):
238         return 'gpgme_data_t'
239     
240     def _getnameprepend(self):
241         return 'gpgme_data_'
242
243     def _errorcheck(self, name):
244         """This function should list all functions returning gpgme_error_t"""
245         if name == 'gpgme_data_release_and_get_mem' or \
246                name == 'gpgme_data_get_encoding' or \
247                name == 'gpgme_data_seek':
248             return 0
249         return 1
250     
251     def __init__(self, string = None, file = None, offset = None,
252                  length = None, cbs = None):
253         """Initialize a new gpgme_data_t object.
254
255         If no args are specified, make it an empty object.
256
257         If string alone is specified, initialize it with the data
258         contained there.
259
260         If file, offset, and length are all specified, file must
261         be either a filename or a file-like object, and the object
262         will be initialized by reading the specified chunk from the file.
263
264         If cbs is specified, it MUST be a tuple of the form:
265
266         ((read_cb, write_cb, seek_cb, release_cb), hook)
267
268         where func is a callback function taking two arguments (count,
269         hook) and returning a string of read data, or None on EOF.
270         This will supply the read() method for the system.
271
272         If file is specified without any other arguments, then
273         it must be a filename, and the object will be initialized from
274         that file.
275
276         Any other use will result in undefined or erroneous behavior."""
277         self.wrapped = None
278         self.last_readcb = None
279
280         if cbs != None:
281             self.new_from_cbs(*cbs)
282         elif string != None:
283             self.new_from_mem(string)
284         elif file != None and offset != None and length != None:
285             self.new_from_filepart(file, offset, length)
286         elif file != None:
287             if type(file) == type("x"):
288                 self.new_from_file(file)
289             else:
290                 self.new_from_fd(file)
291         else:
292             self.new()
293
294     def __del__(self):
295         if self.wrapped != None:
296             pygpgme.gpgme_data_release(self.wrapped)
297         self._free_readcb()
298
299     def _free_readcb(self):
300         if self.last_readcb != None:
301             pygpgme.pygpgme_clear_generic_cb(self.last_readcb)
302             pygpgme.delete_PyObject_p_p(self.last_readcb)
303             self.last_readcb = None
304
305     def new(self):
306         tmp = pygpgme.new_gpgme_data_t_p()
307         errorcheck(pygpgme.gpgme_data_new(tmp))
308         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
309         pygpgme.delete_gpgme_data_t_p(tmp)
310
311     def new_from_mem(self, string, copy = 1):
312         tmp = pygpgme.new_gpgme_data_t_p()
313         errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
314         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
315         pygpgme.delete_gpgme_data_t_p(tmp)
316
317     def new_from_file(self, filename, copy = 1):
318         tmp = pygpgme.new_gpgme_data_t_p()
319         errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy))
320         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
321         pygpgme.delete_gpgme_data_t_p(tmp)
322
323     def new_from_cbs(self, funcs, hook):
324         """Argument funcs must be a 4 element tuple with callbacks:
325         (read_cb, write_cb, seek_cb, release_cb)"""
326         tmp = pygpgme.new_gpgme_data_t_p()
327         self._free_readcb()
328         self.last_readcb = pygpgme.new_PyObject_p_p()
329         hookdata = (funcs, hook)
330         pygpgme.pygpgme_data_new_from_cbs(tmp, hookdata, self.last_readcb)
331         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
332         pygpgme.delete_gpgme_data_t_p(tmp)
333
334     def new_from_filepart(self, file, offset, length):
335         """This wraps the GPGME gpgme_data_new_from_filepart() function.
336         The argument "file" may be:
337
338         1. a string specifying a file name, or
339         3. a a file-like object. supporting the fileno() call and the mode
340            attribute."""
341
342         tmp = pygpgme.new_gpgme_data_t_p()
343         filename = None
344         fp = None
345
346         if type(file) == type("x"):
347             filename = file
348         else:
349             fp = pygpgme.fdopen(file.fileno(), file.mode)
350             if fp == None:
351                 raise ValueError("Failed to open file from %s arg %s" % \
352                       (str(type(file)), str(file)))
353
354         errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
355                                                       offset, length))
356         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
357         pygpgme.delete_gpgme_data_t_p(tmp)
358
359     def new_from_fd(self, file):
360         """This wraps the GPGME gpgme_data_new_from_fd() function.
361         The argument "file" may be a file-like object, supporting the fileno()
362         call and the mode attribute."""
363         
364         tmp = pygpgme.new_gpgme_data_t_p()
365         fp = pygpgme.fdopen(file.fileno(), file.mode)
366         if fp == None:
367             raise ValueError("Failed to open file from %s arg %s" % \
368                   (str(type(file)), str(file)))
369         errorcheck(gpgme_data_new_from_fd(tmp, fp))
370         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
371         pygpgme.delete_gpgme_data_t_p(tmp)
372
373     def new_from_stream(self, file):
374         """This wrap around gpgme_data_new_from_stream is an alias for
375         new_from_fd() method since in python there's not difference
376         between file stream and file descriptor"""
377         self.new_from_fd(file)
378     
379     def write(self, buffer):
380         errorcheck(pygpgme.gpgme_data_write(self.wrapped, buffer, len(buffer)))
381
382     def read(self, size = -1):
383         """Read at most size bytes, returned as a string.
384         
385         If the size argument is negative or omitted, read until EOF is reached.
386
387         Returns the data read, or the empty string if there was no data
388         to read before EOF was reached."""
389         
390         if size == 0:
391             return ''
392
393         if size > 0:
394             return pygpgme.gpgme_data_read(self.wrapped, size)
395         else:
396             retval = ''
397             while 1:
398                 result = pygpgme.gpgme_data_read(self.wrapped, 10240)
399                 if len(result) == 0:
400                     break
401                 retval += result
402             return retval
403
404 def pubkey_algo_name(algo):
405     return pygpgme.gpgme_pubkey_algo_name(algo)
406
407 def hash_algo_name(algo):
408     return pygpgme.gpgme_hash_algo_name(algo)
409
410 def get_protocol_name(proto):
411     return pygpgme.gpgme_get_protocol_name(proto)
412
413 def check_version(version=None):
414     return pygpgme.gpgme_check_version(version)
415
416 def engine_check_version (proto):
417     try:
418         errorcheck(pygpgme.gpgme_engine_check_version(proto))
419         return True
420     except errors.GPGMEError:
421         return False
422
423 def get_engine_info():
424     ptr = pygpgme.new_gpgme_engine_info_t_p()
425     try:
426         errorcheck(pygpgme.gpgme_get_engine_info(ptr))
427         info = pygpgme.gpgme_engine_info_t_p_value(ptr)
428     except errors.GPGMEError:
429         info = None
430     pygpgme.delete_gpgme_engine_info_t_p(ptr)
431     return info
432
433 def set_engine_info(proto, file_name, home_dir=None):
434     """Changes the default configuration of the crypto engine implementing
435     the protocol 'proto'. 'file_name' is the file name of
436     the executable program implementing this protocol. 'home_dir' is the
437     directory name of the configuration directory (engine's default is
438     used if omitted)."""
439     errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir))
440
441 def set_locale(category, value):
442     """Sets the default locale used by contexts"""
443     errorcheck(pygpgme.gpgme_set_locale(None, category, value))
444
445 def wait(hang):
446     """Wait for asynchronous call on any Context  to finish.
447     Wait forever if hang is True.
448     
449     For finished anynch calls it returns a tuple (status, context):
450         status  - status return by asnynchronous call.
451         context - context which caused this call to return.
452         
453     Please read the GPGME manual of more information."""
454     ptr = pygpgme.new_gpgme_error_t_p()
455     context = pygpgme.gpgme_wait(None, ptr, hang)
456     status = pygpgme.gpgme_error_t_p_value(ptr)
457     pygpgme.delete_gpgme_error_t_p(ptr)
458     if context == None:
459         errorcheck(status)
460     else:
461         context = Context(context)
462     return (status, context)
463