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