dc6117fa6b8ddc7a8ccd139e54908204624bb0d8
[gpgme.git] / gpgme / data.c
1 /* data.c
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30
31 #include "util.h"
32 #include "context.h"
33 #include "ops.h"
34
35 #define ALLOC_CHUNK 1024
36 #define my_isdigit(a)  ( (a) >='0' && (a) <= '9' )
37 #define my_isxdigit(a) ( my_isdigit((a))               \
38                          || ((a) >= 'A' && (a) <= 'F') \
39                          || ((a) >= 'f' && (a) <= 'f') )
40
41
42 /**
43  * gpgme_data_new:
44  * @r_dh: returns the new data object 
45  * 
46  * Create a new data object without any content. 
47  * 
48  * Return value: An error value or 0 on success
49  **/
50 GpgmeError
51 gpgme_data_new ( GpgmeData *r_dh )
52 {
53     GpgmeData dh;
54
55     if (!r_dh)
56         return mk_error (Invalid_Value);
57     *r_dh = NULL;
58     dh = xtrycalloc ( 1, sizeof *dh );
59     if (!dh)
60         return mk_error (Out_Of_Core);
61     dh->mode = GPGME_DATA_MODE_INOUT; 
62     *r_dh = dh;
63     return 0;
64 }
65
66
67 /**
68  * gpgme_data_new_from_mem:
69  * @r_dh:   Returns a new data object.
70  * @buffer: Initialize with this.
71  * @size: Size of the buffer
72  * @copy: Flag wether a copy of the buffer should be used.
73  * 
74  * Create a new data object and initialize with data
75  * from the memory.  A @copy with value %TRUE creates a copy of the
76  * memory, a value of %FALSE uses the original memory of @buffer and the
77  * caller has to make sure that this buffer is valid until gpgme_release_data()
78  * is called.
79  * 
80  * Return value: 
81  **/
82 GpgmeError
83 gpgme_data_new_from_mem ( GpgmeData *r_dh,
84                           const char *buffer, size_t size, int copy )
85 {
86     GpgmeData dh;
87     GpgmeError err;
88
89     if (!r_dh || !buffer)
90         return mk_error (Invalid_Value);
91     *r_dh = NULL;
92     err = gpgme_data_new ( &dh );
93     if (err)
94         return err;
95     dh->len  = size;
96     if (copy) {
97         dh->private_buffer = xtrymalloc ( size );
98         if ( !dh->private_buffer ) {
99             gpgme_data_release (dh);
100             return mk_error (Out_Of_Core);
101         }
102         dh->private_len = size;
103         memcpy (dh->private_buffer, buffer, size );
104         dh->data = dh->private_buffer;
105         dh->writepos = size;
106     }
107     else {
108         dh->data = buffer;
109     }
110     dh->type = GPGME_DATA_TYPE_MEM;
111     
112     *r_dh = dh;
113     return 0;
114 }
115
116 /**
117  * gpgme_data_new_from_file:
118  * @r_dh: returns the new data object
119  * @fname: filename
120  * @copy: Flag, whether the file should be copied.
121  * 
122  * Create a new data object and initialize it with the content of 
123  * the file @file.  If @copy is %True the file is immediately read in
124  * adn closed.  @copy of %False is not yet supportted.
125  * 
126  * Return value: An error code or 0 on success. If the error code is
127  * %GPGME_File_Error, the OS error code is held in %errno.
128  **/
129 GpgmeError
130 gpgme_data_new_from_file ( GpgmeData *r_dh, const char *fname, int copy )
131 {
132     GpgmeData dh;
133     GpgmeError err;
134     struct stat st;
135     FILE *fp;
136
137     if (!r_dh)
138         return mk_error (Invalid_Value);
139     *r_dh = NULL;
140     /* We only support copy for now - in future we might want to honor the 
141      * copy flag and just store a file pointer */
142     if (!copy)
143         return mk_error (Not_Implemented);
144     if (!fname)
145         return mk_error (Invalid_Value);
146
147     err = gpgme_data_new ( &dh );
148     if (err)
149         return err;
150
151     fp = fopen (fname, "rb");
152     if (!fp) {
153         int save_errno = errno;
154         gpgme_data_release (dh);
155         errno = save_errno;
156         return mk_error (File_Error);
157     }
158
159     if( fstat(fileno(fp), &st) ) {
160         int save_errno = errno;
161         fclose (fp);
162         gpgme_data_release (dh);
163         errno = save_errno;
164         return mk_error (File_Error);
165     }
166
167     /* We should check the length of the file and don't allow for to
168      * large files */
169     dh->private_buffer = xtrymalloc ( st.st_size );
170     if ( !dh->private_buffer ) {
171         fclose (fp);
172         gpgme_data_release (dh);
173         return mk_error (Out_Of_Core);
174     }
175     dh->private_len = st.st_size;
176
177     if ( fread ( dh->private_buffer, dh->private_len, 1, fp ) != 1 ) {
178         int save_errno = errno;
179         fclose (fp);
180         gpgme_data_release (dh);
181         errno = save_errno;
182         return mk_error (File_Error);
183     }
184
185     fclose (fp);
186
187     dh->len = dh->private_len;
188     dh->data = dh->private_buffer;
189     dh->writepos = dh->len;
190     dh->type = GPGME_DATA_TYPE_MEM;
191     
192     *r_dh = dh;
193     return 0;
194 }
195
196
197
198 /**
199  * gpgme_data_release:
200  * @dh: Data object 
201  * 
202  * Release the data object @dh.  @dh may be NULL in which case nothing
203  * happens.
204  **/
205 void
206 gpgme_data_release ( GpgmeData dh )
207 {
208     if (dh) {
209         xfree (dh->private_buffer); 
210         xfree (dh);
211     }
212 }
213
214 char *
215 _gpgme_data_release_and_return_string ( GpgmeData dh )
216 {
217     char *val = NULL;
218
219     if (dh) {
220         if ( _gpgme_data_append ( dh, "", 0 ) ) /* append EOS */
221             xfree (dh->private_buffer );
222         else {
223             val = dh->private_buffer;
224             if ( !val && dh->data ) {
225                 val = xtrymalloc ( dh->len );
226                 if ( val )
227                     memcpy ( val, dh->data, dh->len );
228             }
229         }
230         xfree (dh);
231     }
232     return val;
233 }
234
235 /**
236  * gpgme_data_release_and_get_mem:
237  * @dh: the data object
238  * @r_len: returns the length of the memory
239  * 
240  * Release the data object @dh and return its content and the length of
241  * that content.  The caller has to free this data.  @dh maybe NULL in
242  * which case NULL is returned.  I there is not enough memory for allocating
243  * the return value, NULL is returned and the object is released.
244  * 
245  * Return value: a pointer to an allocated buffer of length @r_len.
246  **/
247 char *
248 gpgme_data_release_and_get_mem ( GpgmeData dh, size_t *r_len )
249 {
250     char *val = NULL;
251
252     if (r_len)
253         *r_len = 0;
254     if (dh) {
255         size_t len = dh->len;
256         val = dh->private_buffer;
257         if ( !val && dh->data ) {
258             val = xtrymalloc ( len );
259             if ( val )
260                 memcpy ( val, dh->data, len );
261         }
262         xfree (dh);
263         if (val && r_len )
264             *r_len = len;
265     }
266     return val;
267 }
268
269
270 /**
271  * gpgme_data_get_type:
272  * @dh: the data object
273  * 
274  * Get the type of the data object.
275  * Data types are prefixed with %GPGME_DATA_TYPE_
276  * 
277  * Return value: the data type
278  **/
279 GpgmeDataType
280 gpgme_data_get_type ( GpgmeData dh )
281 {
282     if ( !dh || !dh->data )
283         return GPGME_DATA_TYPE_NONE;
284             
285     return dh->type;
286 }
287
288 void 
289 _gpgme_data_set_mode ( GpgmeData dh, GpgmeDataMode mode )
290 {
291     assert (dh);
292     dh->mode = mode;
293 }
294
295
296 GpgmeDataMode
297 _gpgme_data_get_mode ( GpgmeData dh )
298 {
299     assert (dh);
300     return dh->mode;
301 }
302
303 /**
304  * gpgme_data_rewind:
305  * @dh: the data object 
306  * 
307  * Prepare the data object in a way, that a gpgme_data_read() does start
308  * at the beginning of the data.  This has to be done for all types
309  * of data objects.
310  * 
311  * Return value: An error code or 0 on success
312  **/
313 GpgmeError
314 gpgme_data_rewind ( GpgmeData dh )
315 {
316     if ( !dh )
317         return mk_error (Invalid_Value);
318     /* Fixme: We should check whether rewinding does make sense for the
319      * data type */
320     dh->readpos = 0;
321     return 0;
322 }
323
324 /**
325  * gpgme_data_read:
326  * @dh: the data object
327  * @buffer: A buffer 
328  * @length: The length of that bufer
329  * @nread: Returns the number of bytes actually read.
330  * 
331  * Copy data from the current read position (which may be set by
332  * gpgme_data_rewind()) to the supplied @buffer, max. @length bytes
333  * are copied and the actual number of bytes are returned in @nread.
334  * If there are no more bytes available %GPGME_EOF is returned and @nread
335  * is set to 0.
336  * 
337  * Return value: An errocodee or 0 on success, EOF is indcated by the
338  * error code GPGME_EOF.
339  **/
340 GpgmeError
341 gpgme_data_read ( GpgmeData dh, char *buffer, size_t length, size_t *nread )
342 {
343     size_t nbytes;
344
345     if ( !dh )
346         return mk_error (Invalid_Value);
347     nbytes = dh->len - dh->readpos;
348     if ( !nbytes ) {
349         *nread = 0;
350         return mk_error(EOF);
351     }
352     if (nbytes > length)
353         nbytes = length;
354     memcpy ( buffer, dh->data + dh->readpos, nbytes );
355     *nread = nbytes;
356     dh->readpos += nbytes;
357     return 0;
358
359
360 /* 
361  * This function does make sense when we know that it contains no nil chars.
362  */
363 char *
364 _gpgme_data_get_as_string ( GpgmeData dh )
365 {
366     char *val = NULL;
367
368     if (dh) {
369         val = xtrymalloc ( dh->len+1 );
370         if ( val ) {
371             memcpy ( val, dh->data, dh->len );
372             val[dh->len] = 0;
373         }
374     }
375     return val;
376 }
377
378
379
380 GpgmeError
381 _gpgme_data_append ( GpgmeData dh, const char *buffer, size_t length )
382 {
383     assert (dh);
384
385     if ( dh->type == GPGME_DATA_TYPE_NONE ) {
386         /* convert it to a mem data type */
387         assert (!dh->private_buffer);
388         dh->type = GPGME_DATA_TYPE_MEM;
389         dh->private_len = length < ALLOC_CHUNK? ALLOC_CHUNK : length;
390         dh->private_buffer = xtrymalloc ( dh->private_len );
391         if (!dh->private_buffer) {
392             dh->private_len = 0;
393             return mk_error (Out_Of_Core);
394         }
395         dh->writepos = 0;
396         dh->data = dh->private_buffer;
397     }
398     else if ( dh->type != GPGME_DATA_TYPE_MEM ) 
399         return mk_error (Invalid_Type);
400     
401     if ( dh->mode != GPGME_DATA_MODE_INOUT 
402          && dh->mode != GPGME_DATA_MODE_IN  )
403         return mk_error (Invalid_Mode);
404
405     if ( !dh->private_buffer ) {
406         /* we have to copy it now */
407         assert (dh->data);
408         dh->private_len = dh->len+length;
409         if (dh->private_len < ALLOC_CHUNK)
410             dh->private_len = ALLOC_CHUNK;
411         dh->private_buffer = xtrymalloc ( dh->private_len );
412         if (!dh->private_buffer) {
413             dh->private_len = 0;
414             return mk_error (Out_Of_Core);
415         }
416         memcpy ( dh->private_buffer, dh->data, dh->len );
417         dh->writepos = dh->len;
418         dh->data = dh->private_buffer;
419     }
420
421     /* allocate more memory if needed */
422     if ( dh->writepos + length > dh->private_len ) {
423         char *p;
424         size_t newlen = dh->private_len
425                         + (dh->len < ALLOC_CHUNK? ALLOC_CHUNK : length);
426         p = xtryrealloc ( dh->private_buffer, newlen );
427         if ( !p ) 
428             return mk_error (Out_Of_Core);
429         dh->private_buffer = p;
430         dh->private_len = newlen;
431         dh->data = dh->private_buffer;
432         assert ( !(dh->writepos + length > dh->private_len) );      
433     }
434
435     memcpy ( dh->private_buffer + dh->writepos, buffer, length );
436     dh->writepos += length;
437     dh->len += length;
438
439     return 0;
440 }
441
442 GpgmeError
443 _gpgme_data_append_string ( GpgmeData dh, const char *s )
444 {
445     return _gpgme_data_append ( dh, s, s? strlen(s):0 );
446 }
447
448
449 GpgmeError
450 _gpgme_data_append_for_xml ( GpgmeData dh,
451                              const char *buffer, size_t len )
452 {
453     const char *text, *s;
454     size_t n;
455     int rc = 0; 
456        
457     if ( !dh || !buffer )
458         return mk_error (Invalid_Value);
459
460     do {
461         for (text=NULL, s=buffer, n=len; n && !text; s++, n-- ) {
462             if ( *s == '<' ) 
463                 text = "&lt;";
464             else if ( *s == '>' ) 
465                 text = "&gt;";  /* not sure whether this is really needed */
466             else if ( *s == '&' ) 
467                 text = "&amp;";
468             else if ( !*s )
469                 text = "&#00;";
470         }
471         if (text) {
472             s--; n++;
473         }
474         if (s != buffer) 
475             rc = _gpgme_data_append ( dh, buffer, s-buffer );
476         if ( !rc && text) {
477             rc = _gpgme_data_append_string ( dh, text );
478             s++; n--;
479         }
480         buffer = s;
481         len = n;
482     } while ( !rc && len );
483     return rc;
484 }
485
486
487 /*
488  * Append a string to DATA and convert it so that the result will be 
489  * valid XML. 
490  */
491 GpgmeError
492 _gpgme_data_append_string_for_xml ( GpgmeData dh, const char *string )
493 {
494     return _gpgme_data_append_for_xml ( dh, string, strlen (string) );
495 }
496
497
498 static int
499 hextobyte( const byte *s )
500 {
501     int c;
502
503     if( *s >= '0' && *s <= '9' )
504         c = 16 * (*s - '0');
505     else if( *s >= 'A' && *s <= 'F' )
506         c = 16 * (10 + *s - 'A');
507     else if( *s >= 'a' && *s <= 'f' )
508         c = 16 * (10 + *s - 'a');
509     else
510         return -1;
511     s++;
512     if( *s >= '0' && *s <= '9' )
513         c += *s - '0';
514     else if( *s >= 'A' && *s <= 'F' )
515         c += 10 + *s - 'A';
516     else if( *s >= 'a' && *s <= 'f' )
517         c += 10 + *s - 'a';
518     else
519         return -1;
520     return c;
521 }
522
523
524
525
526 /* 
527  * Append a string with percent style (%XX) escape characters as XML
528  */
529 GpgmeError
530 _gpgme_data_append_percentstring_for_xml ( GpgmeData dh, const char *string )
531 {
532     const byte *s;
533     byte *buf, *d;
534     int val;
535     GpgmeError err;
536
537     d = buf = xtrymalloc ( strlen (string) );
538     for (s=string; *s; s++ ) {
539         if ( *s == '%' && (val=hextobyte (s+1)) != -1 ) {
540             *d++ = val;
541             s += 2;
542         }
543         else
544             *d++ = *s;
545     }
546
547     err = _gpgme_data_append_for_xml ( dh, buf, d - buf );
548     xfree (buf);
549     return err;
550 }
551
552
553
554
555
556
557