d8c52d09ae7536352a20a31f9eeeefc13c6decb4
[gpgme.git] / assuan / assuan-inquire.c
1 /* assuan-inquire.c - handle inquire stuff
2  *      Copyright (C) 2001, 2002, 2003, 2005  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, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA. 
20  */
21
22 #include <config.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "assuan-defs.h"
28
29 #define digitp(a) ((a) >= '0' && (a) <= '9')
30 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
31                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
32 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
33
34
35 struct membuf
36 {
37   size_t len;
38   size_t size;
39   char *buf;
40   int out_of_core;
41   int too_large;
42   size_t maxlen;
43 };
44
45
46 \f
47 /* A simple implemnation of a dynamic buffer.  Use init_membuf() to
48    create a buffer, put_membuf to append bytes and get_membuf to
49    release and return the buffer.  Allocation errors are detected but
50    only returned at the final get_membuf(), this helps not to clutter
51    the code with out of core checks.  */
52
53 static void
54 init_membuf (struct membuf *mb, int initiallen, size_t maxlen)
55 {
56   mb->len = 0;
57   mb->size = initiallen;
58   mb->out_of_core = 0;
59   mb->too_large = 0;
60   mb->maxlen = maxlen;
61   /* we need to allocate one byte more for get_membuf */
62   mb->buf = xtrymalloc (initiallen+1);
63   if (!mb->buf)
64       mb->out_of_core = 1;
65 }
66
67 static void
68 put_membuf (struct membuf *mb, const void *buf, size_t len)
69 {
70   if (mb->out_of_core || mb->too_large)
71     return;
72
73   if (mb->maxlen && mb->len + len > mb->maxlen)
74     {
75       mb->too_large = 1;
76       return;
77     }
78
79   if (mb->len + len >= mb->size)
80     {
81       char *p;
82       
83       mb->size += len + 1024;
84       /* we need to allocate one byte more for get_membuf */
85       p = xtryrealloc (mb->buf, mb->size+1);
86       if (!p)
87         {
88           mb->out_of_core = 1;
89           return;
90         }
91       mb->buf = p;
92     }
93   memcpy (mb->buf + mb->len, buf, len);
94   mb->len += len;
95 }
96
97 static void *
98 get_membuf (struct membuf *mb, size_t *len)
99 {
100   char *p;
101
102   if (mb->out_of_core || mb->too_large)
103     {
104       xfree (mb->buf);
105       mb->buf = NULL;
106       return NULL;
107     }
108
109   mb->buf[mb->len] = 0; /* there is enough space for the hidden eos */
110   p = mb->buf;
111   *len = mb->len;
112   mb->buf = NULL;
113   mb->out_of_core = 1; /* don't allow a reuse */
114   return p;
115 }
116
117 static void
118 free_membuf (struct membuf *mb)
119 {
120   xfree (mb->buf);
121   mb->buf = NULL;
122 }
123
124
125 /**
126  * assuan_inquire:
127  * @ctx: An assuan context
128  * @keyword: The keyword used for the inquire
129  * @r_buffer: Returns an allocated buffer
130  * @r_length: Returns the length of this buffer
131  * @maxlen: If not 0, the size limit of the inquired data.
132  * 
133  * A Server may use this to Send an inquire.  r_buffer, r_length and
134  * maxlen may all be NULL/0 to indicate that no real data is expected.
135  * 
136  * Return value: 0 on success or an ASSUAN error code
137  **/
138 assuan_error_t
139 assuan_inquire (assuan_context_t ctx, const char *keyword,
140                 unsigned char **r_buffer, size_t *r_length, size_t maxlen)
141 {
142   assuan_error_t rc;
143   struct membuf mb;
144   char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */
145   unsigned char *line, *p;
146   int linelen;
147   int nodataexpected;
148
149   if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
150     return _assuan_error (ASSUAN_Invalid_Value);
151   nodataexpected = !r_buffer && !r_length && !maxlen;
152   if (!nodataexpected && (!r_buffer || !r_length))
153     return _assuan_error (ASSUAN_Invalid_Value);
154   if (!ctx->is_server)
155     return _assuan_error (ASSUAN_Not_A_Server);
156   if (ctx->in_inquire)
157     return _assuan_error (ASSUAN_Nested_Commands);
158   
159   ctx->in_inquire = 1;
160   if (nodataexpected)
161     memset (&mb, 0, sizeof mb); /* avoid compiler warnings */
162   else
163     init_membuf (&mb, maxlen? maxlen:1024, maxlen);
164
165   strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
166   rc = assuan_write_line (ctx, cmdbuf);
167   if (rc)
168     goto leave;
169
170   for (;;)
171     {
172       do 
173         {
174           rc = _assuan_read_line (ctx);
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
238
239
240
241