Assuan server mode is now basically usable
[gnupg.git] / assuan / assuan-handler.c
1 /* assuan-handler.c - dispatch commands 
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
30
31 static int
32 dummy_handler (ASSUAN_CONTEXT ctx, char *line)
33 {
34   return set_error (ctx, Server_Fault, "no handler registered");
35 }
36
37
38 static int
39 std_handler_nop (ASSUAN_CONTEXT ctx, char *line)
40 {
41   return 0; /* okay */
42 }
43   
44 static int
45 std_handler_cancel (ASSUAN_CONTEXT ctx, char *line)
46 {
47   return set_error (ctx, Not_Implemented, NULL); 
48 }
49   
50 static int
51 std_handler_bye (ASSUAN_CONTEXT ctx, char *line)
52 {
53   return -1; /* pretty simple :-) */
54 }
55   
56 static int
57 std_handler_auth (ASSUAN_CONTEXT ctx, char *line)
58 {
59   return set_error (ctx, Not_Implemented, NULL); 
60 }
61   
62 static int
63 std_handler_reset (ASSUAN_CONTEXT ctx, char *line)
64 {
65   return set_error (ctx, Not_Implemented, NULL); 
66 }
67   
68 static int
69 std_handler_end (ASSUAN_CONTEXT ctx, char *line)
70 {
71   return set_error (ctx, Not_Implemented, NULL); 
72 }
73
74 static int
75 parse_cmd_input_output (ASSUAN_CONTEXT ctx, char *line, int *rfd)
76 {
77   char *endp;
78
79   if (strncmp (line, "FD=", 3))
80     return set_error (ctx, Syntax_Error, "FD=<n> expected");
81   line += 3;
82   if (!digitp (*line))
83     return set_error (ctx, Syntax_Error, "number required");
84   *rfd = strtoul (line, &endp, 10);
85   if (*endp)
86     return set_error (ctx, Syntax_Error, "garbage found");
87   if (*rfd == ctx->inbound.fd)
88     return set_error (ctx, Parameter_Conflict, "fd same as inbound fd");
89   if (*rfd == ctx->outbound.fd)
90     return set_error (ctx, Parameter_Conflict, "fd same as outbound fd");
91   return 0;
92 }
93
94 /* Format is INPUT FD=<n> */
95 static int
96 std_handler_input (ASSUAN_CONTEXT ctx, char *line)
97 {
98   int rc, fd;
99
100   rc = parse_cmd_input_output (ctx, line, &fd);
101   if (rc)
102     return rc;
103   ctx->input_fd = fd;
104   return 0;
105 }
106
107 /* Format is OUTPUT FD=<n> */
108 static int
109 std_handler_output (ASSUAN_CONTEXT ctx, char *line)
110 {
111   int rc, fd;
112
113   rc = parse_cmd_input_output (ctx, line, &fd);
114   if (rc)
115     return rc;
116   ctx->output_fd = fd;
117   return 0;
118 }
119
120
121
122   
123
124 /* This is a table with the standard commands and handler for them.
125    The table is used to initialize a new context and assuciate strings
126    and handlers with cmd_ids */
127 static struct {
128   const char *name;
129   int cmd_id;
130   int (*handler)(ASSUAN_CONTEXT, char *line);
131   int always; /* always initializethis command */
132 } std_cmd_table[] = {
133   { "NOP",    ASSUAN_CMD_NOP,    std_handler_nop, 1 },
134   { "CANCEL", ASSUAN_CMD_CANCEL, std_handler_cancel, 1 },
135   { "BYE",    ASSUAN_CMD_BYE,    std_handler_bye, 1 },
136   { "AUTH",   ASSUAN_CMD_AUTH,   std_handler_auth, 1 },
137   { "RESET",  ASSUAN_CMD_RESET,  std_handler_reset, 1 },
138   { "END",    ASSUAN_CMD_END,    std_handler_end, 1 },
139
140   { "INPUT",  ASSUAN_CMD_INPUT,  std_handler_input },
141   { "OUTPUT", ASSUAN_CMD_OUTPUT, std_handler_output },
142   { NULL }
143 };
144
145
146 /**
147  * assuan_register_command:
148  * @ctx: the server context
149  * @cmd_id: An ID value for the command
150  * @cmd_name: A string with the command name
151  * @handler: The handler function to be called
152  * 
153  * Register a handler to be used for a given command.
154  * 
155  * The @cmd_name must be %NULL or an empty string for all @cmd_ids
156  * below %ASSUAN_CMD_USER because predefined values are used.
157  * 
158  * Return value: 
159  **/
160 int
161 assuan_register_command (ASSUAN_CONTEXT ctx,
162                          int cmd_id, const char *cmd_name,
163                          int (*handler)(ASSUAN_CONTEXT, char *))
164 {
165   int i;
166
167   if (cmd_name && !*cmd_name)
168     cmd_name = NULL;
169
170   if (cmd_id < ASSUAN_CMD_USER)
171     { 
172       if (cmd_name)
173         return ASSUAN_Invalid_Value; /* must be NULL for these values*/
174
175       for (i=0; std_cmd_table[i].name; i++)
176         {
177           if (std_cmd_table[i].cmd_id == cmd_id)
178             {
179               cmd_name = std_cmd_table[i].name;
180               if (!handler)
181                 handler = std_cmd_table[i].handler;
182               break;
183             }
184         }
185       if (!std_cmd_table[i].name)
186         return ASSUAN_Invalid_Value; /* not a pre-registered one */
187     }
188   
189   if (!handler)
190     handler = dummy_handler;
191
192   if (!cmd_name)
193     return ASSUAN_Invalid_Value;
194
195   fprintf (stderr, "DBG-assuan: registering %d as `%s'\n", cmd_id, cmd_name);
196
197   if (!ctx->cmdtbl)
198     {
199       ctx->cmdtbl_size = 10;
200       ctx->cmdtbl = xtrycalloc ( ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
201       if (!ctx->cmdtbl)
202         return ASSUAN_Out_Of_Core;
203       ctx->cmdtbl_used = 0;
204     }
205   else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
206     {
207       struct cmdtbl_s *x;
208
209       fprintf (stderr, "DBG-assuan: enlarging cmdtbl\n");
210       
211       x = xtryrealloc ( ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
212       if (!x)
213         return ASSUAN_Out_Of_Core;
214       ctx->cmdtbl = x;
215       ctx->cmdtbl_size += 10;
216     }
217
218   ctx->cmdtbl[ctx->cmdtbl_used].name = cmd_name;
219   ctx->cmdtbl[ctx->cmdtbl_used].cmd_id = cmd_id;
220   ctx->cmdtbl[ctx->cmdtbl_used].handler = handler;
221   ctx->cmdtbl_used++;
222   return 0;
223 }
224
225 /* Helper to register the standards commands */
226 int
227 _assuan_register_std_commands (ASSUAN_CONTEXT ctx)
228 {
229   int i, rc;
230
231   for (i=0; std_cmd_table[i].name; i++)
232     {
233       if (std_cmd_table[i].always)
234         {
235           rc = assuan_register_command (ctx, std_cmd_table[i].cmd_id,
236                                         NULL, NULL);
237           if (rc)
238             return rc;
239         }
240     } 
241   return 0;
242 }
243
244
245 \f
246 /* Process the special data lines.  The "D " has already been removed
247    from the line.  As all handlers this function may modify the line.  */
248 static int
249 handle_data_line (ASSUAN_CONTEXT ctx, char *line, int linelen)
250 {
251   return set_error (ctx, Not_Implemented, NULL);
252 }
253
254
255 /* Parse the line, break out the command, find it in the command
256    table, remove leading and white spaces from the arguments, all the
257    handler with the argument line and return the error */
258 static int 
259 dispatch_command (ASSUAN_CONTEXT ctx, char *line, int linelen)
260 {
261   char *p;
262   const char *s;
263   int shift, i;
264
265   if (*line == 'D' && line[1] == ' ') /* divert to special handler */
266     return handle_data_line (ctx, line+2, linelen-2);
267
268   for (p=line; *p && *p != ' ' && *p != '\t'; p++)
269     ;
270   if (p==line)
271     return set_error (ctx, Invalid_Command, "leading white-space"); 
272   if (*p) 
273     { /* Skip over leading WS after the keyword */
274       *p++ = 0;
275       while ( *p == ' ' || *p == '\t')
276         p++;
277     }
278   shift = p - line;
279
280   for (i=0; (s=ctx->cmdtbl[i].name); i++)
281     if (!strcmp (line, s))
282       break;
283   if (!s)
284     return set_error (ctx, Unknown_Command, NULL);
285   line += shift;
286   linelen -= shift;
287
288   fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line);
289   return ctx->cmdtbl[i].handler (ctx, line);
290 }
291
292
293
294
295 /**
296  * assuan_process:
297  * @ctx: assuan context
298  * 
299  * This fucntion is used to handle the assuan protocol after a
300  * connection has been established using assuan_accept().  This is the
301  * main protocol handler.
302  * 
303  * Return value: 0 on success or an error code if the assuan operation
304  * failed.  Note, that no error is returned for operational errors.
305  **/
306 int
307 assuan_process (ASSUAN_CONTEXT ctx)
308 {
309   int rc;
310
311   do {
312     /* Read the line but skip comments */
313     do
314       {
315         rc = _assuan_read_line (ctx);
316         if (rc)
317           return rc;
318       
319         fprintf (stderr, "DBG-assuan: got %d bytes `%s'\n",
320                  ctx->inbound.linelen, ctx->inbound.line);
321       }
322     while ( *ctx->inbound.line == '#' || !ctx->inbound.linelen);
323   
324     /* dispatch comamnd and return reply */
325     rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
326     if (!rc)
327       rc = _assuan_write_line (ctx, "OK");
328     else if (rc == -1)
329       { /* No error checking because the peer may have already disconnect */ 
330         _assuan_write_line (ctx, "OK  Bye, bye - hope to meet you again");
331       }
332     else 
333       {
334         char errline[256];
335
336         if (rc < 100)
337           sprintf (errline, "ERR %d server fault (%.50s)",
338                    ASSUAN_Server_Fault, assuan_strerror (rc));
339         else
340           {
341             const char *text = ctx->err_no == rc? ctx->err_str:NULL;
342
343             sprintf (errline, "ERR %d %.50s%s%.100s",
344                      rc, assuan_strerror (rc), text? " - ":"", text?text:"");
345           }
346         rc = _assuan_write_line (ctx, errline);
347       }
348   } while (!rc);
349
350   if (rc == -1)
351     rc = 0;
352
353   return rc;
354 }
355
356
357
358