8d034ca51fd867ffdf0268fdcffc2ce723f20283
[gpgme.git] / assuan / assuan-inquire.c
1 /* assuan-inquire.c - handle inquire stuff
2  *      Copyright (C) 2001, 2002, 2003  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., 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 {
36   size_t len;
37   size_t size;
38   char *buf;
39   int out_of_core;
40   int too_large;
41   size_t maxlen;
42 };
43
44
45 \f
46 /* A simple implemnation of a dynamic buffer.  Use init_membuf() to
47    create a buffer, put_membuf to append bytes and get_membuf to
48    release and return the buffer.  Allocation errors are detected but
49    only returned at the final get_membuf(), this helps not to clutter
50    the code with out of core checks.  */
51
52 static void
53 init_membuf (struct membuf *mb, int initiallen, size_t maxlen)
54 {
55   mb->len = 0;
56   mb->size = initiallen;
57   mb->out_of_core = 0;
58   mb->too_large = 0;
59   mb->maxlen = maxlen;
60   /* we need to allocate one byte more for get_membuf */
61   mb->buf = xtrymalloc (initiallen+1);
62   if (!mb->buf)
63       mb->out_of_core = 1;
64 }
65
66 static void
67 put_membuf (struct membuf *mb, const void *buf, size_t len)
68 {
69   if (mb->out_of_core || mb->too_large)
70     return;
71
72   if (mb->maxlen && mb->len + len > mb->maxlen)
73     {
74       mb->too_large = 1;
75       return;
76     }
77
78   if (mb->len + len >= mb->size)
79     {
80       char *p;
81       
82       mb->size += len + 1024;
83       /* we need to allocate one byte more for get_membuf */
84       p = xtryrealloc (mb->buf, mb->size+1);
85       if (!p)
86         {
87           mb->out_of_core = 1;
88           return;
89         }
90       mb->buf = p;
91     }
92   memcpy (mb->buf + mb->len, buf, len);
93   mb->len += len;
94 }
95
96 static void *
97 get_membuf (struct membuf *mb, size_t *len)
98 {
99   char *p;
100
101   if (mb->out_of_core || mb->too_large)
102     {
103       xfree (mb->buf);
104       mb->buf = NULL;
105       return NULL;
106     }
107
108   mb->buf[mb->len] = 0; /* there is enough space for the hidden eos */
109   p = mb->buf;
110   *len = mb->len;
111   mb->buf = NULL;
112   mb->out_of_core = 1; /* don't allow a reuse */
113   return p;
114 }
115
116 static void
117 free_membuf (struct membuf *mb)
118 {
119   xfree (mb->buf);
120   mb->buf = NULL;
121 }
122
123
124 /**
125  * assuan_inquire:
126  * @ctx: An assuan context
127  * @keyword: The keyword used for the inquire
128  * @r_buffer: Returns an allocated buffer
129  * @r_length: Returns the length of this buffer
130  * @maxlen: If not 0, the size limit of the inquired data.
131  * 
132  * A Server may use this to Send an inquire.  r_buffer, r_length and
133  * maxlen may all be NULL/0 to indicate that no real data is expected.
134  * 
135  * Return value: 0 on success or an ASSUAN error code
136  **/
137 assuan_error_t
138 assuan_inquire (assuan_context_t ctx, const char *keyword,
139                 unsigned char **r_buffer, size_t *r_length, size_t maxlen)
140 {
141   assuan_error_t rc;
142   struct membuf mb;
143   char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */
144   unsigned char *line, *p;
145   int linelen;
146   int nodataexpected;
147
148   if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
149     return ASSUAN_Invalid_Value;
150   nodataexpected = !r_buffer && !r_length && !maxlen;
151   if (!nodataexpected && (!r_buffer || !r_length))
152     return ASSUAN_Invalid_Value;
153   if (!ctx->is_server)
154     return ASSUAN_Not_A_Server;
155   if (ctx->in_inquire)
156     return ASSUAN_Nested_Commands;
157   
158   ctx->in_inquire = 1;
159   if (nodataexpected)
160     memset (&mb, 0, sizeof mb); /* avoid compiler warnings */
161   else
162     init_membuf (&mb, maxlen? maxlen:1024, maxlen);
163
164   strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
165   rc = assuan_write_line (ctx, cmdbuf);
166   if (rc)
167     goto leave;
168
169   for (;;)
170     {
171       do 
172         {
173           rc = _assuan_read_line (ctx);
174           if (rc)
175             goto leave;
176           line = ctx->inbound.line;
177           linelen = ctx->inbound.linelen;
178         }    
179       while (*line == '#' || !linelen);
180       if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
181           && (!line[3] || line[3] == ' '))
182         break; /* END command received*/
183       if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
184         {
185           rc = ASSUAN_Canceled;
186           goto leave;
187         }
188       if (line[0] != 'D' || line[1] != ' ' || nodataexpected)
189         {
190           rc = ASSUAN_Unexpected_Command;
191           goto leave;
192         }
193       if (linelen < 3)
194         continue;
195       line += 2;
196       linelen -= 2;
197
198       p = line;
199       while (linelen)
200         {
201           for (;linelen && *p != '%'; linelen--, p++)
202             ;
203           put_membuf (&mb, line, p-line);
204           if (linelen > 2)
205             { /* handle escaping */
206               unsigned char tmp[1];
207               p++;
208               *tmp = xtoi_2 (p);
209               p += 2;
210               linelen -= 3;
211               put_membuf (&mb, tmp, 1);
212             }
213           line = p;
214         }
215       if (mb.too_large)
216         {
217           rc = ASSUAN_Too_Much_Data;
218           goto leave;
219         }
220     }
221
222   if (!nodataexpected)
223     {
224       *r_buffer = get_membuf (&mb, r_length);
225       if (!*r_buffer)
226         rc = ASSUAN_Out_Of_Core;
227     }
228
229  leave:
230   if (!nodataexpected)
231     free_membuf (&mb);
232   ctx->in_inquire = 0;
233   return rc;
234 }
235
236
237
238
239
240