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