typo fixes
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point 
2  * Copyright (C) 2001, 2002, 2003, 2004 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 <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <unistd.h>
29
30 #include <assuan.h>
31
32 #include "gpgsm.h"
33
34 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
35
36
37 /* The filepointer for status message used in non-server mode */
38 static FILE *statusfp;
39
40 /* Data used to assuciate an Assuan context with local server data */
41 struct server_local_s {
42   assuan_context_t assuan_ctx;
43   int message_fd;
44   int list_internal;
45   int list_external;
46   certlist_t recplist;
47   certlist_t signerlist;
48   certlist_t default_recplist; /* As set by main() - don't release. */
49 };
50
51
52
53 /* Note that it is sufficient to allocate the target string D as
54    long as the source string S, i.e.: strlen(s)+1; */
55 static void
56 strcpy_escaped_plus (char *d, const unsigned char *s)
57 {
58   while (*s)
59     {
60       if (*s == '%' && s[1] && s[2])
61         { 
62           s++;
63           *d++ = xtoi_2 ( s);
64           s += 2;
65         }
66       else if (*s == '+')
67         *d++ = ' ', s++;
68       else
69         *d++ = *s++;
70     }
71   *d = 0; 
72 }
73
74
75
76
77 /* Check whether the option NAME appears in LINE */
78 static int
79 has_option (const char *line, const char *name)
80 {
81   const char *s;
82   int n = strlen (name);
83
84   s = strstr (line, name);
85   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
86 }
87
88
89 static void 
90 close_message_fd (CTRL ctrl)
91 {
92   if (ctrl->server_local->message_fd != -1)
93     {
94       close (ctrl->server_local->message_fd);
95       ctrl->server_local->message_fd = -1;
96     }
97 }
98
99
100 static int
101 option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
102 {
103   CTRL ctrl = assuan_get_pointer (ctx);
104
105   if (!strcmp (key, "include-certs"))
106     {
107       int i = *value? atoi (value) : -1;
108       if (ctrl->include_certs < -2)
109         return ASSUAN_Parameter_Error;
110       ctrl->include_certs = i;
111     }
112   else   if (!strcmp (key, "display"))
113     {
114       if (opt.display)
115         free (opt.display);
116       opt.display = strdup (value);
117       if (!opt.display)
118         return ASSUAN_Out_Of_Core;
119     }
120   else if (!strcmp (key, "ttyname"))
121     {
122       if (opt.ttyname)
123         free (opt.ttyname);
124       opt.ttyname = strdup (value);
125       if (!opt.ttyname)
126         return ASSUAN_Out_Of_Core;
127     }
128   else if (!strcmp (key, "ttytype"))
129     {
130       if (opt.ttytype)
131         free (opt.ttytype);
132       opt.ttytype = strdup (value);
133       if (!opt.ttytype)
134         return ASSUAN_Out_Of_Core;
135     }
136   else if (!strcmp (key, "lc-ctype"))
137     {
138       if (opt.lc_ctype)
139         free (opt.lc_ctype);
140       opt.lc_ctype = strdup (value);
141       if (!opt.lc_ctype)
142         return ASSUAN_Out_Of_Core;
143     }
144   else if (!strcmp (key, "lc-messages"))
145     {
146       if (opt.lc_messages)
147         free (opt.lc_messages);
148       opt.lc_messages = strdup (value);
149       if (!opt.lc_messages)
150         return ASSUAN_Out_Of_Core;
151     }
152   else if (!strcmp (key, "list-mode"))
153     {
154       int i = *value? atoi (value) : 0;
155       if (!i || i == 1) /* default and mode 1 */
156         {
157           ctrl->server_local->list_internal = 1;
158           ctrl->server_local->list_external = 0;
159         }
160       else if (i == 2)
161         {
162           ctrl->server_local->list_internal = 0;
163           ctrl->server_local->list_external = 1;
164         }
165       else if (i == 3)
166         {
167           ctrl->server_local->list_internal = 1;
168           ctrl->server_local->list_external = 1;
169         }
170       else
171         return ASSUAN_Parameter_Error;
172     }
173   else if (!strcmp (key, "with-validation"))
174     {
175       int i = *value? atoi (value) : 0;
176       ctrl->with_validation = i;
177     }
178   else
179     return ASSUAN_Invalid_Option;
180
181   return 0;
182 }
183
184
185
186
187 static void
188 reset_notify (ASSUAN_CONTEXT ctx)
189 {
190   CTRL ctrl = assuan_get_pointer (ctx);
191
192   gpgsm_release_certlist (ctrl->server_local->recplist);
193   gpgsm_release_certlist (ctrl->server_local->signerlist);
194   ctrl->server_local->recplist = NULL;
195   ctrl->server_local->signerlist = NULL;
196   close_message_fd (ctrl);
197   assuan_close_input_fd (ctx);
198   assuan_close_output_fd (ctx);
199 }
200
201
202 static void
203 input_notify (ASSUAN_CONTEXT ctx, const char *line)
204 {
205   CTRL ctrl = assuan_get_pointer (ctx);
206
207   ctrl->autodetect_encoding = 0;
208   ctrl->is_pem = 0;
209   ctrl->is_base64 = 0;
210   if (strstr (line, "--armor"))
211     ctrl->is_pem = 1;  
212   else if (strstr (line, "--base64"))
213     ctrl->is_base64 = 1; 
214   else if (strstr (line, "--binary"))
215     ;
216   else
217     ctrl->autodetect_encoding = 1;
218 }
219
220 static void
221 output_notify (ASSUAN_CONTEXT ctx, const char *line)
222 {
223   CTRL ctrl = assuan_get_pointer (ctx);
224
225   ctrl->create_pem = 0;
226   ctrl->create_base64 = 0;
227   if (strstr (line, "--armor"))
228     ctrl->create_pem = 1;  
229   else if (strstr (line, "--base64"))
230     ctrl->create_base64 = 1; /* just the raw output */
231 }
232
233
234
235 /*  RECIPIENT <userID>
236
237   Set the recipient for the encryption.  <userID> should be the
238   internal representation of the key; the server may accept any other
239   way of specification [we will support this].  If this is a valid and
240   trusted recipient the server does respond with OK, otherwise the
241   return is an ERR with the reason why the recipient can't be used,
242   the encryption will then not be done for this recipient.  If the
243   policy is not to encrypt at all if not all recipients are valid, the
244   client has to take care of this.  All RECIPIENT commands are
245   cumulative until a RESET or an successful ENCRYPT command.  */
246 static int 
247 cmd_recipient (ASSUAN_CONTEXT ctx, char *line)
248 {
249   CTRL ctrl = assuan_get_pointer (ctx);
250   int rc;
251
252   rc = gpgsm_add_to_certlist (ctrl, line, 0, &ctrl->server_local->recplist, 0);
253   if (rc)
254     {
255       gpg_err_code_t r = gpg_err_code (rc);
256       gpgsm_status2 (ctrl, STATUS_INV_RECP,
257                    r == -1? "1":
258                    r == GPG_ERR_NO_PUBKEY?       "1":
259                    r == GPG_ERR_AMBIGUOUS_NAME?  "2":
260                    r == GPG_ERR_WRONG_KEY_USAGE? "3":
261                    r == GPG_ERR_CERT_REVOKED?    "4":
262                    r == GPG_ERR_CERT_EXPIRED?    "5":
263                    r == GPG_ERR_NO_CRL_KNOWN?    "6":
264                    r == GPG_ERR_CRL_TOO_OLD?     "7":
265                    r == GPG_ERR_NO_POLICY_MATCH? "8":
266                    "0",
267                    line, NULL);
268     }
269
270   return map_to_assuan_status (rc);
271 }
272
273 /*  SIGNER <userID>
274
275   Set the signer's keys for the signature creation.  <userID> should
276   be the internal representation of the key; the server may accept any
277   other way of specification [we will support this].  If this is a
278   valid and usable signing key the server does respond with OK,
279   otherwise it returns an ERR with the reason why the key can't be
280   used, the signing will then not be done for this key.  If the policy
281   is not to sign at all if not all signer keys are valid, the client
282   has to take care of this.  All SIGNER commands are cumulative until
283   a RESET but they are *not* reset by an SIGN command becuase it can
284   be expected that set of signers are used for more than one sign
285   operation.  
286
287   Note that this command returns an INV_RECP status which is a bit
288   strange, but they are very similar.  */
289 static int 
290 cmd_signer (ASSUAN_CONTEXT ctx, char *line)
291 {
292   CTRL ctrl = assuan_get_pointer (ctx);
293   int rc;
294
295   rc = gpgsm_add_to_certlist (ctrl, line, 1,
296                               &ctrl->server_local->signerlist, 0);
297   if (rc)
298     {
299       gpg_err_code_t r = gpg_err_code (rc);
300       gpgsm_status2 (ctrl, STATUS_INV_RECP,
301                    r == -1?                          "1":
302                    r == GPG_ERR_NO_PUBKEY?           "1":
303                    r == GPG_ERR_AMBIGUOUS_NAME?      "2":
304                    r == GPG_ERR_WRONG_KEY_USAGE?     "3":
305                    r == GPG_ERR_CERT_REVOKED?        "4":
306                    r == GPG_ERR_CERT_EXPIRED?        "5":
307                    r == GPG_ERR_NO_CRL_KNOWN?        "6":
308                    r == GPG_ERR_CRL_TOO_OLD?         "7":
309                    r == GPG_ERR_NO_POLICY_MATCH?     "8":
310                    r == GPG_ERR_NO_SECKEY?           "9":
311                    "0",
312                   line, NULL);
313     }
314   return map_to_assuan_status (rc);
315 }
316
317
318 /* ENCRYPT 
319
320   Do the actual encryption process. Takes the plaintext from the INPUT
321   command, writes to the ciphertext to the file descriptor set with
322   the OUTPUT command, take the recipients form all the recipients set
323   so far.  If this command fails the clients should try to delete all
324   output currently done or otherwise mark it as invalid.  GPGSM does
325   ensure that there won't be any security problem with leftover data
326   on the output in this case.
327
328   This command should in general not fail, as all necessary checks
329   have been done while setting the recipients.  The input and output
330   pipes are closed. */
331 static int 
332 cmd_encrypt (ASSUAN_CONTEXT ctx, char *line)
333 {
334   CTRL ctrl = assuan_get_pointer (ctx);
335   certlist_t cl;
336   int inp_fd, out_fd;
337   FILE *out_fp;
338   int rc;
339
340   inp_fd = assuan_get_input_fd (ctx);
341   if (inp_fd == -1)
342     return set_error (No_Input, NULL);
343   out_fd = assuan_get_output_fd (ctx);
344   if (out_fd == -1)
345     return set_error (No_Output, NULL);
346
347   out_fp = fdopen ( dup(out_fd), "w");
348   if (!out_fp)
349     return set_error (General_Error, "fdopen() failed");
350   
351   /* Now add all encrypt-to marked recipients from the default
352      list. */
353   rc = 0;
354   if (!opt.no_encrypt_to)
355     {
356       for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
357         if (cl->is_encrypt_to)
358           rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
359                                            &ctrl->server_local->recplist, 1);
360     }
361   if (!rc)
362     rc = gpgsm_encrypt (assuan_get_pointer (ctx),
363                         ctrl->server_local->recplist,
364                         inp_fd, out_fp);
365   fclose (out_fp);
366
367   gpgsm_release_certlist (ctrl->server_local->recplist);
368   ctrl->server_local->recplist = NULL;
369   /* Close and reset the fd */
370   close_message_fd (ctrl);
371   assuan_close_input_fd (ctx);
372   assuan_close_output_fd (ctx);
373   return map_to_assuan_status (rc);
374 }
375
376 /* DECRYPT
377
378   This performs the decrypt operation after doing some check on the
379   internal state. (e.g. that only needed data has been set).  Because
380   it utilizes the GPG-Agent for the session key decryption, there is
381   no need to ask the client for a protecting passphrase - GpgAgent
382   does take care of this by requesting this from the user. */
383 static int 
384 cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
385 {
386   CTRL ctrl = assuan_get_pointer (ctx);
387   int inp_fd, out_fd;
388   FILE *out_fp;
389   int rc;
390
391   inp_fd = assuan_get_input_fd (ctx);
392   if (inp_fd == -1)
393     return set_error (No_Input, NULL);
394   out_fd = assuan_get_output_fd (ctx);
395   if (out_fd == -1)
396     return set_error (No_Output, NULL);
397
398   out_fp = fdopen ( dup(out_fd), "w");
399   if (!out_fp)
400     return set_error (General_Error, "fdopen() failed");
401   rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
402   fclose (out_fp);
403
404   /* close and reset the fd */
405   close_message_fd (ctrl);
406   assuan_close_input_fd (ctx);
407   assuan_close_output_fd (ctx);
408
409   return map_to_assuan_status (rc);
410 }
411
412
413 /* VERIFY
414
415   This does a verify operation on the message send to the input-FD.
416   The result is written out using status lines.  If an output FD was
417   given, the signed text will be written to that.
418   
419   If the signature is a detached one, the server will inquire about
420   the signed material and the client must provide it.
421   */
422 static int 
423 cmd_verify (ASSUAN_CONTEXT ctx, char *line)
424 {
425   int rc;
426   CTRL ctrl = assuan_get_pointer (ctx);
427   int fd = assuan_get_input_fd (ctx);
428   int out_fd = assuan_get_output_fd (ctx);
429   FILE *out_fp = NULL;
430
431   if (fd == -1)
432     return set_error (No_Input, NULL);
433
434   if (out_fd != -1)
435     {
436       out_fp = fdopen ( dup(out_fd), "w");
437       if (!out_fp)
438         return set_error (General_Error, "fdopen() failed");
439     }
440
441   rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
442                      ctrl->server_local->message_fd, out_fp);
443   if (out_fp)
444     fclose (out_fp);
445
446   /* close and reset the fd */
447   close_message_fd (ctrl);
448   assuan_close_input_fd (ctx);
449   assuan_close_output_fd (ctx);
450
451   return map_to_assuan_status (rc);
452 }
453
454
455 /* SIGN [--detached]
456
457   Sign the data set with the INPUT command and write it to the sink
458   set by OUTPUT.  With "--detached" specified, a detached signature is
459   created (surprise).  */
460 static int 
461 cmd_sign (ASSUAN_CONTEXT ctx, char *line)
462 {
463   CTRL ctrl = assuan_get_pointer (ctx);
464   int inp_fd, out_fd;
465   FILE *out_fp;
466   int detached;
467   int rc;
468
469   inp_fd = assuan_get_input_fd (ctx);
470   if (inp_fd == -1)
471     return set_error (No_Input, NULL);
472   out_fd = assuan_get_output_fd (ctx);
473   if (out_fd == -1)
474     return set_error (No_Output, NULL);
475
476   detached = has_option (line, "--detached"); 
477
478   out_fp = fdopen ( dup(out_fd), "w");
479   if (!out_fp)
480     return set_error (General_Error, "fdopen() failed");
481
482   rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
483                    inp_fd, detached, out_fp);
484   fclose (out_fp);
485
486   /* close and reset the fd */
487   close_message_fd (ctrl);
488   assuan_close_input_fd (ctx);
489   assuan_close_output_fd (ctx);
490
491   return map_to_assuan_status (rc);
492 }
493
494
495 /* IMPORT
496
497   Import the certificates read form the input-fd, return status
498   message for each imported one.  The import checks the validity of
499   the certificate but not of the entire chain.  It is possible to
500   import expired certificates.  */
501 static int 
502 cmd_import (ASSUAN_CONTEXT ctx, char *line)
503 {
504   CTRL ctrl = assuan_get_pointer (ctx);
505   int rc;
506   int fd = assuan_get_input_fd (ctx);
507
508   if (fd == -1)
509     return set_error (No_Input, NULL);
510
511   rc = gpgsm_import (assuan_get_pointer (ctx), fd);
512
513   /* close and reset the fd */
514   close_message_fd (ctrl);
515   assuan_close_input_fd (ctx);
516   assuan_close_output_fd (ctx);
517
518   return map_to_assuan_status (rc);
519 }
520
521
522 static int 
523 cmd_export (ASSUAN_CONTEXT ctx, char *line)
524 {
525   CTRL ctrl = assuan_get_pointer (ctx);
526   int fd = assuan_get_output_fd (ctx);
527   FILE *out_fp;
528   char *p;
529   STRLIST list, sl;
530
531   if (fd == -1)
532     return set_error (No_Output, NULL);
533   
534   /* break the line down into an STRLIST */
535   list = NULL;
536   for (p=line; *p; line = p)
537     {
538       while (*p && *p != ' ')
539         p++;
540       if (*p)
541         *p++ = 0;
542       if (*line)
543         {
544           sl = xtrymalloc (sizeof *sl + strlen (line));
545           if (!sl)
546             {
547               free_strlist (list);
548               return ASSUAN_Out_Of_Core;
549             }
550           sl->flags = 0;
551           strcpy_escaped_plus (sl->d, line);
552           sl->next = list;
553           list = sl;
554         }
555     }
556
557   out_fp = fdopen ( dup(fd), "w");
558   if (!out_fp)
559     {
560       free_strlist (list);
561       return set_error (General_Error, "fdopen() failed");
562     }
563
564   gpgsm_export (ctrl, list, out_fp);
565   fclose (out_fp);
566   free_strlist (list);
567   /* close and reset the fd */
568   close_message_fd (ctrl);
569   assuan_close_input_fd (ctx);
570   assuan_close_output_fd (ctx);
571   return 0;
572 }
573
574
575 static int 
576 cmd_delkeys (ASSUAN_CONTEXT ctx, char *line)
577 {
578   CTRL ctrl = assuan_get_pointer (ctx);
579   char *p;
580   STRLIST list, sl;
581   int rc;
582
583   /* break the line down into an STRLIST */
584   list = NULL;
585   for (p=line; *p; line = p)
586     {
587       while (*p && *p != ' ')
588         p++;
589       if (*p)
590         *p++ = 0;
591       if (*line)
592         {
593           sl = xtrymalloc (sizeof *sl + strlen (line));
594           if (!sl)
595             {
596               free_strlist (list);
597               return ASSUAN_Out_Of_Core;
598             }
599           sl->flags = 0;
600           strcpy_escaped_plus (sl->d, line);
601           sl->next = list;
602           list = sl;
603         }
604     }
605
606   rc = gpgsm_delete (ctrl, list);
607   free_strlist (list);
608
609   /* close and reset the fd */
610   close_message_fd (ctrl);
611   assuan_close_input_fd (ctx);
612   assuan_close_output_fd (ctx);
613
614   return map_to_assuan_status (rc);
615 }
616
617
618
619 /* MESSAGE FD=<n>
620
621    Set the file descriptor to read a message which is used with
622    detached signatures */
623 static int 
624 cmd_message (ASSUAN_CONTEXT ctx, char *line)
625 {
626   char *endp;
627   int fd;
628   CTRL ctrl = assuan_get_pointer (ctx);
629
630   if (strncmp (line, "FD=", 3))
631     return set_error (Syntax_Error, "FD=<n> expected");
632   line += 3;
633   if (!digitp (line))
634     return set_error (Syntax_Error, "number required");
635   fd = strtoul (line, &endp, 10);
636   if (*endp)
637     return set_error (Syntax_Error, "garbage found");
638   if (fd == -1)
639     return set_error (No_Input, NULL);
640
641   ctrl->server_local->message_fd = fd;
642   return 0;
643 }
644
645
646 static int 
647 do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode)
648 {
649   CTRL ctrl = assuan_get_pointer (ctx);
650   FILE *fp = assuan_get_data_fp (ctx);
651   char *p;
652   STRLIST list, sl;
653   unsigned int listmode;
654   gpg_error_t err;
655
656   if (!fp)
657     return set_error (General_Error, "no data stream");
658   
659   /* break the line down into an STRLIST */
660   list = NULL;
661   for (p=line; *p; line = p)
662     {
663       while (*p && *p != ' ')
664         p++;
665       if (*p)
666         *p++ = 0;
667       if (*line)
668         {
669           sl = xtrymalloc (sizeof *sl + strlen (line));
670           if (!sl)
671             {
672               free_strlist (list);
673               return ASSUAN_Out_Of_Core;
674             }
675           sl->flags = 0;
676           strcpy_escaped_plus (sl->d, line);
677           sl->next = list;
678           list = sl;
679         }
680     }
681
682   ctrl->with_colons = 1;
683   listmode = mode; 
684   if (ctrl->server_local->list_internal)
685     listmode |= (1<<6);
686   if (ctrl->server_local->list_external)
687     listmode |= (1<<7);
688   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
689   free_strlist (list);
690   return map_to_assuan_status (err);
691 }
692
693 static int 
694 cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
695 {
696   return do_listkeys (ctx, line, 3);
697 }
698
699 static int 
700 cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line)
701 {
702   return do_listkeys (ctx, line, 2);
703 }
704
705 \f
706 /* GENKEY
707
708    Read the parameters in native format from the input fd and write a
709    certificate request to the output.
710  */
711 static int 
712 cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
713 {
714   CTRL ctrl = assuan_get_pointer (ctx);
715   int inp_fd, out_fd;
716   FILE *out_fp;
717   int rc;
718
719   inp_fd = assuan_get_input_fd (ctx);
720   if (inp_fd == -1)
721     return set_error (No_Input, NULL);
722   out_fd = assuan_get_output_fd (ctx);
723   if (out_fd == -1)
724     return set_error (No_Output, NULL);
725
726   out_fp = fdopen ( dup(out_fd), "w");
727   if (!out_fp)
728     return set_error (General_Error, "fdopen() failed");
729   rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
730   fclose (out_fp);
731
732   /* close and reset the fds */
733   assuan_close_input_fd (ctx);
734   assuan_close_output_fd (ctx);
735
736   return map_to_assuan_status (rc);
737 }
738
739
740
741
742 \f
743 /* Tell the assuan library about our commands */
744 static int
745 register_commands (ASSUAN_CONTEXT ctx)
746 {
747   static struct {
748     const char *name;
749     int (*handler)(ASSUAN_CONTEXT, char *line);
750   } table[] = {
751     { "RECIPIENT",     cmd_recipient },
752     { "SIGNER",        cmd_signer },
753     { "ENCRYPT",       cmd_encrypt },
754     { "DECRYPT",       cmd_decrypt },
755     { "VERIFY",        cmd_verify },
756     { "SIGN",          cmd_sign },
757     { "IMPORT",        cmd_import },
758     { "EXPORT",        cmd_export },
759     { "INPUT",         NULL }, 
760     { "OUTPUT",        NULL }, 
761     { "MESSAGE",       cmd_message },
762     { "LISTKEYS",      cmd_listkeys },
763     { "LISTSECRETKEYS",cmd_listsecretkeys },
764     { "GENKEY",        cmd_genkey },
765     { "DELKEYS",       cmd_delkeys },
766     { NULL }
767   };
768   int i, rc;
769
770   for (i=0; table[i].name; i++)
771     {
772       rc = assuan_register_command (ctx, table[i].name, table[i].handler);
773       if (rc)
774         return rc;
775     } 
776   return 0;
777 }
778
779 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
780    set from the command line or config file.  We only require those
781    marked as encrypt-to. */
782 void
783 gpgsm_server (certlist_t default_recplist)
784 {
785   int rc;
786   int filedes[2];
787   ASSUAN_CONTEXT ctx;
788   struct server_control_s ctrl;
789   static const char hello[] = ("GNU Privacy Guard's S/M server "
790                                VERSION " ready");
791
792   memset (&ctrl, 0, sizeof ctrl);
793   gpgsm_init_default_ctrl (&ctrl);
794
795   /* For now we use a simple pipe based server so that we can work
796      from scripts.  We will later add options to run as a daemon and
797      wait for requests on a Unix domain socket */
798   filedes[0] = 0;
799   filedes[1] = 1;
800   rc = assuan_init_pipe_server (&ctx, filedes);
801   if (rc)
802     {
803       log_error ("failed to initialize the server: %s\n",
804                  assuan_strerror(rc));
805       gpgsm_exit (2);
806     }
807   rc = register_commands (ctx);
808   if (rc)
809     {
810       log_error ("failed to the register commands with Assuan: %s\n",
811                  assuan_strerror(rc));
812       gpgsm_exit (2);
813     }
814   if (opt.verbose || opt.debug)
815     {
816       char *tmp = NULL;
817       const char *s1 = getenv ("GPG_AGENT_INFO");
818       const char *s2 = getenv ("DIRMNGR_INFO");
819
820       if (asprintf (&tmp,
821                     "Home: %s\n"
822                     "Config: %s\n"
823                     "AgentInfo: %s\n"
824                     "DirmngrInfo: %s\n"
825                     "%s",
826                     opt.homedir,
827                     opt.config_filename,
828                     s1?s1:"[not set]",
829                     s2?s2:"[not set]",
830                     hello) > 0)
831         {
832           assuan_set_hello_line (ctx, tmp);
833           free (tmp);
834         }
835     }
836   else
837     assuan_set_hello_line (ctx, hello);
838
839   assuan_register_reset_notify (ctx, reset_notify);
840   assuan_register_input_notify (ctx, input_notify);
841   assuan_register_output_notify (ctx, output_notify);
842   assuan_register_option_handler (ctx, option_handler);
843
844   assuan_set_pointer (ctx, &ctrl);
845   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
846   ctrl.server_local->assuan_ctx = ctx;
847   ctrl.server_local->message_fd = -1;
848   ctrl.server_local->list_internal = 1;
849   ctrl.server_local->list_external = 0;
850   ctrl.server_local->default_recplist = default_recplist;
851
852   if (DBG_ASSUAN)
853     assuan_set_log_stream (ctx, log_get_stream ());
854
855   for (;;)
856     {
857       rc = assuan_accept (ctx);
858       if (rc == -1)
859         {
860           break;
861         }
862       else if (rc)
863         {
864           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
865           break;
866         }
867       
868       rc = assuan_process (ctx);
869       if (rc)
870         {
871           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
872           continue;
873         }
874     }
875
876   gpgsm_release_certlist (ctrl.server_local->recplist);
877   ctrl.server_local->recplist = NULL;
878   gpgsm_release_certlist (ctrl.server_local->signerlist);
879   ctrl.server_local->signerlist = NULL;
880
881   assuan_deinit_server (ctx);
882 }
883
884
885 static const char *
886 get_status_string ( int no ) 
887 {
888   const char *s;
889
890   switch (no)
891     {
892     case STATUS_ENTER  : s = "ENTER"; break;
893     case STATUS_LEAVE  : s = "LEAVE"; break;
894     case STATUS_ABORT  : s = "ABORT"; break;
895     case STATUS_NEWSIG : s = "NEWSIG"; break;
896     case STATUS_GOODSIG: s = "GOODSIG"; break;
897     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
898     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
899     case STATUS_BADSIG : s = "BADSIG"; break;
900     case STATUS_ERRSIG : s = "ERRSIG"; break;
901     case STATUS_BADARMOR : s = "BADARMOR"; break;
902     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
903     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
904     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
905     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
906     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
907     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
908     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
909     case STATUS_GET_LINE         : s = "GET_LINE"; break;
910     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
911     case STATUS_GOT_IT   : s = "GOT_IT"; break;
912     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
913     case STATUS_SHM_GET  : s = "SHM_GET"; break;
914     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
915     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
916     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
917     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
918     case STATUS_SIG_ID   : s = "SIG_ID"; break;
919     case STATUS_ENC_TO   : s = "ENC_TO"; break;
920     case STATUS_NODATA   : s = "NODATA"; break;
921     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
922     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
923     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
924     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
925     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
926     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
927     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
928     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
929     case STATUS_GOODMDC  : s = "GOODMDC"; break;
930     case STATUS_BADMDC   : s = "BADMDC"; break;
931     case STATUS_ERRMDC   : s = "ERRMDC"; break;
932     case STATUS_IMPORTED         : s = "IMPORTED"; break;
933     case STATUS_IMPORT_OK        : s = "IMPORT_OK"; break;
934     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
935     case STATUS_FILE_START       : s = "FILE_START"; break;
936     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
937     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
938     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
939     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
940     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
941     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
942     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
943     case STATUS_PROGRESS         : s = "PROGRESS"; break;
944     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
945     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
946     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
947     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
948     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
949     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
950     case STATUS_END_STREAM     : s = "END_STREAM"; break;
951     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
952     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
953     case STATUS_INV_RECP       : s = "INV_RECP"; break;
954     case STATUS_NO_RECP        : s = "NO_RECP"; break;
955     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
956     case STATUS_EXPSIG         : s = "EXPSIG"; break;
957     case STATUS_EXPKEYSIG      : s = "EXPKEYSIG"; break;
958     case STATUS_TRUNCATED      : s = "TRUNCATED"; break;
959     case STATUS_ERROR          : s = "ERROR"; break;
960     case STATUS_IMPORT_PROBLEM : s = "IMPORT_PROBLEM"; break;
961     default: s = "?"; break;
962     }
963   return s;
964 }
965
966
967 gpg_error_t
968 gpgsm_status2 (CTRL ctrl, int no, ...)
969 {
970   gpg_error_t err = 0;
971   va_list arg_ptr;
972   const char *text;
973
974   va_start (arg_ptr, no);
975
976   if (ctrl->no_server && ctrl->status_fd == -1)
977     ; /* No status wanted. */
978   else if (ctrl->no_server)
979     {
980       if (!statusfp)
981         {
982           if (ctrl->status_fd == 1)
983             statusfp = stdout;
984           else if (ctrl->status_fd == 2)
985             statusfp = stderr;
986           else
987             statusfp = fdopen (ctrl->status_fd, "w");
988       
989           if (!statusfp)
990             {
991               log_fatal ("can't open fd %d for status output: %s\n",
992                          ctrl->status_fd, strerror(errno));
993             }
994         }
995       
996       fputs ("[GNUPG:] ", statusfp);
997       fputs (get_status_string (no), statusfp);
998     
999       while ( (text = va_arg (arg_ptr, const char*) ))
1000         {
1001           putc ( ' ', statusfp );
1002           for (; *text; text++) 
1003             {
1004               if (*text == '\n')
1005                 fputs ( "\\n", statusfp );
1006               else if (*text == '\r')
1007                 fputs ( "\\r", statusfp );
1008               else 
1009                 putc ( *(const byte *)text,  statusfp );
1010             }
1011         }
1012       putc ('\n', statusfp);
1013       fflush (statusfp);
1014     }
1015   else 
1016     {
1017       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
1018       char buf[950], *p;
1019       size_t n;
1020
1021       p = buf; 
1022       n = 0;
1023       while ( (text = va_arg (arg_ptr, const char *)) )
1024         {
1025           if (n)
1026             {
1027               *p++ = ' ';
1028               n++;
1029             }
1030           for ( ; *text && n < DIM (buf)-2; n++)
1031             *p++ = *text++;
1032         }
1033       *p = 0;
1034       err = map_assuan_err (assuan_write_status (ctx,
1035                                                  get_status_string (no), buf));
1036     }
1037
1038   va_end (arg_ptr);
1039   return err;
1040 }
1041
1042 gpg_error_t
1043 gpgsm_status (CTRL ctrl, int no, const char *text)
1044 {
1045   return gpgsm_status2 (ctrl, no, text, NULL);
1046 }
1047
1048 gpg_error_t
1049 gpgsm_status_with_err_code (CTRL ctrl, int no, const char *text,
1050                             gpg_err_code_t ec)
1051 {
1052   char buf[30];
1053
1054   sprintf (buf, "%u", (unsigned int)ec);
1055   if (text)
1056     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1057   else
1058     return gpgsm_status2 (ctrl, no, buf, NULL);
1059 }
1060
1061 #if 0
1062 /*
1063  * Write a status line with a buffer using %XX escapes.  If WRAP is >
1064  * 0 wrap the line after this length.  If STRING is not NULL it will
1065  * be prepended to the buffer, no escaping is done for string.
1066  * A wrap of -1 forces spaces not to be encoded as %20.
1067  */
1068 void
1069 write_status_text_and_buffer ( int no, const char *string,
1070                                const char *buffer, size_t len, int wrap )
1071 {
1072     const char *s, *text;
1073     int esc, first;
1074     int lower_limit = ' ';
1075     size_t n, count, dowrap;
1076
1077     if( !statusfp )
1078         return;  /* not enabled */
1079     
1080     if (wrap == -1) {
1081         lower_limit--;
1082         wrap = 0;
1083     }
1084
1085     text = get_status_string (no);
1086     count = dowrap = first = 1;
1087     do {
1088         if (dowrap) {
1089             fprintf (statusfp, "[GNUPG:] %s ", text );
1090             count = dowrap = 0;
1091             if (first && string) {
1092                 fputs (string, statusfp);
1093                 count += strlen (string);
1094             }
1095             first = 0;
1096         }
1097         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
1098             if ( *s == '%' || *(const byte*)s <= lower_limit 
1099                            || *(const byte*)s == 127 ) 
1100                 esc = 1;
1101             if ( wrap && ++count > wrap ) {
1102                 dowrap=1;
1103                 break;
1104             }
1105         }
1106         if (esc) {
1107             s--; n++;
1108         }
1109         if (s != buffer) 
1110             fwrite (buffer, s-buffer, 1, statusfp );
1111         if ( esc ) {
1112             fprintf (statusfp, "%%%02X", *(const byte*)s );
1113             s++; n--;
1114         }
1115         buffer = s;
1116         len = n;
1117         if ( dowrap && len )
1118             putc ( '\n', statusfp );
1119     } while ( len );
1120
1121     putc ('\n',statusfp);
1122     fflush (statusfp);
1123 }
1124 #endif