2001-12-16 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / assuan / assuan-inquire.c
1 /* assuan-inquire.c - handle inquire stuff
2  *      Copyright (C) 2001 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   mb->buf = xtrymalloc (initiallen);
60   if (!mb->buf)
61       mb->out_of_core = 1;
62 }
63
64 static void
65 put_membuf (struct membuf *mb, const void *buf, size_t len)
66 {
67   if (mb->out_of_core || mb->too_large)
68     return;
69
70   if (mb->maxlen && mb->len + len > mb->maxlen)
71     {
72       mb->too_large = 1;
73       return;
74     }
75
76   if (mb->len + len >= mb->size)
77     {
78       char *p;
79       
80       mb->size += len + 1024;
81       p = xtryrealloc (mb->buf, mb->size);
82       if (!p)
83         {
84           mb->out_of_core = 1;
85           return;
86         }
87       mb->buf = p;
88     }
89   memcpy (mb->buf + mb->len, buf, len);
90   mb->len += len;
91 }
92
93 static void *
94 get_membuf (struct membuf *mb, size_t *len)
95 {
96   char *p;
97
98   if (mb->out_of_core || mb->too_large)
99     {
100       xfree (mb->buf);
101       mb->buf = NULL;
102       return NULL;
103     }
104
105   p = mb->buf;
106   *len = mb->len;
107   mb->buf = NULL;
108   mb->out_of_core = 1; /* don't allow a reuse */
109   return p;
110 }
111
112 static void
113 free_membuf (struct membuf *mb)
114 {
115   xfree (mb->buf);
116   mb->buf = NULL;
117 }
118
119
120 /**
121  * assuan_inquire:
122  * @ctx: An assuan context
123  * @keyword: The keyword used for the inquire
124  * @r_buffer: Returns an allocated buffer
125  * @r_length: Returns the length of this buffer
126  * @maxlen: If no 0, the size limit of the inquired data.
127  * 
128  * A Server may use this to Send an inquire
129  * 
130  * Return value: 0 on success or an ASSUAN error code
131  **/
132 AssuanError
133 assuan_inquire (ASSUAN_CONTEXT ctx, const char *keyword,
134                 char **r_buffer, size_t *r_length, size_t maxlen)
135 {
136   AssuanError rc;
137   struct membuf mb;
138   char cmdbuf[100];
139   unsigned char *line, *p;
140   int linelen;
141
142   if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))
143       || !r_buffer || !r_length )
144     return ASSUAN_Invalid_Value;
145   if (!ctx->is_server)
146     return ASSUAN_Not_A_Server;
147   if (ctx->in_inquire)
148     return ASSUAN_Nested_Commands;
149   
150   ctx->in_inquire = 1;
151   init_membuf (&mb, maxlen? maxlen:1024, maxlen);
152
153   strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
154   rc = assuan_write_line (ctx, cmdbuf);
155   if (rc)
156     goto leave;
157
158   for (;;)
159     {
160       do 
161         {
162           rc = _assuan_read_line (ctx);
163           if (rc)
164             goto leave;
165           line = ctx->inbound.line;
166           linelen = ctx->inbound.linelen;
167         }    
168       while (*line == '#' || !linelen);
169       if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
170           && (!line[3] || line[3] == ' '))
171         break; /* END command received*/
172       if (line[0] != 'D' || line[1] != ' ')
173         {
174           rc = ASSUAN_Unexpected_Command;
175           goto leave;
176         }
177       if (linelen < 3)
178         continue;
179       line += 2;
180       linelen -= 2;
181
182       p = line;
183       while (linelen)
184         {
185           for (;linelen && *p != '%'; linelen--, p++)
186             ;
187           put_membuf (&mb, line, p-line);
188           if (linelen > 2)
189             { /* handle escaping */
190               unsigned char tmp[1];
191               p++;
192               *tmp = xtoi_2 (p);
193               p += 2;
194               linelen -= 3;
195               put_membuf (&mb, tmp, 1);
196             }
197           line = p;
198         }
199       if (mb.too_large)
200         {
201           rc = ASSUAN_Too_Much_Data;
202           goto leave;
203         }
204     }
205   
206   *r_buffer = get_membuf (&mb, r_length);
207   if (!*r_buffer)
208     rc = ASSUAN_Out_Of_Core;
209
210  leave:
211   free_membuf (&mb);
212   ctx->in_inquire = 0;
213   return rc;
214 }
215
216
217
218
219
220