updated assuan
[pinentry.git] / assuan / assuan-inquire.c
1 /* assuan-inquire.c - handle inquire stuff
2  *      Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG 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  * GnuPG 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 <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "assuan-defs.h"
27
28 #define digitp(a) ((a) >= '0' && (a) <= '9')
29 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
30                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
31 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
32
33
34 struct membuf {
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 implemnation 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 AssuanError
137 assuan_inquire (ASSUAN_CONTEXT ctx, const char *keyword,
138                 char **r_buffer, size_t *r_length, size_t maxlen)
139 {
140   AssuanError rc;
141   struct membuf mb;
142   char cmdbuf[100];
143   unsigned char *line, *p;
144   int linelen;
145   int nodataexpected;
146
147   if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
148     return ASSUAN_Invalid_Value;
149   nodataexpected = !r_buffer && !r_length && !maxlen;
150   if (!nodataexpected && (!r_buffer || !r_length))
151     return ASSUAN_Invalid_Value;
152   if (!ctx->is_server)
153     return ASSUAN_Not_A_Server;
154   if (ctx->in_inquire)
155     return 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           rc = _assuan_read_line (ctx);
173           if (rc)
174             goto leave;
175           line = ctx->inbound.line;
176           linelen = ctx->inbound.linelen;
177         }    
178       while (*line == '#' || !linelen);
179       if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
180           && (!line[3] || line[3] == ' '))
181         break; /* END command received*/
182       if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
183         {
184           rc = ASSUAN_Canceled;
185           goto leave;
186         }
187       if (line[0] != 'D' || line[1] != ' ' || nodataexpected)
188         {
189           rc = ASSUAN_Unexpected_Command;
190           goto leave;
191         }
192       if (linelen < 3)
193         continue;
194       line += 2;
195       linelen -= 2;
196
197       p = line;
198       while (linelen)
199         {
200           for (;linelen && *p != '%'; linelen--, p++)
201             ;
202           put_membuf (&mb, line, p-line);
203           if (linelen > 2)
204             { /* handle escaping */
205               unsigned char tmp[1];
206               p++;
207               *tmp = xtoi_2 (p);
208               p += 2;
209               linelen -= 3;
210               put_membuf (&mb, tmp, 1);
211             }
212           line = p;
213         }
214       if (mb.too_large)
215         {
216           rc = ASSUAN_Too_Much_Data;
217           goto leave;
218         }
219     }
220
221   if (!nodataexpected)
222     {
223       *r_buffer = get_membuf (&mb, r_length);
224       if (!*r_buffer)
225         rc = ASSUAN_Out_Of_Core;
226     }
227
228  leave:
229   if (!nodataexpected)
230     free_membuf (&mb);
231   ctx->in_inquire = 0;
232   return rc;
233 }
234
235
236
237
238
239