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