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