tests: Flush stdout in the fake pinentry.
[gnupg.git] / tests / openpgp / fake-pinentry.c
1 /* Fake pinentry program for the OpenPGP test suite.
2  *
3  * Copyright (C) 2016 g10 code GmbH
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdarg.h>
25
26 FILE *log_stream;
27
28 int
29 reply (const char *fmt, ...)
30 {
31   int result;
32   va_list ap;
33
34   if (log_stream)
35     {
36       fprintf (log_stream, "> ");
37       va_start (ap, fmt);
38       vfprintf (log_stream, fmt, ap);
39       va_end (ap);
40     }
41   va_start (ap, fmt);
42   result = vprintf (fmt, ap);
43   va_end (ap);
44
45   fflush (stdout);
46   return result;
47 }
48
49 /* Return the first line from FNAME, removing it from the file.  */
50 char *
51 get_passphrase (const char *fname)
52 {
53   char *passphrase = NULL;
54   size_t fname_len;
55   char *fname_new;
56   FILE *source, *sink;
57   char linebuf[80];
58
59   fname_len = strlen (fname);
60   fname_new = malloc (fname_len + 5);
61   if (fname_new == NULL)
62     {
63       perror ("malloc");
64       exit (1);
65     }
66   snprintf (fname_new, fname_len + 5, "%s.new", fname);
67
68   source = fopen (fname, "r");
69   if (! source)
70     {
71       perror (fname);
72       exit (1);
73     }
74
75   sink = fopen (fname_new, "w");
76   if (! sink)
77     {
78       perror (fname_new);
79       exit (1);
80     }
81
82   while (fgets (linebuf, sizeof linebuf, source))
83     {
84       linebuf[sizeof linebuf - 1] = 0;
85       if (passphrase == NULL)
86         {
87           passphrase = strdup (linebuf);
88           if (passphrase == NULL)
89             {
90               perror ("strdup");
91               exit (1);
92             }
93         }
94       else
95         fputs (linebuf, sink);
96     }
97
98   if (ferror (source))
99     {
100       perror (fname);
101       exit (1);
102     }
103
104   if (ferror (sink))
105     {
106       perror (fname_new);
107       exit (1);
108     }
109
110   fclose (source);
111   fclose (sink);
112   rename (fname_new, fname);
113   return passphrase;
114 }
115
116 \f
117 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
118
119 /* Skip over options in LINE.
120
121    Blanks after the options are also removed.  Options are indicated
122    by two leading dashes followed by a string consisting of non-space
123    characters.  The special option "--" indicates an explicit end of
124    options; all what follows will not be considered an option.  The
125    first no-option string also indicates the end of option parsing. */
126 char *
127 skip_options (const char *line)
128 {
129   while (spacep (line))
130     line++;
131   while (*line == '-' && line[1] == '-')
132     {
133       while (*line && !spacep (line))
134         line++;
135       while (spacep (line))
136         line++;
137     }
138   return (char*) line;
139 }
140
141
142 /* Return a pointer to the argument of the option with NAME.  If such
143    an option is not given, NULL is returned. */
144 char *
145 option_value (const char *line, const char *name)
146 {
147   char *s;
148   int n = strlen (name);
149
150   s = strstr (line, name);
151   if (s && s >= skip_options (line))
152     return NULL;
153   if (s && (s == line || spacep (s-1))
154       && s[n] && (spacep (s+n) || s[n] == '='))
155     {
156       s += n + 1;
157       s += strspn (s, " ");
158       if (*s && !spacep(s))
159         return s;
160     }
161   return NULL;
162 }
163
164 int
165 main (int argc, char **argv)
166 {
167   char *args;
168   char *logfile;
169   char *passphrasefile;
170   char *passphrase;
171
172   /* We get our options via PINENTRY_USER_DATA.  */
173   (void) argc, (void) argv;
174
175   setvbuf (stdin, NULL, _IOLBF, BUFSIZ);
176   setvbuf (stdout, NULL, _IOLBF, BUFSIZ);
177
178   args = getenv ("PINENTRY_USER_DATA");
179   if (! args)
180     args = "";
181
182   logfile = option_value (args, "--logfile");
183   if (logfile)
184     {
185       char *p = logfile, more;
186       while (*p && ! spacep (p))
187         p++;
188       more = !! *p;
189       *p = 0;
190       args = more ? p+1 : p;
191
192       log_stream = fopen (logfile, "a");
193       if (! log_stream)
194         {
195           perror (logfile);
196           return 1;
197         }
198     }
199
200   passphrasefile = option_value (args, "--passphrasefile");
201   if (passphrasefile)
202     {
203       char *p = passphrasefile, more;
204       while (*p && ! spacep (p))
205         p++;
206       more = !! *p;
207       *p = 0;
208       args = more ? p+1 : p;
209
210       passphrase = get_passphrase (passphrasefile);
211       if (! passphrase)
212         {
213           reply ("# Passphrasefile '%s' is empty.  Terminating.\n",
214                  passphrasefile);
215           return 1;
216         }
217
218       p = passphrase + strlen (passphrase) - 1;
219       if (*p == '\n')
220         *p = 0;
221     }
222   else
223     {
224       passphrase = skip_options (args);
225       if (*passphrase == 0)
226         passphrase = "no PINENTRY_USER_DATA -- using default passphrase";
227     }
228
229   reply ("# fake-pinentry started.  Passphrase='%s'.\n", passphrase);
230   reply ("OK - what's up?\n");
231
232   while (! feof (stdin))
233     {
234       char buffer[1024];
235
236       if (fgets (buffer, sizeof buffer, stdin) == NULL)
237         break;
238
239       if (log_stream)
240         fprintf (log_stream, "< %s", buffer);
241
242       if (strncmp (buffer, "GETPIN", 6) == 0)
243         reply ("D %s\n", passphrase);
244       else if (strncmp (buffer, "BYE", 3) == 0)
245         {
246           reply ("OK\n");
247           break;
248         }
249
250       reply ("OK\n");
251     }
252
253   reply ("# Connection terminated.\n");
254   if (log_stream)
255     fclose (log_stream);
256
257   return 0;
258 }