assuan/
[gpgme.git] / assuan / assuan-inquire.c
1 /* assuan-inquire.c - handle inquire stuff
2  * Copyright (C) 2001, 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
3  *
4  * This file is part of Assuan.
5  *
6  * Assuan is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Assuan is distributed in the hope that it will be useful, but
12  * 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 program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include "assuan-defs.h"
26
27 #define digitp(a) ((a) >= '0' && (a) <= '9')
28 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
29                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
30 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
31
32
33 struct membuf
34 {
35   size_t len;
36   size_t size;
37   char *buf;
38   int out_of_core;
39   int too_large;
40   size_t maxlen;
41 };
42
43
44 \f
45 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
46    create a buffer, put_membuf to append bytes and get_membuf to
47    release and return the buffer.  Allocation errors are detected but
48    only returned at the final get_membuf(), this helps not to clutter
49    the code with out of core checks.  */
50
51 static void
52 init_membuf (struct membuf *mb, int initiallen, size_t maxlen)
53 {
54   mb->len = 0;
55   mb->size = initiallen;
56   mb->out_of_core = 0;
57   mb->too_large = 0;
58   mb->maxlen = maxlen;
59   /* we need to allocate one byte more for get_membuf */
60   mb->buf = xtrymalloc (initiallen+1);
61   if (!mb->buf)
62       mb->out_of_core = 1;
63 }
64
65 static void
66 put_membuf (struct membuf *mb, const void *buf, size_t len)
67 {
68   if (mb->out_of_core || mb->too_large)
69     return;
70
71   if (mb->maxlen && mb->len + len > mb->maxlen)
72     {
73       mb->too_large = 1;
74       return;
75     }
76
77   if (mb->len + len >= mb->size)
78     {
79       char *p;
80       
81       mb->size += len + 1024;
82       /* we need to allocate one byte more for get_membuf */
83       p = xtryrealloc (mb->buf, mb->size+1);
84       if (!p)
85         {
86           mb->out_of_core = 1;
87           return;
88         }
89       mb->buf = p;
90     }
91   memcpy (mb->buf + mb->len, buf, len);
92   mb->len += len;
93 }
94
95 static void *
96 get_membuf (struct membuf *mb, size_t *len)
97 {
98   char *p;
99
100   if (mb->out_of_core || mb->too_large)
101     {
102       xfree (mb->buf);
103       mb->buf = NULL;
104       return NULL;
105     }
106
107   mb->buf[mb->len] = 0; /* there is enough space for the hidden eos */
108   p = mb->buf;
109   *len = mb->len;
110   mb->buf = NULL;
111   mb->out_of_core = 1; /* don't allow a reuse */
112   return p;
113 }
114
115 static void
116 free_membuf (struct membuf *mb)
117 {
118   xfree (mb->buf);
119   mb->buf = NULL;
120 }
121
122
123 /**
124  * assuan_inquire:
125  * @ctx: An assuan context
126  * @keyword: The keyword used for the inquire
127  * @r_buffer: Returns an allocated buffer
128  * @r_length: Returns the length of this buffer
129  * @maxlen: If not 0, the size limit of the inquired data.
130  * 
131  * A Server may use this to Send an inquire.  r_buffer, r_length and
132  * maxlen may all be NULL/0 to indicate that no real data is expected.
133  * 
134  * Return value: 0 on success or an ASSUAN error code
135  **/
136 assuan_error_t
137 assuan_inquire (assuan_context_t ctx, const char *keyword,
138                 unsigned char **r_buffer, size_t *r_length, size_t maxlen)
139 {
140   assuan_error_t rc;
141   struct membuf mb;
142   char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */
143   unsigned char *line, *p;
144   int linelen;
145   int nodataexpected;
146
147   if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
148     return _assuan_error (ASSUAN_Invalid_Value);
149   nodataexpected = !r_buffer && !r_length && !maxlen;
150   if (!nodataexpected && (!r_buffer || !r_length))
151     return _assuan_error (ASSUAN_Invalid_Value);
152   if (!ctx->is_server)
153     return _assuan_error (ASSUAN_Not_A_Server);
154   if (ctx->in_inquire)
155     return _assuan_error (ASSUAN_Nested_Commands);
156   
157   ctx->in_inquire = 1;
158   if (nodataexpected)
159     memset (&mb, 0, sizeof mb); /* avoid compiler warnings */
160   else
161     init_membuf (&mb, maxlen? maxlen:1024, maxlen);
162
163   strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
164   rc = assuan_write_line (ctx, cmdbuf);
165   if (rc)
166     goto leave;
167
168   for (;;)
169     {
170       do 
171         {
172           do
173             rc = _assuan_read_line (ctx);
174           while (_assuan_error_is_eagain (rc));
175           if (rc)
176             goto leave;
177           line = (unsigned char *) ctx->inbound.line;
178           linelen = ctx->inbound.linelen;
179         }    
180       while (*line == '#' || !linelen);
181       if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
182           && (!line[3] || line[3] == ' '))
183         break; /* END command received*/
184       if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
185         {
186           rc = _assuan_error (ASSUAN_Canceled);
187           goto leave;
188         }
189       if (line[0] != 'D' || line[1] != ' ' || nodataexpected)
190         {
191           rc = _assuan_error (ASSUAN_Unexpected_Command);
192           goto leave;
193         }
194       if (linelen < 3)
195         continue;
196       line += 2;
197       linelen -= 2;
198
199       p = line;
200       while (linelen)
201         {
202           for (;linelen && *p != '%'; linelen--, p++)
203             ;
204           put_membuf (&mb, line, p-line);
205           if (linelen > 2)
206             { /* handle escaping */
207               unsigned char tmp[1];
208               p++;
209               *tmp = xtoi_2 (p);
210               p += 2;
211               linelen -= 3;
212               put_membuf (&mb, tmp, 1);
213             }
214           line = p;
215         }
216       if (mb.too_large)
217         {
218           rc = _assuan_error (ASSUAN_Too_Much_Data);
219           goto leave;
220         }
221     }
222
223   if (!nodataexpected)
224     {
225       *r_buffer = get_membuf (&mb, r_length);
226       if (!*r_buffer)
227         rc = _assuan_error (ASSUAN_Out_Of_Core);
228     }
229
230  leave:
231   if (!nodataexpected)
232     free_membuf (&mb);
233   ctx->in_inquire = 0;
234   return rc;
235 }
236
237 \f
238 void
239 _assuan_inquire_release (assuan_context_t ctx)
240 {
241   if (ctx->in_inquire)
242     {
243       if (ctx->inquire_membuf)
244         {
245           free_membuf (ctx->inquire_membuf);
246           free (ctx->inquire_membuf);
247         }
248       ctx->in_inquire = 0;
249     }
250 }
251
252
253 int
254 _assuan_inquire_ext_cb (assuan_context_t ctx)
255 {
256   int rc;
257   unsigned char *line;
258   int linelen;
259   struct membuf *mb;
260   unsigned char *p;
261
262   line = (unsigned char *) ctx->inbound.line;
263   linelen = ctx->inbound.linelen;
264   mb = ctx->inquire_membuf;
265
266   if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
267     {
268       rc = _assuan_error (ASSUAN_Canceled);
269       goto leave;
270     }
271   if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
272       && (!line[3] || line[3] == ' '))
273     {
274       rc = 0;
275       goto leave;
276     }
277
278   if (line[0] != 'D' || line[1] != ' ' || mb == NULL)
279     {
280       rc = _assuan_error (ASSUAN_Unexpected_Command);
281       goto leave;
282     }
283   
284   if (linelen < 3)
285     return 0;
286   line += 2;
287   linelen -= 2;
288   
289   p = line;
290   while (linelen)
291     {
292       for (;linelen && *p != '%'; linelen--, p++)
293         ;
294       put_membuf (mb, line, p-line);
295       if (linelen > 2)
296         { /* handle escaping */
297           unsigned char tmp[1];
298           p++;
299           *tmp = xtoi_2 (p);
300           p += 2;
301           linelen -= 3;
302           put_membuf (mb, tmp, 1);
303         }
304       line = p;
305     }
306   if (mb->too_large)
307     {
308       rc = _assuan_error (ASSUAN_Too_Much_Data);
309       goto leave;
310     }
311
312   return 0;
313
314  leave:
315   {
316     size_t buf_len = 0;
317     unsigned char *buf = NULL;
318
319     if (mb)
320       {
321         buf = get_membuf (mb, &buf_len);
322         if (!buf)
323           rc = _assuan_error (ASSUAN_Out_Of_Core);
324         free_membuf (mb);
325         free (mb);
326         ctx->inquire_membuf = NULL;
327       }
328     ctx->in_inquire = 0;
329     rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len);
330   }
331   return rc;
332 }
333
334 /**
335  * assuan_inquire_ext:
336  * @ctx: An assuan context
337  * @keyword: The keyword used for the inquire
338  * @maxlen: If not 0, the size limit of the inquired data.
339  * @cb: A callback handler which is invoked after the operation completed.
340  * @cb_data: A user-provided value passed to the callback handler.
341  * 
342  * A Server may use this to Send an inquire.  r_buffer, r_length and
343  * maxlen may all be NULL/0 to indicate that no real data is expected.
344  * When this function returns, 
345  *
346  * Return value: 0 on success or an ASSUAN error code
347  **/
348 assuan_error_t
349 assuan_inquire_ext (assuan_context_t ctx, const char *keyword, size_t maxlen,
350                     int (*cb) (void *cb_data, int rc, unsigned char *buf,
351                                size_t len),
352                     void *cb_data)
353 {
354   assuan_error_t rc;
355   struct membuf *mb = NULL;
356   char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */
357
358   if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
359     return _assuan_error (ASSUAN_Invalid_Value);
360   if (!ctx->is_server)
361     return _assuan_error (ASSUAN_Not_A_Server);
362   if (ctx->in_inquire)
363     return _assuan_error (ASSUAN_Nested_Commands);
364
365   mb = malloc (sizeof (struct membuf));
366   if (!mb)
367     return _assuan_error (ASSUAN_Out_Of_Core);
368   init_membuf (mb, maxlen ? maxlen : 1024, maxlen);
369
370   strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
371   rc = assuan_write_line (ctx, cmdbuf);
372   if (rc)
373     {
374       free_membuf (mb); 
375       free (mb);
376       return rc;
377     }
378
379   ctx->in_inquire = 1;
380
381   /* Set up the continuation.  */
382   ctx->inquire_cb = cb;
383   ctx->inquire_cb_data = cb_data;
384   ctx->inquire_membuf = mb;
385
386   return 0;
387 }