2005-10-01 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / assuan / assuan-client.c
1 /* assuan-client.c - client functions
2  *      Copyright (C) 2001, 2002, 2005 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 <errno.h>
25 #include <unistd.h>
26 #include <assert.h>
27
28 #include "assuan-defs.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 assuan_error_t
36 _assuan_read_from_server (ASSUAN_CONTEXT ctx, int *okay, int *off)
37 {
38   char *line;
39   int linelen;
40   assuan_error_t rc;
41
42   *okay = 0;
43   *off = 0;
44   do 
45     {
46       rc = _assuan_read_line (ctx);
47       if (rc)
48         return rc;
49       line = ctx->inbound.line;
50       linelen = ctx->inbound.linelen;
51     }    
52   while (*line == '#' || !linelen);
53
54   if (linelen >= 1
55       && line[0] == 'D' && line[1] == ' ')
56     {
57       *okay = 2; /* data line */
58       *off = 2;
59     }
60   else if (linelen >= 1
61            && line[0] == 'S' 
62            && (line[1] == '\0' || line[1] == ' '))
63     {
64       *okay = 4;
65       *off = 1;
66       while (line[*off] == ' ')
67         ++*off;
68     }  
69   else if (linelen >= 2
70            && line[0] == 'O' && line[1] == 'K'
71            && (line[2] == '\0' || line[2] == ' '))
72     {
73       *okay = 1;
74       *off = 2;
75       while (line[*off] == ' ')
76         ++*off;
77     }
78   else if (linelen >= 3
79            && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
80            && (line[3] == '\0' || line[3] == ' '))
81     {
82       *okay = 0;
83       *off = 3;
84       while (line[*off] == ' ')
85         ++*off;
86     }  
87   else if (linelen >= 7
88            && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
89            && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
90            && line[6] == 'E' 
91            && (line[7] == '\0' || line[7] == ' '))
92     {
93       *okay = 3;
94       *off = 7;
95       while (line[*off] == ' ')
96         ++*off;
97     }
98   else if (linelen >= 3
99            && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
100            && (line[3] == '\0' || line[3] == ' '))
101     {
102       *okay = 5; /* end line */
103       *off = 3;
104     }
105   else
106     rc = ASSUAN_Invalid_Response;
107   return rc;
108 }
109
110
111 \f
112 /**
113  * assuan_transact:
114  * @ctx: The Assuan context
115  * @command: Coimmand line to be send to server
116  * @data_cb: Callback function for data lines
117  * @data_cb_arg: first argument passed to @data_cb
118  * @inquire_cb: Callback function for a inquire response
119  * @inquire_cb_arg: first argument passed to @inquire_cb
120  * @status_cb: Callback function for a status response
121  * @status_cb_arg: first argument passed to @status_cb
122  * 
123  * FIXME: Write documentation
124  * 
125  * Return value: 0 on success or error code.  The error code may be
126  * the one one returned by the server in error lines or from the
127  * callback functions.
128  **/
129 assuan_error_t
130 assuan_transact (ASSUAN_CONTEXT ctx,
131                  const char *command,
132                  assuan_error_t (*data_cb)(void *, const void *, size_t),
133                  void *data_cb_arg,
134                  assuan_error_t (*inquire_cb)(void*, const char *),
135                  void *inquire_cb_arg,
136                  assuan_error_t (*status_cb)(void*, const char *),
137                  void *status_cb_arg)
138 {
139   int rc, okay, off;
140   char *line;
141   int linelen;
142
143   rc = assuan_write_line (ctx, command);
144   if (rc)
145     return rc;
146
147   if (*command == '#' || !*command)
148     return 0; /* Don't expect a response for a comment line.  */
149
150  again:
151   rc = _assuan_read_from_server (ctx, &okay, &off);
152   if (rc)
153     return rc; /* error reading from server */
154
155   line = ctx->inbound.line + off;
156   linelen = ctx->inbound.linelen - off;
157
158   if (!okay)
159     {
160       rc = atoi (line);
161       if (rc < 100)
162         rc = ASSUAN_Server_Fault;
163     }
164   else if (okay == 2)
165     {
166       if (!data_cb)
167         rc = ASSUAN_No_Data_Callback;
168       else 
169         {
170           char *s, *d;
171
172           for (s=d=line; linelen; linelen--)
173             {
174               if (*s == '%' && linelen > 2)
175                 { /* handle escaping */
176                   s++;
177                   *d++ = xtoi_2 (s);
178                   s += 2;
179                   linelen -= 2;
180                 }
181               else
182                 *d++ = *s++;
183             }
184           *d = 0; /* add a hidden string terminator */
185           rc = data_cb (data_cb_arg, line, d - line);
186           if (!rc)
187             goto again;
188         }
189     }
190   else if (okay == 3)
191     {
192       if (!inquire_cb)
193         {
194           assuan_write_line (ctx, "END"); /* get out of inquire mode */
195           _assuan_read_from_server (ctx, &okay, &off); /* dummy read */
196           rc = ASSUAN_No_Inquire_Callback;
197         }
198       else
199         {
200           rc = inquire_cb (inquire_cb_arg, line);
201           if (!rc)
202             rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
203           if (!rc)
204             goto again;
205         }
206     }
207   else if (okay == 4)
208     {
209       if (status_cb)
210         rc = status_cb (status_cb_arg, line);
211       if (!rc)
212         goto again;
213     }
214   else if (okay == 5)
215     {
216       if (!data_cb)
217         rc = ASSUAN_No_Data_Callback;
218       else 
219         {
220           rc = data_cb (data_cb_arg, NULL, 0);
221           if (!rc)
222             goto again;
223         }
224     }
225
226   return rc;
227 }
228