contrib/git-credential-gnome-keyring.c: set Gnome application name
[git] / contrib / credential / gnome-keyring / git-credential-gnome-keyring.c
1 /*
2  * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
3  *               2012 Philipp A. Hartmann <pah@qo.cx>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 /*
21  * Credits:
22  * - GNOME Keyring API handling originally written by John Szakmeister
23  * - ported to credential helper API by Philipp A. Hartmann
24  */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <glib.h>
32 #include <gnome-keyring.h>
33
34 /*
35  * This credential struct and API is simplified from git's credential.{h,c}
36  */
37 struct credential
38 {
39         char          *protocol;
40         char          *host;
41         unsigned short port;
42         char          *path;
43         char          *username;
44         char          *password;
45 };
46
47 #define CREDENTIAL_INIT \
48   { NULL,NULL,0,NULL,NULL,NULL }
49
50 typedef int (*credential_op_cb)(struct credential*);
51
52 struct credential_operation
53 {
54         char             *name;
55         credential_op_cb op;
56 };
57
58 #define CREDENTIAL_OP_END \
59   { NULL,NULL }
60
61 /* ---------------- common helper functions ----------------- */
62
63 static inline void free_password(char *password)
64 {
65         char *c = password;
66         if (!password)
67                 return;
68
69         while (*c) *c++ = '\0';
70         free(password);
71 }
72
73 static inline void warning(const char *fmt, ...)
74 {
75         va_list ap;
76
77         va_start(ap, fmt);
78         fprintf(stderr, "warning: ");
79         vfprintf(stderr, fmt, ap);
80         fprintf(stderr, "\n" );
81         va_end(ap);
82 }
83
84 static inline void error(const char *fmt, ...)
85 {
86         va_list ap;
87
88         va_start(ap, fmt);
89         fprintf(stderr, "error: ");
90         vfprintf(stderr, fmt, ap);
91         fprintf(stderr, "\n" );
92         va_end(ap);
93 }
94
95 static inline void die_errno(int err)
96 {
97         error("%s", strerror(err));
98         exit(EXIT_FAILURE);
99 }
100
101 static inline char *xstrdup(const char *str)
102 {
103         char *ret = strdup(str);
104         if (!ret)
105                 die_errno(errno);
106
107         return ret;
108 }
109
110 /* ----------------- GNOME Keyring functions ----------------- */
111
112 /* create a special keyring option string, if path is given */
113 static char* keyring_object(struct credential *c)
114 {
115         char* object = NULL;
116
117         if (!c->path)
118                 return object;
119
120         object = (char*) malloc(strlen(c->host)+strlen(c->path)+8);
121         if (!object)
122                 die_errno(errno);
123
124         if (c->port)
125                 sprintf(object,"%s:%hd/%s",c->host,c->port,c->path);
126         else
127                 sprintf(object,"%s/%s",c->host,c->path);
128
129         return object;
130 }
131
132 static int keyring_get(struct credential *c)
133 {
134         char* object = NULL;
135         GList *entries;
136         GnomeKeyringNetworkPasswordData *password_data;
137         GnomeKeyringResult result;
138
139         if (!c->protocol || !(c->host || c->path))
140                 return EXIT_FAILURE;
141
142         object = keyring_object(c);
143
144         result = gnome_keyring_find_network_password_sync(
145                                 c->username,
146                                 NULL /* domain */,
147                                 c->host,
148                                 object,
149                                 c->protocol,
150                                 NULL /* authtype */,
151                                 c->port,
152                                 &entries);
153
154         free(object);
155
156         if (result == GNOME_KEYRING_RESULT_NO_MATCH)
157                 return EXIT_SUCCESS;
158
159         if (result == GNOME_KEYRING_RESULT_CANCELLED)
160                 return EXIT_SUCCESS;
161
162         if (result != GNOME_KEYRING_RESULT_OK) {
163                 error("%s",gnome_keyring_result_to_message(result));
164                 return EXIT_FAILURE;
165         }
166
167         /* pick the first one from the list */
168         password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
169
170         free_password(c->password);
171         c->password = xstrdup(password_data->password);
172
173         if (!c->username)
174                 c->username = xstrdup(password_data->user);
175
176         gnome_keyring_network_password_list_free(entries);
177
178         return EXIT_SUCCESS;
179 }
180
181
182 static int keyring_store(struct credential *c)
183 {
184         guint32 item_id;
185         char  *object = NULL;
186
187         /*
188          * Sanity check that what we are storing is actually sensible.
189          * In particular, we can't make a URL without a protocol field.
190          * Without either a host or pathname (depending on the scheme),
191          * we have no primary key. And without a username and password,
192          * we are not actually storing a credential.
193          */
194         if (!c->protocol || !(c->host || c->path) ||
195             !c->username || !c->password)
196                 return EXIT_FAILURE;
197
198         object = keyring_object(c);
199
200         gnome_keyring_set_network_password_sync(
201                                 GNOME_KEYRING_DEFAULT,
202                                 c->username,
203                                 NULL /* domain */,
204                                 c->host,
205                                 object,
206                                 c->protocol,
207                                 NULL /* authtype */,
208                                 c->port,
209                                 c->password,
210                                 &item_id);
211
212         free(object);
213         return EXIT_SUCCESS;
214 }
215
216 static int keyring_erase(struct credential *c)
217 {
218         char  *object = NULL;
219         GList *entries;
220         GnomeKeyringNetworkPasswordData *password_data;
221         GnomeKeyringResult result;
222
223         /*
224          * Sanity check that we actually have something to match
225          * against. The input we get is a restrictive pattern,
226          * so technically a blank credential means "erase everything".
227          * But it is too easy to accidentally send this, since it is equivalent
228          * to empty input. So explicitly disallow it, and require that the
229          * pattern have some actual content to match.
230          */
231         if (!c->protocol && !c->host && !c->path && !c->username)
232                 return EXIT_FAILURE;
233
234         object = keyring_object(c);
235
236         result = gnome_keyring_find_network_password_sync(
237                                 c->username,
238                                 NULL /* domain */,
239                                 c->host,
240                                 object,
241                                 c->protocol,
242                                 NULL /* authtype */,
243                                 c->port,
244                                 &entries);
245
246         free(object);
247
248         if (result == GNOME_KEYRING_RESULT_NO_MATCH)
249                 return EXIT_SUCCESS;
250
251         if (result == GNOME_KEYRING_RESULT_CANCELLED)
252                 return EXIT_SUCCESS;
253
254         if (result != GNOME_KEYRING_RESULT_OK)
255         {
256                 error("%s",gnome_keyring_result_to_message(result));
257                 return EXIT_FAILURE;
258         }
259
260         /* pick the first one from the list (delete all matches?) */
261         password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
262
263         result = gnome_keyring_item_delete_sync(
264                 password_data->keyring, password_data->item_id);
265
266         gnome_keyring_network_password_list_free(entries);
267
268         if (result != GNOME_KEYRING_RESULT_OK)
269         {
270                 error("%s",gnome_keyring_result_to_message(result));
271                 return EXIT_FAILURE;
272         }
273
274         return EXIT_SUCCESS;
275 }
276
277 /*
278  * Table with helper operation callbacks, used by generic
279  * credential helper main function.
280  */
281 static struct credential_operation const credential_helper_ops[] =
282 {
283         { "get",   keyring_get   },
284         { "store", keyring_store },
285         { "erase", keyring_erase },
286         CREDENTIAL_OP_END
287 };
288
289 /* ------------------ credential functions ------------------ */
290
291 static void credential_init(struct credential *c)
292 {
293         memset(c, 0, sizeof(*c));
294 }
295
296 static void credential_clear(struct credential *c)
297 {
298         free(c->protocol);
299         free(c->host);
300         free(c->path);
301         free(c->username);
302         free_password(c->password);
303
304         credential_init(c);
305 }
306
307 static int credential_read(struct credential *c)
308 {
309         char    buf[1024];
310         size_t line_len;
311         char   *key      = buf;
312         char   *value;
313
314         while (fgets(buf, sizeof(buf), stdin))
315         {
316                 line_len = strlen(buf);
317
318                 if (line_len && buf[line_len-1] == '\n')
319                         buf[--line_len]='\0';
320
321                 if (!line_len)
322                         break;
323
324                 value = strchr(buf,'=');
325                 if (!value) {
326                         warning("invalid credential line: %s", key);
327                         return -1;
328                 }
329                 *value++ = '\0';
330
331                 if (!strcmp(key, "protocol")) {
332                         free(c->protocol);
333                         c->protocol = xstrdup(value);
334                 } else if (!strcmp(key, "host")) {
335                         free(c->host);
336                         c->host = xstrdup(value);
337                         value = strrchr(c->host,':');
338                         if (value) {
339                                 *value++ = '\0';
340                                 c->port = atoi(value);
341                         }
342                 } else if (!strcmp(key, "path")) {
343                         free(c->path);
344                         c->path = xstrdup(value);
345                 } else if (!strcmp(key, "username")) {
346                         free(c->username);
347                         c->username = xstrdup(value);
348                 } else if (!strcmp(key, "password")) {
349                         free_password(c->password);
350                         c->password = xstrdup(value);
351                         while (*value) *value++ = '\0';
352                 }
353                 /*
354                  * Ignore other lines; we don't know what they mean, but
355                  * this future-proofs us when later versions of git do
356                  * learn new lines, and the helpers are updated to match.
357                  */
358         }
359         return 0;
360 }
361
362 static void credential_write_item(FILE *fp, const char *key, const char *value)
363 {
364         if (!value)
365                 return;
366         fprintf(fp, "%s=%s\n", key, value);
367 }
368
369 static void credential_write(const struct credential *c)
370 {
371         /* only write username/password, if set */
372         credential_write_item(stdout, "username", c->username);
373         credential_write_item(stdout, "password", c->password);
374 }
375
376 static void usage(const char *name)
377 {
378         struct credential_operation const *try_op = credential_helper_ops;
379         const char *basename = strrchr(name,'/');
380
381         basename = (basename) ? basename + 1 : name;
382         fprintf(stderr, "usage: %s <", basename);
383         while (try_op->name) {
384                 fprintf(stderr,"%s",(try_op++)->name);
385                 if (try_op->name)
386                         fprintf(stderr,"%s","|");
387         }
388         fprintf(stderr,"%s",">\n");
389 }
390
391 int main(int argc, char *argv[])
392 {
393         int ret = EXIT_SUCCESS;
394
395         struct credential_operation const *try_op = credential_helper_ops;
396         struct credential                  cred   = CREDENTIAL_INIT;
397
398         if (!argv[1]) {
399                 usage(argv[0]);
400                 exit(EXIT_FAILURE);
401         }
402
403         g_set_application_name("Git Credential Helper");
404
405         /* lookup operation callback */
406         while (try_op->name && strcmp(argv[1], try_op->name))
407                 try_op++;
408
409         /* unsupported operation given -- ignore silently */
410         if (!try_op->name || !try_op->op)
411                 goto out;
412
413         ret = credential_read(&cred);
414         if (ret)
415                 goto out;
416
417         /* perform credential operation */
418         ret = (*try_op->op)(&cred);
419
420         credential_write(&cred);
421
422 out:
423         credential_clear(&cred);
424         return ret;
425 }