Detect unsigned time_t and adjust y2038 detection.
[gnupg.git] / util / assuan-client.c
1 /* assuan-client.c - client functions
2  *      Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3  *      Copyright (C) 2005 Free Software Foundation, Inc.
4  *
5  * This file is part of Assuan.
6  *
7  * Assuan is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * Assuan is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 /* Please note that this is a stripped down and modified version of
22    the orginal Assuan code from libassuan. */
23
24 #include <config.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <assert.h>
30 #include <string.h>
31
32 #include "assuan-defs.h"
33
34 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
35                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
36 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
37
38
39 assuan_error_t
40 _assuan_read_from_server (assuan_context_t ctx, int *okay, int *off)
41 {
42   char *line;
43   int linelen;
44   assuan_error_t rc;
45
46   *okay = 0;
47   *off = 0;
48   do 
49     {
50       rc = _assuan_read_line (ctx);
51       if (rc)
52         return rc;
53       line = ctx->inbound.line;
54       linelen = ctx->inbound.linelen;
55     }    
56   while (*line == '#' || !linelen);
57
58   if (linelen >= 1
59       && line[0] == 'D' && line[1] == ' ')
60     {
61       *okay = 2; /* data line */
62       *off = 2;
63     }
64   else if (linelen >= 1
65            && line[0] == 'S' 
66            && (line[1] == '\0' || line[1] == ' '))
67     {
68       *okay = 4;
69       *off = 1;
70       while (line[*off] == ' ')
71         ++*off;
72     }  
73   else if (linelen >= 2
74            && line[0] == 'O' && line[1] == 'K'
75            && (line[2] == '\0' || line[2] == ' '))
76     {
77       *okay = 1;
78       *off = 2;
79       while (line[*off] == ' ')
80         ++*off;
81     }
82   else if (linelen >= 3
83            && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
84            && (line[3] == '\0' || line[3] == ' '))
85     {
86       *okay = 0;
87       *off = 3;
88       while (line[*off] == ' ')
89         ++*off;
90     }  
91   else if (linelen >= 7
92            && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
93            && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
94            && line[6] == 'E' 
95            && (line[7] == '\0' || line[7] == ' '))
96     {
97       *okay = 3;
98       *off = 7;
99       while (line[*off] == ' ')
100         ++*off;
101     }
102   else if (linelen >= 3
103            && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
104            && (line[3] == '\0' || line[3] == ' '))
105     {
106       *okay = 5; /* end line */
107       *off = 3;
108     }
109   else
110     rc = ASSUAN_Invalid_Response;
111   return rc;
112 }
113
114
115 \f
116 assuan_error_t
117 assuan_transact (assuan_context_t ctx,
118                  const char *command,
119                  assuan_error_t (*data_cb)(void *, const void *, size_t),
120                  void *data_cb_arg,
121                  assuan_error_t (*inquire_cb)(void*, const char *),
122                  void *inquire_cb_arg,
123                  assuan_error_t (*status_cb)(void*, const char *),
124                  void *status_cb_arg)
125 {
126   return assuan_transact2 (ctx, command,
127                            data_cb, data_cb_arg,
128                            inquire_cb, inquire_cb_arg,
129                            status_cb, status_cb_arg,
130                            NULL, NULL);
131 }
132
133
134 /**
135  * assuan_transact2:
136  * @ctx: The Assuan context
137  * @command: Coimmand line to be send to server
138  * @data_cb: Callback function for data lines
139  * @data_cb_arg: first argument passed to @data_cb
140  * @inquire_cb: Callback function for a inquire response
141  * @inquire_cb_arg: first argument passed to @inquire_cb
142  * @status_cb: Callback function for a status response
143  * @status_cb_arg: first argument passed to @status_cb
144  * @okay_cb: Callback function for the final  OK response
145  * @okay_cb_arg: first argument passed to @okay_cb
146  * 
147  * FIXME: Write documentation
148  * 
149  * Return value: 0 on success or error code.  The error code may be
150  * the one one returned by the server in error lines or from the
151  * callback functions.
152  **/
153 assuan_error_t
154 assuan_transact2 (assuan_context_t ctx,
155                   const char *command,
156                   assuan_error_t (*data_cb)(void *, const void *, size_t),
157                   void *data_cb_arg,
158                   assuan_error_t (*inquire_cb)(void*, const char *),
159                   void *inquire_cb_arg,
160                   assuan_error_t (*status_cb)(void*, const char *),
161                   void *status_cb_arg,
162                   assuan_error_t (*okay_cb)(void*, const char *),
163                   void *okay_cb_arg)
164 {
165   int rc, okay, off;
166   unsigned char *line;
167   int linelen;
168
169   rc = assuan_write_line (ctx, command);
170   if (rc)
171     return rc;
172
173   if (*command == '#' || !*command)
174     return 0; /* Don't expect a response for a comment line.  */
175
176  again:
177   rc = _assuan_read_from_server (ctx, &okay, &off);
178   if (rc)
179     return rc; /* error reading from server */
180
181   line = ctx->inbound.line + off;
182   linelen = ctx->inbound.linelen - off;
183
184   if (!okay)
185     {
186       rc = atoi (line);
187       if (rc < 100)
188         rc = ASSUAN_Server_Fault;
189     }
190   else if (okay == 1) /* Received OK. */
191     {
192       if (okay_cb)
193         {
194           rc = okay_cb (okay_cb_arg, line);
195           /* We better wipe out the buffer after processing it.  This
196              is no real guarantee that it won't get swapped out but at
197              least for the standard cases we can make sure that a
198              passphrase returned with the OK line is rendered
199              unreadable.  In fact the current Assuan interface suffers
200              from the problem that it is not possible to do assuan I/O
201              through secure memory.  There is no easy solution given
202              the current implementation but we need to address it
203              sooner or later.  The problem was introduced with
204              gpg-agent's GET_PASPHRASE command but it might also make
205              sense to have a way to convey sessions keys through
206              secured memory.  Note that the old implementation in gpg
207              for accessing the passphrase in fact used secure memory
208              but had the drawback of using a limited and not fully
209              conforming Assuan implementation - given that pinentry
210              and gpg-agent neither use secured memory for Assuan I/O,
211              it is negligible to drop the old implementation in gpg's
212              passphrase.c and use the wipememory workaround here.  */
213           memset (line, 0, strlen (line));
214         }
215     }
216   else if (okay == 2)
217     {
218       if (!data_cb)
219         rc = ASSUAN_No_Data_Callback;
220       else 
221         {
222           unsigned char *s, *d;
223
224           for (s=d=line; linelen; linelen--)
225             {
226               if (*s == '%' && linelen > 2)
227                 { /* handle escaping */
228                   s++;
229                   *d++ = xtoi_2 (s);
230                   s += 2;
231                   linelen -= 2;
232                 }
233               else
234                 *d++ = *s++;
235             }
236           *d = 0; /* add a hidden string terminator */
237           rc = data_cb (data_cb_arg, line, d - line);
238           if (!rc)
239             goto again;
240         }
241     }
242   else if (okay == 3)
243     {
244       if (!inquire_cb)
245         {
246           assuan_write_line (ctx, "END"); /* get out of inquire mode */
247           _assuan_read_from_server (ctx, &okay, &off); /* dummy read */
248           rc = ASSUAN_No_Inquire_Callback;
249         }
250       else
251         {
252           rc = inquire_cb (inquire_cb_arg, line);
253           if (!rc)
254             rc = assuan_send_data (ctx, NULL, 0); /* flush and send END */
255           if (!rc)
256             goto again;
257         }
258     }
259   else if (okay == 4)
260     {
261       if (status_cb)
262         rc = status_cb (status_cb_arg, line);
263       if (!rc)
264         goto again;
265     }
266   else if (okay == 5)
267     {
268       if (!data_cb)
269         rc = ASSUAN_No_Data_Callback;
270       else 
271         {
272           rc = data_cb (data_cb_arg, NULL, 0);
273           if (!rc)
274             goto again;
275         }
276     }
277
278   return rc;
279 }
280