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