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