Line data Source code
1 : /* client.c - Functions common to all clients.
2 : Copyright (C) 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 :
21 : #ifdef HAVE_CONFIG_H
22 : #include <config.h>
23 : #endif
24 :
25 : #include <stdlib.h>
26 :
27 : #include "assuan-defs.h"
28 : #include "debug.h"
29 :
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 : void
36 2 : _assuan_client_finish (assuan_context_t ctx)
37 : {
38 2 : if (ctx->inbound.fd != ASSUAN_INVALID_FD)
39 : {
40 2 : _assuan_close (ctx, ctx->inbound.fd);
41 2 : if (ctx->inbound.fd == ctx->outbound.fd)
42 1 : ctx->outbound.fd = ASSUAN_INVALID_FD;
43 2 : ctx->inbound.fd = ASSUAN_INVALID_FD;
44 : }
45 2 : if (ctx->outbound.fd != ASSUAN_INVALID_FD)
46 : {
47 1 : _assuan_close (ctx, ctx->outbound.fd);
48 1 : ctx->outbound.fd = ASSUAN_INVALID_FD;
49 : }
50 2 : if (ctx->pid != ASSUAN_INVALID_PID && ctx->pid)
51 : {
52 1 : _assuan_waitpid (ctx, ctx->pid, ctx->flags.no_waitpid, NULL, 0);
53 1 : ctx->pid = ASSUAN_INVALID_PID;
54 : }
55 :
56 2 : _assuan_uds_deinit (ctx);
57 2 : }
58 :
59 :
60 : /* Disconnect and release the context CTX. */
61 : void
62 2 : _assuan_client_release (assuan_context_t ctx)
63 : {
64 2 : assuan_write_line (ctx, "BYE");
65 :
66 2 : _assuan_client_finish (ctx);
67 2 : }
68 :
69 :
70 : /* This function also does deescaping for data lines. */
71 : gpg_error_t
72 17 : assuan_client_read_response (assuan_context_t ctx,
73 : char **line_r, int *linelen_r)
74 : {
75 : gpg_error_t rc;
76 17 : char *line = NULL;
77 17 : int linelen = 0;
78 :
79 17 : *line_r = NULL;
80 17 : *linelen_r = 0;
81 :
82 : do
83 : {
84 : do
85 : {
86 17 : rc = _assuan_read_line (ctx);
87 : }
88 17 : while (_assuan_error_is_eagain (ctx, rc));
89 17 : if (rc)
90 0 : return rc;
91 17 : line = ctx->inbound.line;
92 17 : linelen = ctx->inbound.linelen;
93 : }
94 17 : while (!linelen);
95 :
96 : /* For data lines, we deescape immediately. The user will never
97 : have to worry about it. */
98 17 : if (linelen >= 1 && line[0] == 'D' && line[1] == ' ')
99 : {
100 : char *s, *d;
101 67 : for (s=d=line; linelen; linelen--)
102 : {
103 66 : if (*s == '%' && linelen > 2)
104 : { /* handle escaping */
105 0 : s++;
106 0 : *d++ = xtoi_2 (s);
107 0 : s += 2;
108 0 : linelen -= 2;
109 : }
110 : else
111 66 : *d++ = *s++;
112 : }
113 1 : *d = 0; /* add a hidden string terminator */
114 :
115 1 : linelen = d - line;
116 1 : ctx->inbound.linelen = linelen;
117 : }
118 :
119 17 : *line_r = line;
120 17 : *linelen_r = linelen;
121 :
122 17 : return 0;
123 : }
124 :
125 :
126 : gpg_error_t
127 17 : assuan_client_parse_response (assuan_context_t ctx, char *line, int linelen,
128 : assuan_response_t *response, int *off)
129 : {
130 17 : *response = ASSUAN_RESPONSE_ERROR;
131 17 : *off = 0;
132 :
133 17 : if (linelen >= 1
134 17 : && line[0] == 'D' && line[1] == ' ')
135 : {
136 1 : *response = ASSUAN_RESPONSE_DATA; /* data line */
137 1 : *off = 2;
138 : }
139 16 : else if (linelen >= 1
140 16 : && line[0] == 'S'
141 0 : && (line[1] == '\0' || line[1] == ' '))
142 : {
143 0 : *response = ASSUAN_RESPONSE_STATUS;
144 0 : *off = 1;
145 0 : while (line[*off] == ' ')
146 0 : ++*off;
147 : }
148 32 : else if (linelen >= 2
149 16 : && line[0] == 'O' && line[1] == 'K'
150 16 : && (line[2] == '\0' || line[2] == ' '))
151 : {
152 16 : *response = ASSUAN_RESPONSE_OK;
153 16 : *off = 2;
154 35 : while (line[*off] == ' ')
155 3 : ++*off;
156 : }
157 0 : else if (linelen >= 3
158 0 : && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
159 0 : && (line[3] == '\0' || line[3] == ' '))
160 : {
161 0 : *response = ASSUAN_RESPONSE_ERROR;
162 0 : *off = 3;
163 0 : while (line[*off] == ' ')
164 0 : ++*off;
165 : }
166 0 : else if (linelen >= 7
167 0 : && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
168 0 : && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
169 0 : && line[6] == 'E'
170 0 : && (line[7] == '\0' || line[7] == ' '))
171 : {
172 0 : *response = ASSUAN_RESPONSE_INQUIRE;
173 0 : *off = 7;
174 0 : while (line[*off] == ' ')
175 0 : ++*off;
176 : }
177 0 : else if (linelen >= 3
178 0 : && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
179 0 : && (line[3] == '\0' || line[3] == ' '))
180 : {
181 0 : *response = ASSUAN_RESPONSE_END;
182 0 : *off = 3;
183 : }
184 0 : else if (linelen >= 1 && line[0] == '#')
185 : {
186 0 : *response = ASSUAN_RESPONSE_COMMENT;
187 0 : *off = 1;
188 : }
189 : else
190 0 : return _assuan_error (ctx, GPG_ERR_ASS_INV_RESPONSE);
191 :
192 17 : return 0;
193 : }
194 :
195 :
196 : gpg_error_t
197 17 : _assuan_read_from_server (assuan_context_t ctx, assuan_response_t *response,
198 : int *off, int convey_comments)
199 : {
200 : gpg_error_t rc;
201 : char *line;
202 : int linelen;
203 :
204 : do
205 : {
206 17 : *response = ASSUAN_RESPONSE_ERROR;
207 17 : *off = 0;
208 17 : rc = assuan_client_read_response (ctx, &line, &linelen);
209 17 : if (!rc)
210 17 : rc = assuan_client_parse_response (ctx, line, linelen, response, off);
211 : }
212 17 : while (!rc && *response == ASSUAN_RESPONSE_COMMENT && !convey_comments);
213 :
214 17 : return rc;
215 : }
216 :
217 :
218 : /**
219 : * assuan_transact:
220 : * @ctx: The Assuan context
221 : * @command: Command line to be send to the server
222 : * @data_cb: Callback function for data lines
223 : * @data_cb_arg: first argument passed to @data_cb
224 : * @inquire_cb: Callback function for a inquire response
225 : * @inquire_cb_arg: first argument passed to @inquire_cb
226 : * @status_cb: Callback function for a status response
227 : * @status_cb_arg: first argument passed to @status_cb
228 : *
229 : * FIXME: Write documentation
230 : *
231 : * Return value: 0 on success or an error code. The error code may be
232 : * the one one returned by the server via error lines or from the
233 : * callback functions. Take care: If a callback returns an error
234 : * this function returns immediately with this error.
235 : **/
236 : gpg_error_t
237 14 : assuan_transact (assuan_context_t ctx,
238 : const char *command,
239 : gpg_error_t (*data_cb)(void *, const void *, size_t),
240 : void *data_cb_arg,
241 : gpg_error_t (*inquire_cb)(void*, const char *),
242 : void *inquire_cb_arg,
243 : gpg_error_t (*status_cb)(void*, const char *),
244 : void *status_cb_arg)
245 : {
246 : gpg_error_t rc;
247 : assuan_response_t response;
248 : int off;
249 : char *line;
250 : int linelen;
251 :
252 14 : rc = assuan_write_line (ctx, command);
253 14 : if (rc)
254 0 : return rc;
255 :
256 14 : if (*command == '#' || !*command)
257 0 : return 0; /* Don't expect a response for a comment line. */
258 :
259 : again:
260 15 : rc = _assuan_read_from_server (ctx, &response, &off,
261 15 : ctx->flags.convey_comments);
262 15 : if (rc)
263 0 : return rc; /* error reading from server */
264 :
265 15 : line = ctx->inbound.line + off;
266 15 : linelen = ctx->inbound.linelen - off;
267 :
268 15 : if (response == ASSUAN_RESPONSE_ERROR)
269 0 : rc = atoi (line);
270 15 : else if (response == ASSUAN_RESPONSE_DATA)
271 : {
272 1 : if (!data_cb)
273 0 : rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
274 : else
275 : {
276 1 : rc = data_cb (data_cb_arg, line, linelen);
277 1 : if (!rc)
278 1 : goto again;
279 : }
280 : }
281 14 : else if (response == ASSUAN_RESPONSE_INQUIRE)
282 : {
283 0 : if (!inquire_cb)
284 : {
285 0 : assuan_write_line (ctx, "END"); /* get out of inquire mode */
286 0 : _assuan_read_from_server (ctx, &response, &off, 0); /* dummy read */
287 0 : rc = _assuan_error (ctx, GPG_ERR_ASS_NO_INQUIRE_CB);
288 : }
289 : else
290 : {
291 0 : rc = inquire_cb (inquire_cb_arg, line);
292 0 : if (!rc)
293 0 : rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
294 : else
295 : { /* Flush and send CAN. */
296 : /* Note that in this error case we don't want to return
297 : an error code from sending the cancel. The dummy
298 : read is to remove the response from the server which
299 : we are not interested in. */
300 0 : assuan_send_data (ctx, NULL, 1);
301 0 : _assuan_read_from_server (ctx, &response, &off, 0);
302 : }
303 0 : if (!rc)
304 0 : goto again;
305 : }
306 : }
307 14 : else if (response == ASSUAN_RESPONSE_STATUS)
308 : {
309 0 : if (status_cb)
310 0 : rc = status_cb (status_cb_arg, line);
311 0 : if (!rc)
312 0 : goto again;
313 : }
314 14 : else if (response == ASSUAN_RESPONSE_COMMENT && ctx->flags.convey_comments)
315 : {
316 0 : line -= off; /* Send line with the comment marker. */
317 0 : if (status_cb)
318 0 : rc = status_cb (status_cb_arg, line);
319 0 : if (!rc)
320 0 : goto again;
321 : }
322 14 : else if (response == ASSUAN_RESPONSE_END)
323 : {
324 0 : if (!data_cb)
325 0 : rc = _assuan_error (ctx, GPG_ERR_ASS_NO_DATA_CB);
326 : else
327 : {
328 0 : rc = data_cb (data_cb_arg, NULL, 0);
329 0 : if (!rc)
330 0 : goto again;
331 : }
332 : }
333 :
334 14 : return rc;
335 : }
|