tests: Improve handling of Windows newlines.
[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   return result;
46 }
47
48 /* Return the first line from FNAME, removing it from the file.  */
49 char *
50 get_passphrase (const char *fname)
51 {
52   char *passphrase = NULL;
53   size_t fname_len;
54   char *fname_new;
55   FILE *source, *sink;
56   char linebuf[80];
57
58   fname_len = strlen (fname);
59   fname_new = malloc (fname_len + 5);
60   if (fname_new == NULL)
61     {
62       perror ("malloc");
63       exit (1);
64     }
65   snprintf (fname_new, fname_len + 5, "%s.new", fname);
66
67   source = fopen (fname, "r");
68   if (! source)
69     {
70       perror (fname);
71       exit (1);
72     }
73
74   sink = fopen (fname_new, "w");
75   if (! sink)
76     {
77       perror (fname_new);
78       exit (1);
79     }
80
81   while (fgets (linebuf, sizeof linebuf, source))
82     {
83       linebuf[sizeof linebuf - 1] = 0;
84       if (passphrase == NULL)
85         {
86           passphrase = strdup (linebuf);
87           if (passphrase == NULL)
88             {
89               perror ("strdup");
90               exit (1);
91             }
92         }
93       else
94         fputs (linebuf, sink);
95     }
96
97   if (ferror (source))
98     {
99       perror (fname);
100       exit (1);
101     }
102
103   if (ferror (sink))
104     {
105       perror (fname_new);
106       exit (1);
107     }
108
109   fclose (source);
110   fclose (sink);
111   rename (fname_new, fname);
112   return passphrase;
113 }
114
115 \f
116 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
117
118 /* Skip over options in LINE.
119
120    Blanks after the options are also removed.  Options are indicated
121    by two leading dashes followed by a string consisting of non-space
122    characters.  The special option "--" indicates an explicit end of
123    options; all what follows will not be considered an option.  The
124    first no-option string also indicates the end of option parsing. */
125 char *
126 skip_options (const char *line)
127 {
128   while (spacep (line))
129     line++;
130   while (*line == '-' && line[1] == '-')
131     {
132       while (*line && !spacep (line))
133         line++;
134       while (spacep (line))
135         line++;
136     }
137   return (char*) line;
138 }
139
140
141 /* Return a pointer to the argument of the option with NAME.  If such
142    an option is not given, NULL is returned. */
143 char *
144 option_value (const char *line, const char *name)
145 {
146   char *s;
147   int n = strlen (name);
148
149   s = strstr (line, name);
150   if (s && s >= skip_options (line))
151     return NULL;
152   if (s && (s == line || spacep (s-1))
153       && s[n] && (spacep (s+n) || s[n] == '='))
154     {
155       s += n + 1;
156       s += strspn (s, " ");
157       if (*s && !spacep(s))
158         return s;
159     }
160   return NULL;
161 }
162
163 int
164 main (int argc, char **argv)
165 {
166   char *args;
167   char *logfile;
168   char *passphrasefile;
169   char *passphrase;
170
171   /* We get our options via PINENTRY_USER_DATA.  */
172   (void) argc, (void) argv;
173
174   setvbuf (stdin, NULL, _IOLBF, BUFSIZ);
175   setvbuf (stdout, NULL, _IOLBF, BUFSIZ);
176
177   args = getenv ("PINENTRY_USER_DATA");
178   if (! args)
179     args = "";
180
181   logfile = option_value (args, "--logfile");
182   if (logfile)
183     {
184       char *p = logfile, more;
185       while (*p && ! spacep (p))
186         p++;
187       more = !! *p;
188       *p = 0;
189       args = more ? p+1 : p;
190
191       log_stream = fopen (logfile, "a");
192       if (! log_stream)
193         {
194           perror (logfile);
195           return 1;
196         }
197     }
198
199   passphrasefile = option_value (args, "--passphrasefile");
200   if (passphrasefile)
201     {
202       char *p = passphrasefile, more;
203       while (*p && ! spacep (p))
204         p++;
205       more = !! *p;
206       *p = 0;
207       args = more ? p+1 : p;
208
209       passphrase = get_passphrase (passphrasefile);
210       if (! passphrase)
211         {
212           reply ("# Passphrasefile '%s' is empty.  Terminating.\n",
213                  passphrasefile);
214           return 1;
215         }
216
217       p = passphrase + strlen (passphrase) - 1;
218       if (*p == '\n')
219         *p = 0;
220     }
221   else
222     {
223       passphrase = skip_options (args);
224       if (*passphrase == 0)
225         passphrase = "no PINENTRY_USER_DATA -- using default passphrase";
226     }
227
228   reply ("# fake-pinentry started.  Passphrase='%s'.\n", passphrase);
229   reply ("OK - what's up?\n");
230
231   while (! feof (stdin))
232     {
233       char buffer[1024];
234
235       if (fgets (buffer, sizeof buffer, stdin) == NULL)
236         break;
237
238       if (log_stream)
239         fprintf (log_stream, "< %s", buffer);
240
241       if (strncmp (buffer, "GETPIN", 6) == 0)
242         reply ("D %s\n", passphrase);
243       else if (strncmp (buffer, "BYE", 3) == 0)
244         {
245           reply ("OK\n");
246           break;
247         }
248
249       reply ("OK\n");
250     }
251
252   reply ("# Connection terminated.\n");
253   if (log_stream)
254     fclose (log_stream);
255
256   return 0;
257 }