1 /*
2  * Seahorse
3  *
4  * Copyright (C) 2004 - 2006 Stefan Walter
5  * Copyright (C) 2011 Collabora Ltd.
6  *
7  * This program 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 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program 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.
15  * See the GNU General Public License for more details.
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 
27 #include <glib/gi18n.h>
28 
29 #include "seahorse-ldap-source.h"
30 
31 #include "seahorse-pgp-key.h"
32 #include "seahorse-pgp-subkey.h"
33 #include "seahorse-pgp-uid.h"
34 
35 #include "seahorse-common.h"
36 
37 #include "libseahorse/seahorse-progress.h"
38 #include "libseahorse/seahorse-util.h"
39 
40 #include <ldap.h>
41 
42 #ifdef WITH_LDAP
43 
44 /* Amount of keys to load in a batch */
45 #define DEFAULT_LOAD_BATCH 30
46 
47 struct _SeahorseLDAPSource {
48     SeahorseServerSource parent;
49 };
50 
51 /* -----------------------------------------------------------------------------
52  * SERVER INFO
53  */
54 
55 typedef struct _LDAPServerInfo {
56     char *base_dn;              /* The base dn where PGP keys are found */
57     char *key_attr;             /* The attribute of PGP key data */
58     guint version;              /* The version of the PGP server software */
59 } LDAPServerInfo;
60 
61 static void
free_ldap_server_info(LDAPServerInfo * sinfo)62 free_ldap_server_info (LDAPServerInfo *sinfo)
63 {
64     if (sinfo) {
65         g_free (sinfo->base_dn);
66         g_free (sinfo->key_attr);
67         g_free (sinfo);
68     }
69 }
70 
71 static void
set_ldap_server_info(SeahorseLDAPSource * lsrc,LDAPServerInfo * sinfo)72 set_ldap_server_info (SeahorseLDAPSource *lsrc, LDAPServerInfo *sinfo)
73 {
74     g_object_set_data_full (G_OBJECT (lsrc), "server-info", sinfo,
75                             (GDestroyNotify)free_ldap_server_info);
76 }
77 
78 static LDAPServerInfo*
get_ldap_server_info(SeahorseLDAPSource * lsrc,gboolean force)79 get_ldap_server_info (SeahorseLDAPSource *lsrc, gboolean force)
80 {
81     LDAPServerInfo *sinfo;
82 
83     sinfo = g_object_get_data (G_OBJECT (lsrc), "server-info");
84 
85     /* When we're asked to force getting the data, we fill in
86      * some defaults */
87     if (!sinfo && force) {
88         sinfo = g_new0 (LDAPServerInfo, 1);
89         sinfo->base_dn = g_strdup ("OU=ACTIVE,O=PGP KEYSPACE,C=US");
90         sinfo->key_attr = g_strdup ("pgpKey");
91         sinfo->version = 0;
92         set_ldap_server_info (lsrc, sinfo);
93     }
94 
95     return sinfo;
96 }
97 
98 static void
destroy_ldap(gpointer data)99 destroy_ldap (gpointer data)
100 {
101     if (data)
102         ldap_unbind_ext ((LDAP *) data, NULL, NULL);
103 }
104 
105 /* -----------------------------------------------------------------------------
106  *  LDAP HELPERS
107  */
108 
109 #define LDAP_ERROR_DOMAIN (get_ldap_error_domain())
110 
111 static char**
get_ldap_values(LDAP * ld,LDAPMessage * entry,const char * attribute)112 get_ldap_values (LDAP *ld, LDAPMessage *entry, const char *attribute)
113 {
114     GArray *array;
115     struct berval **bv;
116     char *value;
117     int num, i;
118 
119     bv = ldap_get_values_len (ld, entry, attribute);
120     if (!bv)
121         return NULL;
122 
123     array = g_array_new (TRUE, TRUE, sizeof (char*));
124     num = ldap_count_values_len (bv);
125     for(i = 0; i < num; i++) {
126         value = g_strndup (bv[i]->bv_val, bv[i]->bv_len);
127         g_array_append_val(array, value);
128     }
129 
130     return (char**)g_array_free (array, FALSE);
131 }
132 
133 #ifdef WITH_DEBUG
134 
135 static void
dump_ldap_entry(LDAP * ld,LDAPMessage * res)136 dump_ldap_entry (LDAP *ld, LDAPMessage *res)
137 {
138     BerElement *pos;
139     char *t;
140 
141     t = ldap_get_dn (ld, res);
142     g_debug ("dn: %s", t);
143     ldap_memfree (t);
144 
145     for (t = ldap_first_attribute (ld, res, &pos); t;
146          t = ldap_next_attribute (ld, res, pos)) {
147         g_auto(GStrv) values = NULL;
148 
149         values = get_ldap_values (ld, res, t);
150         for (char **v = values; *v; v++)
151             g_debug ("%s: %s", t, *v);
152 
153         ldap_memfree (t);
154     }
155 
156     ber_free (pos, 0);
157 }
158 
159 #endif /* WITH_DEBUG */
160 
161 static GQuark
get_ldap_error_domain()162 get_ldap_error_domain ()
163 {
164     static GQuark q = 0;
165     if(q == 0)
166         q = g_quark_from_static_string ("seahorse-ldap-error");
167     return q;
168 }
169 
170 static char*
get_string_attribute(LDAP * ld,LDAPMessage * res,const char * attribute)171 get_string_attribute (LDAP *ld, LDAPMessage *res, const char *attribute)
172 {
173     g_auto(GStrv) vals = NULL;
174     char *v;
175 
176     vals = get_ldap_values (ld, res, attribute);
177     if (!vals)
178         return NULL;
179     v = vals[0] ? g_strdup (vals[0]) : NULL;
180     return v;
181 }
182 
183 static gboolean
get_boolean_attribute(LDAP * ld,LDAPMessage * res,const char * attribute)184 get_boolean_attribute (LDAP* ld, LDAPMessage *res, const char *attribute)
185 {
186     g_auto(GStrv) vals = NULL;
187     gboolean b;
188 
189     vals = get_ldap_values (ld, res, attribute);
190     if (!vals)
191         return FALSE;
192     b = vals[0] && atoi (vals[0]) == 1;
193     return b;
194 }
195 
196 static long int
get_int_attribute(LDAP * ld,LDAPMessage * res,const char * attribute)197 get_int_attribute (LDAP* ld, LDAPMessage *res, const char *attribute)
198 {
199     g_auto(GStrv) vals = NULL;
200     long int d;
201 
202     vals = get_ldap_values (ld, res, attribute);
203     if (!vals)
204         return 0;
205     d = vals[0] ? atoi (vals[0]) : 0;
206     return d;
207 }
208 
209 static long int
get_date_attribute(LDAP * ld,LDAPMessage * res,const char * attribute)210 get_date_attribute (LDAP* ld, LDAPMessage *res, const char *attribute)
211 {
212     g_auto(GStrv) vals = NULL;
213     struct tm t;
214     long int d = 0;
215 
216     vals = get_ldap_values (ld, res, attribute);
217     if (!vals)
218         return 0;
219 
220     if (vals[0]) {
221         memset(&t, 0, sizeof (t));
222 
223         /* YYYYMMDDHHmmssZ */
224         sscanf(vals[0], "%4d%2d%2d%2d%2d%2d",
225             &t.tm_year, &t.tm_mon, &t.tm_mday,
226             &t.tm_hour, &t.tm_min, &t.tm_sec);
227 
228         t.tm_year -= 1900;
229         t.tm_isdst = -1;
230         t.tm_mon--;
231 
232         d = mktime (&t);
233     }
234 
235     return d;
236 }
237 
238 static const char*
get_algo_attribute(LDAP * ld,LDAPMessage * res,const char * attribute)239 get_algo_attribute (LDAP* ld, LDAPMessage *res, const char *attribute)
240 {
241     g_auto(GStrv) vals = NULL;
242     const char *a = NULL;
243 
244     vals = get_ldap_values (ld, res, attribute);
245     if (!vals)
246         return 0;
247 
248     if (vals[0]) {
249         if (g_ascii_strcasecmp (vals[0], "DH/DSS") == 0 ||
250             g_ascii_strcasecmp (vals[0], "Elg") == 0 ||
251             g_ascii_strcasecmp (vals[0], "Elgamal") == 0 ||
252             g_ascii_strcasecmp (vals[0], "DSS/DH") == 0)
253             a = "Elgamal";
254         if (g_ascii_strcasecmp (vals[0], "RSA") == 0)
255             a = "RSA";
256         if (g_ascii_strcasecmp (vals[0], "DSA") == 0)
257             a = "DSA";
258     }
259 
260     return a;
261 }
262 
263 /*
264  * Escapes a value so it's safe to use in an LDAP filter. Also trims
265  * any spaces which cause problems with some LDAP servers.
266  */
267 static char*
escape_ldap_value(const char * v)268 escape_ldap_value (const char *v)
269 {
270     GString *value;
271     char* result;
272 
273     g_assert (v);
274     value = g_string_sized_new (strlen(v));
275 
276     for ( ; *v; v++) {
277         switch(*v) {
278         case '#': case ',': case '+': case '\\':
279         case '/': case '\"': case '<': case '>': case ';':
280             value = g_string_append_c (value, '\\');
281             value = g_string_append_c (value, *v);
282             continue;
283         };
284 
285         if(*v < 32 || *v > 126) {
286             g_string_append_printf (value, "\\%02X", *v);
287             continue;
288         }
289 
290         value = g_string_append_c (value, *v);
291     }
292 
293     result = g_string_free (value, FALSE);
294     g_strstrip (result);
295     return result;
296 }
297 
298 typedef gboolean (*SeahorseLdapCallback)   (LDAPMessage *result,
299                                             gpointer user_data);
300 
301 typedef struct {
302     GSource source;
303     LDAP *ldap;
304     int ldap_op;
305     GCancellable *cancellable;
306     gboolean cancelled;
307     int cancelled_sig;
308 } SeahorseLdapGSource;
309 
310 static gboolean
seahorse_ldap_gsource_prepare(GSource * gsource,int * timeout)311 seahorse_ldap_gsource_prepare (GSource *gsource,
312                                int *timeout)
313 {
314     SeahorseLdapGSource *ldap_gsource = (SeahorseLdapGSource *)gsource;
315 
316     if (ldap_gsource->cancelled)
317         return TRUE;
318 
319     /* No other way, but to poll */
320     *timeout = 50;
321     return FALSE;
322 }
323 
324 static gboolean
seahorse_ldap_gsource_check(GSource * gsource)325 seahorse_ldap_gsource_check (GSource *gsource)
326 {
327     return TRUE;
328 }
329 
330 static gboolean
seahorse_ldap_gsource_dispatch(GSource * gsource,GSourceFunc callback,gpointer user_data)331 seahorse_ldap_gsource_dispatch (GSource *gsource,
332                                 GSourceFunc callback,
333                                 gpointer user_data)
334 {
335     SeahorseLdapGSource *ldap_gsource = (SeahorseLdapGSource *)gsource;
336     struct timeval timeout;
337     LDAPMessage *result;
338     gboolean ret;
339     int rc, i;
340 
341     if (ldap_gsource->cancelled) {
342         ((SeahorseLdapCallback)callback) (NULL, user_data);
343         return FALSE;
344     }
345 
346     for (i = 0; i < DEFAULT_LOAD_BATCH; i++) {
347 
348         /* This effects a poll */
349         timeout.tv_sec = 0;
350         timeout.tv_usec = 0;
351 
352         rc = ldap_result (ldap_gsource->ldap, ldap_gsource->ldap_op,
353                           0, &timeout, &result);
354         if (rc == -1) {
355             g_warning ("ldap_result failed with rc = %d, errno = %s",
356                        rc, g_strerror (errno));
357             return G_SOURCE_REMOVE;
358         }
359 
360         /* Timeout */
361         if (rc == 0)
362             return G_SOURCE_CONTINUE;
363 
364         ret = ((SeahorseLdapCallback)callback) (result, user_data);
365         ldap_msgfree (result);
366 
367         if (!ret)
368             return G_SOURCE_REMOVE;
369     }
370 
371     return G_SOURCE_CONTINUE;
372 }
373 
374 static void
seahorse_ldap_gsource_finalize(GSource * gsource)375 seahorse_ldap_gsource_finalize (GSource *gsource)
376 {
377     SeahorseLdapGSource *ldap_gsource = (SeahorseLdapGSource *)gsource;
378     g_cancellable_disconnect (ldap_gsource->cancellable,
379                               ldap_gsource->cancelled_sig);
380     g_clear_object (&ldap_gsource->cancellable);
381 }
382 
383 static GSourceFuncs seahorse_ldap_gsource_funcs = {
384     seahorse_ldap_gsource_prepare,
385     seahorse_ldap_gsource_check,
386     seahorse_ldap_gsource_dispatch,
387     seahorse_ldap_gsource_finalize,
388 };
389 
390 static void
on_ldap_gsource_cancelled(GCancellable * cancellable,gpointer user_data)391 on_ldap_gsource_cancelled (GCancellable *cancellable,
392                            gpointer user_data)
393 {
394     SeahorseLdapGSource *ldap_gsource = user_data;
395     ldap_gsource->cancelled = TRUE;
396 }
397 
398 static GSource *
seahorse_ldap_gsource_new(LDAP * ldap,int ldap_op,GCancellable * cancellable)399 seahorse_ldap_gsource_new (LDAP *ldap,
400                            int ldap_op,
401                            GCancellable *cancellable)
402 {
403     GSource *gsource;
404     SeahorseLdapGSource *ldap_gsource;
405 
406     gsource = g_source_new (&seahorse_ldap_gsource_funcs,
407                             sizeof (SeahorseLdapGSource));
408 
409     ldap_gsource = (SeahorseLdapGSource *)gsource;
410     ldap_gsource->ldap = ldap;
411     ldap_gsource->ldap_op = ldap_op;
412 
413     if (cancellable) {
414         ldap_gsource->cancellable = g_object_ref (cancellable);
415         ldap_gsource->cancelled_sig = g_cancellable_connect (cancellable,
416                                                              G_CALLBACK (on_ldap_gsource_cancelled),
417                                                              ldap_gsource, NULL);
418     }
419 
420     return gsource;
421 }
422 
423 static gboolean
seahorse_ldap_source_propagate_error(SeahorseLDAPSource * self,int rc,GError ** error)424 seahorse_ldap_source_propagate_error (SeahorseLDAPSource *self,
425                                       int rc, GError **error)
426 {
427     g_autofree char *uri = NULL;
428 
429     if (rc == LDAP_SUCCESS)
430         return FALSE;
431 
432     uri = seahorse_place_get_uri (SEAHORSE_PLACE (self));
433     g_set_error (error, LDAP_ERROR_DOMAIN, rc, _("Couldn’t communicate with %s: %s"),
434                  uri, ldap_err2string (rc));
435 
436     return TRUE;
437 }
438 
439 typedef struct {
440     LDAP *ldap;
441 } ConnectClosure;
442 
443 static void
connect_closure_free(gpointer data)444 connect_closure_free (gpointer data)
445 {
446     ConnectClosure *closure = data;
447     if (closure->ldap)
448         ldap_unbind_ext (closure->ldap, NULL, NULL);
449     g_free (closure);
450 }
451 
452 static gboolean
on_connect_server_info_completed(LDAPMessage * result,gpointer user_data)453 on_connect_server_info_completed (LDAPMessage *result,
454                                   gpointer user_data)
455 {
456     GTask *task = G_TASK (user_data);
457     ConnectClosure *closure = g_task_get_task_data (task);
458     GCancellable *cancellable = g_task_get_cancellable (task);
459     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (g_task_get_source_object (task));
460     char *message;
461     int code;
462     int type;
463     int rc;
464 
465     type = ldap_msgtype (result);
466     g_return_val_if_fail (type == LDAP_RES_SEARCH_ENTRY || type == LDAP_RES_SEARCH_RESULT, FALSE);
467 
468     /* If we have results then fill in the server info */
469     if (type == LDAP_RES_SEARCH_ENTRY) {
470         LDAPServerInfo *sinfo;
471 
472         g_debug ("Server Info Result");
473 #ifdef WITH_DEBUG
474         dump_ldap_entry (closure->ldap, result);
475 #endif
476 
477         /* NOTE: When adding attributes here make sure to add them to kServerAttributes */
478         sinfo = g_new0 (LDAPServerInfo, 1);
479         sinfo->version = get_int_attribute (closure->ldap, result, "version");
480         sinfo->base_dn = get_string_attribute (closure->ldap, result, "basekeyspacedn");
481         if (!sinfo->base_dn)
482             sinfo->base_dn = get_string_attribute (closure->ldap, result, "pgpbasekeyspacedn");
483         sinfo->key_attr = g_strdup (sinfo->version > 1 ? "pgpkeyv2" : "pgpkey");
484         set_ldap_server_info (self, sinfo);
485 
486         return G_SOURCE_CONTINUE;
487     }
488 
489     rc = ldap_parse_result (closure->ldap, result, &code, NULL, &message,
490                             NULL, NULL, 0);
491     g_return_val_if_fail (rc == LDAP_SUCCESS, FALSE);
492 
493     if (code != LDAP_SUCCESS)
494         g_warning ("operation to get LDAP server info failed: %s", message);
495 
496     ldap_memfree (message);
497 
498     g_task_return_pointer (task, g_steal_pointer (&closure->ldap), destroy_ldap);
499     seahorse_progress_end (cancellable, task);
500     return G_SOURCE_REMOVE;
501 }
502 
503 static const char *SERVER_ATTRIBUTES[] = {
504     "basekeyspacedn",
505     "pgpbasekeyspacedn",
506     "version",
507     NULL
508 };
509 
510 static gboolean
on_connect_bind_completed(LDAPMessage * result,gpointer user_data)511 on_connect_bind_completed (LDAPMessage *result,
512                            gpointer user_data)
513 {
514     GTask *task = G_TASK (user_data);
515     ConnectClosure *closure = g_task_get_task_data (task);
516     GCancellable *cancellable = g_task_get_cancellable (task);
517     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (g_task_get_source_object (task));
518     LDAPServerInfo *sinfo;
519     g_autoptr(GError) error = NULL;
520     char *message;
521     int ldap_op;
522     int code;
523     int rc;
524     g_autoptr(GSource) gsource = NULL;
525 
526     g_return_val_if_fail (ldap_msgtype (result) == LDAP_RES_BIND, FALSE);
527 
528     /* The result of the bind operation */
529     rc = ldap_parse_result (closure->ldap, result, &code, NULL, &message,
530                             NULL, NULL, 0);
531     g_return_val_if_fail (rc == LDAP_SUCCESS, FALSE);
532     ldap_memfree (message);
533 
534     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
535         g_task_return_error (task, g_steal_pointer (&error));
536         return G_SOURCE_REMOVE;
537     }
538 
539     /* Check if we need server info */
540     sinfo = get_ldap_server_info (self, FALSE);
541     if (sinfo != NULL) {
542         g_task_return_pointer (task, g_steal_pointer (&closure->ldap), destroy_ldap);
543         seahorse_progress_end (cancellable, task);
544         return G_SOURCE_REMOVE;
545     }
546 
547     /* Retrieve the server info */
548     rc = ldap_search_ext (closure->ldap, "cn=PGPServerInfo", LDAP_SCOPE_BASE,
549                           "(objectclass=*)", (char **)SERVER_ATTRIBUTES, 0,
550                           NULL, NULL, NULL, 0, &ldap_op);
551 
552     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
553         g_task_return_error (task, g_steal_pointer (&error));
554         return G_SOURCE_REMOVE;
555     }
556 
557     gsource = seahorse_ldap_gsource_new (closure->ldap, ldap_op, cancellable);
558     g_source_set_callback (gsource,
559                            G_SOURCE_FUNC (on_connect_server_info_completed),
560                            g_object_ref (task), g_object_unref);
561     g_source_attach (gsource, g_main_context_default ());
562 
563     return G_SOURCE_REMOVE;
564 }
565 
566 static void
on_address_resolved(GObject * src_object,GAsyncResult * res,gpointer user_data)567 on_address_resolved (GObject      *src_object,
568                      GAsyncResult *res,
569                      gpointer      user_data)
570 {
571     GSocketAddressEnumerator *enumer = G_SOCKET_ADDRESS_ENUMERATOR (src_object);
572     g_autoptr(GTask) task = user_data;
573     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (g_task_get_source_object (task));
574     ConnectClosure *closure = g_task_get_task_data (task);
575     GCancellable *cancellable = g_task_get_cancellable (task);
576     g_autofree char *uri = NULL;
577     g_autoptr(GSocketAddress) addr = NULL;
578     g_autoptr(GError) error = NULL;
579     g_autofree char *addr_str = NULL;
580     g_autofree char *resolved_url = NULL;
581     int rc;
582     struct berval cred;
583     int ldap_op;
584     g_autoptr(GSource) gsource = NULL;
585 
586     /* Note: this is the original (unresolved) URI */
587     uri = seahorse_place_get_uri (SEAHORSE_PLACE (self));
588 
589     /* Get the resolved IP */
590     addr = g_socket_address_enumerator_next_finish (enumer, res, &error);
591     if (!addr) {
592         g_task_return_new_error (task, SEAHORSE_ERROR, -1,
593                                  _("Couldn’t resolve address %s"), uri);
594         return;
595     }
596 
597     /* Now that we've resolved our address, connect via IP */
598     seahorse_progress_update (cancellable, task, _("Connecting to: %s"), uri);
599 
600     /* Re-create the URL with the resolved IP */
601     addr_str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (addr));
602     resolved_url = g_strdup_printf ("ldap://%s", addr_str);
603     rc = ldap_initialize (&closure->ldap, resolved_url);
604 
605     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
606         g_task_return_error (task, g_steal_pointer (&error));
607         return;
608     }
609 
610     /* Start the bind operation */
611     cred.bv_val = "";
612     cred.bv_len = 0;
613 
614     rc = ldap_sasl_bind (closure->ldap, NULL, LDAP_SASL_SIMPLE, &cred, NULL,
615                          NULL, &ldap_op);
616     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
617         g_task_return_error (task, g_steal_pointer (&error));
618         return;
619     }
620 
621     gsource = seahorse_ldap_gsource_new (closure->ldap, ldap_op, cancellable);
622     g_source_set_callback (gsource, G_SOURCE_FUNC (on_connect_bind_completed),
623                            g_object_ref (task), g_object_unref);
624     g_source_attach (gsource, g_main_context_default ());
625 }
626 
627 static void
seahorse_ldap_source_connect_async(SeahorseLDAPSource * source,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)628 seahorse_ldap_source_connect_async (SeahorseLDAPSource *source,
629                                     GCancellable *cancellable,
630                                     GAsyncReadyCallback callback,
631                                     gpointer user_data)
632 {
633     g_autoptr(GTask) task = NULL;
634     ConnectClosure *closure;
635     g_autofree char *uri = NULL;
636     g_autoptr(GSocketConnectable) addr = NULL;
637     g_autoptr(GSocketAddressEnumerator) addr_enumer = NULL;
638     g_autoptr(GError) error = NULL;
639 
640     task = g_task_new (source, cancellable, callback, user_data);
641     g_task_set_source_tag (task, seahorse_ldap_source_connect_async);
642 
643     closure = g_new0 (ConnectClosure, 1);
644     g_task_set_task_data (task, closure, connect_closure_free);
645 
646     /* Take the URI & turn it into a GNetworkAddress, to do address resolving */
647     uri = seahorse_place_get_uri (SEAHORSE_PLACE (source));
648     g_return_if_fail (uri && uri[0]);
649 
650     addr = g_network_address_parse_uri (uri, LDAP_PORT, &error);
651     if (!addr) {
652       g_task_return_new_error (task, SEAHORSE_ERROR, -1,
653                                _("Invalid URI: %s"), uri);
654       return;
655     }
656 
657     seahorse_progress_prep_and_begin (cancellable, task, NULL);
658 
659     /* Now get a GSocketAddressEnumerator to do the resolving */
660     seahorse_progress_update (cancellable, task,
661                               _("Resolving server address: %s"), uri);
662 
663     addr_enumer = g_socket_connectable_enumerate (addr);
664     g_socket_address_enumerator_next_async (addr_enumer,
665                                             cancellable,
666                                             on_address_resolved,
667                                             g_steal_pointer (&task));
668 }
669 
670 static LDAP *
seahorse_ldap_source_connect_finish(SeahorseLDAPSource * source,GAsyncResult * result,GError ** error)671 seahorse_ldap_source_connect_finish (SeahorseLDAPSource *source,
672                                      GAsyncResult *result,
673                                      GError **error)
674 {
675     g_return_val_if_fail (g_task_is_valid (result, source), NULL);
676     g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
677                           seahorse_ldap_source_connect_async, NULL);
678 
679     return g_task_propagate_pointer (G_TASK (result), error);
680 }
681 
682 G_DEFINE_TYPE (SeahorseLDAPSource, seahorse_ldap_source, SEAHORSE_TYPE_SERVER_SOURCE);
683 
684 static void
seahorse_ldap_source_init(SeahorseLDAPSource * self)685 seahorse_ldap_source_init (SeahorseLDAPSource *self)
686 {
687 }
688 
689 typedef struct {
690     char *filter;
691     LDAP *ldap;
692     GcrSimpleCollection *results;
693 } SearchClosure;
694 
695 static void
search_closure_free(gpointer data)696 search_closure_free (gpointer data)
697 {
698     SearchClosure *closure = data;
699     g_clear_object (&closure->results);
700     g_free (closure->filter);
701     if (closure->ldap)
702         ldap_unbind_ext (closure->ldap, NULL, NULL);
703     g_free (closure);
704 }
705 
706 static const char *PGP_ATTRIBUTES[] = {
707     "pgpcertid",
708     "pgpuserid",
709     "pgprevoked",
710     "pgpdisabled",
711     "pgpkeycreatetime",
712     "pgpkeyexpiretime"
713     "pgpkeysize",
714     "pgpkeytype",
715     NULL
716 };
717 
718 /* Add a key to the key source from an LDAP entry */
719 static void
search_parse_key_from_ldap_entry(SeahorseLDAPSource * self,GcrSimpleCollection * results,LDAP * ldap,LDAPMessage * res)720 search_parse_key_from_ldap_entry (SeahorseLDAPSource *self,
721                                   GcrSimpleCollection *results,
722                                   LDAP *ldap,
723                                   LDAPMessage *res)
724 {
725     const char *algo;
726     long int timestamp;
727     long int expires;
728     g_autofree char *fpr = NULL;
729     g_autofree char *uidstr = NULL;
730     gboolean revoked;
731     gboolean disabled;
732     int length;
733 
734     g_return_if_fail (ldap_msgtype (res) == LDAP_RES_SEARCH_ENTRY);
735 
736     fpr = get_string_attribute (ldap, res, "pgpcertid");
737     uidstr = get_string_attribute (ldap, res, "pgpuserid");
738     revoked = get_boolean_attribute (ldap, res, "pgprevoked");
739     disabled = get_boolean_attribute (ldap, res, "pgpdisabled");
740     timestamp = get_date_attribute (ldap, res, "pgpkeycreatetime");
741     expires = get_date_attribute (ldap, res, "pgpkeyexpiretime");
742     algo = get_algo_attribute (ldap, res, "pgpkeytype");
743     length = get_int_attribute (ldap, res, "pgpkeysize");
744 
745     if (fpr && uidstr) {
746         g_autoptr (SeahorsePgpSubkey) subkey = NULL;
747         g_autoptr(SeahorsePgpKey) key = NULL;
748         g_autofree char *fingerprint = NULL;
749         g_autoptr(SeahorsePgpUid) uid = NULL;
750         guint flags;
751 
752         /* Build up a subkey */
753         subkey = seahorse_pgp_subkey_new ();
754         seahorse_pgp_subkey_set_keyid (subkey, fpr);
755         fingerprint = seahorse_pgp_subkey_calc_fingerprint (fpr);
756         seahorse_pgp_subkey_set_fingerprint (subkey, fingerprint);
757         seahorse_pgp_subkey_set_algorithm (subkey, algo);
758         seahorse_pgp_subkey_set_length (subkey, length);
759 
760         if (timestamp > 0) {
761             g_autoptr(GDateTime) created_date = NULL;
762             created_date = g_date_time_new_from_unix_utc (timestamp);
763             seahorse_pgp_subkey_set_created (subkey, created_date);
764         }
765         if (expires > 0) {
766             g_autoptr(GDateTime) expires_date = NULL;
767             expires_date = g_date_time_new_from_unix_utc (expires);
768             seahorse_pgp_subkey_set_expires (subkey, expires_date);
769         }
770 
771         flags = SEAHORSE_FLAG_EXPORTABLE;
772         if (revoked)
773             flags |= SEAHORSE_FLAG_REVOKED;
774         if (disabled)
775             flags |= SEAHORSE_FLAG_DISABLED;
776         seahorse_pgp_subkey_set_flags (subkey, flags);
777 
778         key = seahorse_pgp_key_new ();
779 
780         /* Build up a uid */
781         uid = seahorse_pgp_uid_new (key, uidstr);
782         if (revoked)
783             seahorse_pgp_uid_set_validity (uid, SEAHORSE_VALIDITY_REVOKED);
784         seahorse_pgp_key_add_uid (key, uid);
785 
786         /* Now build them into a key */
787         seahorse_pgp_key_add_subkey (key, subkey);
788         g_object_set (key,
789                       "object-flags", flags,
790                       "place", self,
791                       NULL);
792 
793         seahorse_pgp_key_realize (key);
794         gcr_simple_collection_add (results, G_OBJECT (key));
795     }
796 }
797 
798 static gboolean
on_search_search_completed(LDAPMessage * result,gpointer user_data)799 on_search_search_completed (LDAPMessage *result,
800                             gpointer user_data)
801 {
802     GTask *task = G_TASK (user_data);
803     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (g_task_get_source_object (task));
804     SearchClosure *closure = g_task_get_task_data (task);
805     GCancellable *cancellable = g_task_get_cancellable (task);
806     g_autoptr(GError) error = NULL;
807     int type;
808     int rc;
809     int code;
810     char *message;
811 
812     type = ldap_msgtype (result);
813     g_return_val_if_fail (type == LDAP_RES_SEARCH_ENTRY || type == LDAP_RES_SEARCH_RESULT, FALSE);
814 
815     /* An LDAP entry */
816     if (type == LDAP_RES_SEARCH_ENTRY) {
817         g_debug ("Retrieved Key Entry");
818 #ifdef WITH_DEBUG
819         dump_ldap_entry (closure->ldap, result);
820 #endif
821 
822         search_parse_key_from_ldap_entry (self, closure->results,
823                                           closure->ldap, result);
824         return G_SOURCE_CONTINUE;
825     }
826 
827     /* All entries done */
828     rc = ldap_parse_result (closure->ldap, result, &code, NULL,
829                             &message, NULL, NULL, 0);
830     g_return_val_if_fail (rc == LDAP_SUCCESS, FALSE);
831 
832     /* Error codes that we ignore */
833     switch (code) {
834     case LDAP_SIZELIMIT_EXCEEDED:
835         code = LDAP_SUCCESS;
836         break;
837     };
838 
839     if (code != LDAP_SUCCESS)
840         g_task_return_new_error (task, LDAP_ERROR_DOMAIN, code, "%s", message);
841     else if (seahorse_ldap_source_propagate_error (self, code, &error))
842         g_task_return_error (task, g_steal_pointer (&error));
843     else
844         g_task_return_boolean (task, TRUE);
845 
846     ldap_memfree (message);
847     seahorse_progress_end (cancellable, task);
848 
849     return G_SOURCE_REMOVE;
850 }
851 
852 static void
on_search_connect_completed(GObject * source,GAsyncResult * result,gpointer user_data)853 on_search_connect_completed (GObject *source,
854                              GAsyncResult *result,
855                              gpointer user_data)
856 {
857     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (source);
858     g_autoptr(GTask) task = G_TASK (user_data);
859     SearchClosure *closure = g_task_get_task_data (task);
860     GCancellable *cancellable = g_task_get_cancellable (task);
861     g_autoptr(GError) error = NULL;
862     LDAPServerInfo *sinfo;
863     int ldap_op;
864     int rc;
865     g_autoptr(GSource) gsource = NULL;
866 
867     closure->ldap = seahorse_ldap_source_connect_finish (self, result, &error);
868     if (error != NULL) {
869         g_task_return_error (task, g_steal_pointer (&error));
870         return;
871     }
872 
873     sinfo = get_ldap_server_info (self, TRUE);
874 
875     g_debug ("Searching Server ... base: %s, filter: %s",
876              sinfo->base_dn, closure->filter);
877 
878     rc = ldap_search_ext (closure->ldap, sinfo->base_dn, LDAP_SCOPE_SUBTREE,
879                           closure->filter, (char **)PGP_ATTRIBUTES, 0,
880                           NULL, NULL, NULL, 0, &ldap_op);
881 
882     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
883         g_task_return_error (task, g_steal_pointer (&error));
884         return;
885     }
886 
887     gsource = seahorse_ldap_gsource_new (closure->ldap, ldap_op, cancellable);
888     g_source_set_callback (gsource, G_SOURCE_FUNC (on_search_search_completed),
889                            g_steal_pointer (&task), g_object_unref);
890     g_source_attach (gsource, g_main_context_default ());
891 }
892 
893 
894 static void
seahorse_ldap_source_search_async(SeahorseServerSource * source,const char * match,GcrSimpleCollection * results,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)895 seahorse_ldap_source_search_async (SeahorseServerSource *source,
896                                    const char *match,
897                                    GcrSimpleCollection *results,
898                                    GCancellable *cancellable,
899                                    GAsyncReadyCallback callback,
900                                    gpointer user_data)
901 {
902     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (source);
903     SearchClosure *closure;
904     g_autoptr(GTask) task = NULL;
905     g_autofree char *text = NULL;
906 
907     task = g_task_new (source, cancellable, callback, user_data);
908     g_task_set_source_tag (task, seahorse_ldap_source_search_async);
909     closure = g_new0 (SearchClosure, 1);
910     closure->results = g_object_ref (results);
911     text = escape_ldap_value (match);
912     closure->filter = g_strdup_printf ("(pgpuserid=*%s*)", text);
913     g_task_set_task_data (task, closure, search_closure_free);
914 
915     seahorse_progress_prep_and_begin (cancellable, task, NULL);
916 
917     seahorse_ldap_source_connect_async (self, cancellable,
918                                         on_search_connect_completed,
919                                         g_steal_pointer (&task));
920 }
921 
922 static gboolean
seahorse_ldap_source_search_finish(SeahorseServerSource * source,GAsyncResult * result,GError ** error)923 seahorse_ldap_source_search_finish (SeahorseServerSource *source,
924                                     GAsyncResult *result,
925                                     GError **error)
926 {
927     g_return_val_if_fail (SEAHORSE_IS_LDAP_SOURCE (source), FALSE);
928     g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
929 
930     return g_task_propagate_boolean (G_TASK (result), error);
931 }
932 
933 typedef struct {
934     GPtrArray *keydatas;
935     int current_index;
936     LDAP *ldap;
937 } ImportClosure;
938 
939 static void
import_closure_free(gpointer data)940 import_closure_free (gpointer data)
941 {
942     ImportClosure *closure = data;
943     g_ptr_array_free (closure->keydatas, TRUE);
944     if (closure->ldap)
945         ldap_unbind_ext (closure->ldap, NULL, NULL);
946     g_free (closure);
947 }
948 
949 static void import_send_key (SeahorseLDAPSource *self, GTask *task);
950 
951 /* Called when results come in for a key send */
952 static gboolean
on_import_add_completed(LDAPMessage * result,gpointer user_data)953 on_import_add_completed (LDAPMessage *result,
954                          gpointer user_data)
955 {
956     GTask *task = G_TASK (user_data);
957     ImportClosure *closure = g_task_get_task_data (task);
958     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (g_task_get_source_object (task));
959     GError *error = NULL;
960     char *message;
961     int code;
962     int rc;
963 
964     g_return_val_if_fail (ldap_msgtype (result) == LDAP_RES_ADD, FALSE);
965 
966     rc = ldap_parse_result (closure->ldap, result, &code, NULL,
967                             &message, NULL, NULL, 0);
968     g_return_val_if_fail (rc == LDAP_SUCCESS, FALSE);
969 
970     /* TODO: Somehow communicate this to the user */
971     if (code == LDAP_ALREADY_EXISTS)
972         code = LDAP_SUCCESS;
973 
974     ldap_memfree (message);
975 
976     if (seahorse_ldap_source_propagate_error (self, code, &error)) {
977         g_task_return_error (task, g_steal_pointer (&error));
978         return G_SOURCE_REMOVE;
979     }
980 
981     import_send_key (self, task);
982     return G_SOURCE_REMOVE;
983 }
984 
985 static void
import_send_key(SeahorseLDAPSource * self,GTask * task)986 import_send_key (SeahorseLDAPSource *self, GTask *task)
987 {
988     ImportClosure *closure = g_task_get_task_data (task);
989     GCancellable *cancellable = g_task_get_cancellable (task);
990     LDAPServerInfo *sinfo;
991     g_autofree char *base = NULL;
992     LDAPMod mod;
993     LDAPMod *attrs[2];
994     char *values[2];
995     g_autoptr(GSource) gsource = NULL;
996     GError *error = NULL;
997     char *keydata;
998     int ldap_op;
999     int rc;
1000 
1001     if (closure->current_index >= 0) {
1002         keydata = g_ptr_array_index (closure->keydatas, closure->current_index);
1003         seahorse_progress_end (cancellable, keydata);
1004     }
1005 
1006     closure->current_index++;
1007 
1008     /* All done, complete operation */
1009     if (closure->current_index == (int) closure->keydatas->len) {
1010         g_task_return_boolean (task, TRUE);
1011         return;
1012     }
1013 
1014     keydata = g_ptr_array_index (closure->keydatas, closure->current_index);
1015     seahorse_progress_begin (cancellable, keydata);
1016     values[0] = keydata;
1017     values[1] = NULL;
1018 
1019     sinfo = get_ldap_server_info (self, TRUE);
1020     memset (&mod, 0, sizeof (mod));
1021     mod.mod_op = LDAP_MOD_ADD;
1022     mod.mod_type = sinfo->key_attr;
1023     mod.mod_values = values;
1024 
1025     attrs[0] = &mod;
1026     attrs[1] = NULL;
1027 
1028     base = g_strdup_printf ("pgpCertid=virtual,%s", sinfo->base_dn);
1029     rc = ldap_add_ext (closure->ldap, base, attrs, NULL, NULL, &ldap_op);
1030 
1031     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
1032         g_task_return_error (task, g_steal_pointer (&error));
1033         return;
1034     }
1035 
1036     gsource = seahorse_ldap_gsource_new (closure->ldap, ldap_op, cancellable);
1037     g_source_set_callback (gsource, G_SOURCE_FUNC (on_import_add_completed),
1038                            g_object_ref (task), g_object_unref);
1039     g_source_attach (gsource, g_main_context_default ());
1040 }
1041 
1042 static void
on_import_connect_completed(GObject * source,GAsyncResult * result,gpointer user_data)1043 on_import_connect_completed (GObject *source,
1044                              GAsyncResult *result,
1045                              gpointer user_data)
1046 {
1047     g_autoptr(GTask) task = G_TASK (user_data);
1048     ImportClosure *closure = g_task_get_task_data (task);
1049     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (source);
1050     g_autoptr(GError) error = NULL;
1051 
1052     closure->ldap = seahorse_ldap_source_connect_finish (self, result, &error);
1053     if (error != NULL) {
1054         g_task_return_error (task, g_steal_pointer (&error));
1055         return;
1056     }
1057 
1058     import_send_key (self, task);
1059 }
1060 
1061 static void
seahorse_ldap_source_import_async(SeahorseServerSource * source,GInputStream * input,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1062 seahorse_ldap_source_import_async (SeahorseServerSource *source,
1063                                    GInputStream *input,
1064                                    GCancellable *cancellable,
1065                                    GAsyncReadyCallback callback,
1066                                    gpointer user_data)
1067 {
1068     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (source);
1069     g_autoptr(GTask) task = NULL;
1070     ImportClosure *closure;
1071 
1072     task = g_task_new (source, cancellable, callback, user_data);
1073     g_task_set_source_tag (task, seahorse_ldap_source_import_async);
1074 
1075     closure = g_new0 (ImportClosure, 1);
1076     closure->current_index = -1;
1077     g_task_set_task_data (task, closure, import_closure_free);
1078 
1079     closure->keydatas = g_ptr_array_new_with_free_func (g_free);
1080     for (;;) {
1081         g_autoptr(GString) buf = g_string_sized_new (2048);
1082         guint len;
1083         g_autofree char *keydata = NULL;
1084 
1085         len = seahorse_util_read_data_block (buf, input, "-----BEGIN PGP PUBLIC KEY BLOCK-----",
1086                                              "-----END PGP PUBLIC KEY BLOCK-----");
1087         if (len <= 0)
1088             break;
1089 
1090         keydata = g_string_free (g_steal_pointer (&buf), FALSE);
1091         seahorse_progress_prep (cancellable, keydata, NULL);
1092         g_ptr_array_add (closure->keydatas, g_steal_pointer (&keydata));
1093     }
1094 
1095     seahorse_ldap_source_connect_async (self, cancellable,
1096                                         on_import_connect_completed,
1097                                         g_steal_pointer (&task));
1098 }
1099 
1100 static GList *
seahorse_ldap_source_import_finish(SeahorseServerSource * source,GAsyncResult * result,GError ** error)1101 seahorse_ldap_source_import_finish (SeahorseServerSource *source,
1102                                     GAsyncResult *result,
1103                                     GError **error)
1104 {
1105     g_return_val_if_fail (g_task_is_valid (result, source), NULL);
1106     g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
1107                           seahorse_ldap_source_import_async, NULL);
1108 
1109     if (!g_task_propagate_boolean (G_TASK (result), error))
1110         return NULL;
1111 
1112     /* We don't know the keys that were imported, since this is a server */
1113     return NULL;
1114 }
1115 
1116 typedef struct {
1117     GPtrArray *fingerprints;
1118     int current_index;
1119     GString *data;
1120     LDAP *ldap;
1121 } ExportClosure;
1122 
1123 static void
export_closure_free(gpointer data)1124 export_closure_free (gpointer data)
1125 {
1126     ExportClosure *closure = data;
1127     g_ptr_array_free (closure->fingerprints, TRUE);
1128     if (closure->data)
1129         g_string_free (closure->data, TRUE);
1130     if (closure->ldap)
1131         ldap_unbind_ext (closure->ldap, NULL, NULL);
1132     g_free (closure);
1133 }
1134 
1135 static void     export_retrieve_key     (SeahorseLDAPSource *self,
1136                                          GTask *task);
1137 
1138 static gboolean
on_export_search_completed(LDAPMessage * result,gpointer user_data)1139 on_export_search_completed (LDAPMessage *result,
1140                             gpointer user_data)
1141 {
1142     GTask *task = G_TASK (user_data);
1143     ExportClosure *closure = g_task_get_task_data (task);
1144     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (g_task_get_source_object (task));
1145     LDAPServerInfo *sinfo;
1146     char *message;
1147     GError *error = NULL;
1148     int code;
1149     int type;
1150     int rc;
1151 
1152     type = ldap_msgtype (result);
1153     g_return_val_if_fail (type == LDAP_RES_SEARCH_ENTRY || type == LDAP_RES_SEARCH_RESULT, FALSE);
1154     sinfo = get_ldap_server_info (self, TRUE);
1155 
1156     /* An LDAP Entry */
1157     if (type == LDAP_RES_SEARCH_ENTRY) {
1158         g_autofree char *key = NULL;
1159 
1160         g_debug ("Server Info Result");
1161 #ifdef WITH_DEBUG
1162         dump_ldap_entry (closure->ldap, result);
1163 #endif
1164 
1165         key = get_string_attribute (closure->ldap, result, sinfo->key_attr);
1166         if (key == NULL) {
1167             g_warning ("key server missing pgp key data");
1168             seahorse_ldap_source_propagate_error (self, LDAP_NO_SUCH_OBJECT, &error);
1169             g_task_return_error (task, g_steal_pointer (&error));
1170             return G_SOURCE_REMOVE;
1171         }
1172 
1173         g_string_append (closure->data, key);
1174         g_string_append_c (closure->data, '\n');
1175 
1176         return G_SOURCE_CONTINUE;
1177     }
1178 
1179     /* No more entries, result */
1180     rc = ldap_parse_result (closure->ldap, result, &code, NULL,
1181                             &message, NULL, NULL, 0);
1182     g_return_val_if_fail (rc == LDAP_SUCCESS, FALSE);
1183 
1184     if (seahorse_ldap_source_propagate_error (self, code, &error)) {
1185         g_task_return_error (task, g_steal_pointer (&error));
1186         return G_SOURCE_REMOVE;
1187     }
1188 
1189     ldap_memfree (message);
1190 
1191     /* Process more keys if possible */
1192     export_retrieve_key (self, task);
1193     return G_SOURCE_REMOVE;
1194 }
1195 
1196 static void
export_retrieve_key(SeahorseLDAPSource * self,GTask * task)1197 export_retrieve_key (SeahorseLDAPSource *self,
1198                      GTask *task)
1199 {
1200     ExportClosure *closure = g_task_get_task_data (task);
1201     GCancellable *cancellable = g_task_get_cancellable (task);
1202     LDAPServerInfo *sinfo;
1203     g_autofree char *filter = NULL;
1204     char *attrs[2];
1205     g_autoptr(GSource) gsource = NULL;
1206     const char *fingerprint;
1207     g_autoptr(GError) error = NULL;
1208     int length, rc;
1209     int ldap_op;
1210 
1211     if (closure->current_index > 0) {
1212         fingerprint = g_ptr_array_index (closure->fingerprints,
1213                                          closure->current_index);
1214         seahorse_progress_end (cancellable, fingerprint);
1215     }
1216 
1217     closure->current_index++;
1218 
1219     /* All done, complete operation */
1220     if (closure->current_index == (int) closure->fingerprints->len) {
1221         g_task_return_boolean (task, TRUE);
1222         return;
1223     }
1224 
1225     fingerprint = g_ptr_array_index (closure->fingerprints,
1226                                      closure->current_index);
1227     seahorse_progress_begin (cancellable, fingerprint);
1228     length = strlen (fingerprint);
1229     if (length > 16)
1230         fingerprint += (length - 16);
1231 
1232     filter = g_strdup_printf ("(pgpcertid=%.16s)", fingerprint);
1233     sinfo = get_ldap_server_info (self, TRUE);
1234 
1235     attrs[0] = sinfo->key_attr;
1236     attrs[1] = NULL;
1237 
1238     rc = ldap_search_ext (closure->ldap, sinfo->base_dn, LDAP_SCOPE_SUBTREE,
1239                           filter, attrs, 0,
1240                           NULL, NULL, NULL, 0, &ldap_op);
1241 
1242     if (seahorse_ldap_source_propagate_error (self, rc, &error)) {
1243         g_task_return_error (task, g_steal_pointer (&error));
1244         return;
1245     }
1246 
1247     gsource = seahorse_ldap_gsource_new (closure->ldap, ldap_op, cancellable);
1248     g_source_set_callback (gsource, (GSourceFunc)on_export_search_completed,
1249                            g_object_ref (task), g_object_unref);
1250     g_source_attach (gsource, g_main_context_default ());
1251 }
1252 
1253 static void
on_export_connect_completed(GObject * source,GAsyncResult * result,gpointer user_data)1254 on_export_connect_completed (GObject *source,
1255                              GAsyncResult *result,
1256                              gpointer user_data)
1257 {
1258     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (source);
1259     g_autoptr(GTask) task = G_TASK (user_data);
1260     ExportClosure *closure = g_task_get_task_data (task);
1261     g_autoptr(GError) error = NULL;
1262 
1263     closure->ldap = seahorse_ldap_source_connect_finish (self, result, &error);
1264     if (error != NULL) {
1265         g_task_return_error (task, g_steal_pointer (&error));
1266         return;
1267     }
1268 
1269     export_retrieve_key (self, task);
1270 }
1271 
1272 static void
seahorse_ldap_source_export_async(SeahorseServerSource * source,const char ** keyids,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1273 seahorse_ldap_source_export_async (SeahorseServerSource *source,
1274                                    const char **keyids,
1275                                    GCancellable *cancellable,
1276                                    GAsyncReadyCallback callback,
1277                                    gpointer user_data)
1278 {
1279     SeahorseLDAPSource *self = SEAHORSE_LDAP_SOURCE (source);
1280     ExportClosure *closure;
1281     g_autoptr(GTask) task = NULL;
1282 
1283     task = g_task_new (self, cancellable, callback, user_data);
1284     g_task_set_source_tag (task, seahorse_ldap_source_export_async);
1285 
1286     closure = g_new0 (ExportClosure, 1);
1287     closure->data = g_string_sized_new (1024);
1288     closure->fingerprints = g_ptr_array_new_with_free_func (g_free);
1289     for (int i = 0; keyids[i] != NULL; i++) {
1290         char *fingerprint = g_strdup (keyids[i]);
1291 
1292         g_ptr_array_add (closure->fingerprints, fingerprint);
1293         seahorse_progress_prep (cancellable, fingerprint, NULL);
1294     }
1295     closure->current_index = -1;
1296     g_task_set_task_data (task, closure, export_closure_free);
1297 
1298     seahorse_ldap_source_connect_async (self, cancellable,
1299                                         on_export_connect_completed,
1300                                         g_steal_pointer (&task));
1301 }
1302 
1303 static gpointer
seahorse_ldap_source_export_finish(SeahorseServerSource * source,GAsyncResult * result,gsize * size,GError ** error)1304 seahorse_ldap_source_export_finish (SeahorseServerSource *source,
1305                                     GAsyncResult *result,
1306                                     gsize *size,
1307                                     GError **error)
1308 {
1309     ExportClosure *closure;
1310     gpointer output;
1311 
1312     g_return_val_if_fail (size != NULL, NULL);
1313     g_return_val_if_fail (g_task_is_valid (result, source), NULL);
1314 
1315     if (!g_task_propagate_boolean (G_TASK (result), error))
1316         return NULL;
1317 
1318     closure = g_task_get_task_data (G_TASK (result));
1319     *size = closure->data->len;
1320     output = g_string_free (closure->data, FALSE);
1321     closure->data = NULL;
1322     return output;
1323 }
1324 
1325 /* Initialize the basic class stuff */
1326 static void
seahorse_ldap_source_class_init(SeahorseLDAPSourceClass * klass)1327 seahorse_ldap_source_class_init (SeahorseLDAPSourceClass *klass)
1328 {
1329     SeahorseServerSourceClass *server_class = SEAHORSE_SERVER_SOURCE_CLASS (klass);
1330 
1331     server_class->search_async = seahorse_ldap_source_search_async;
1332     server_class->search_finish = seahorse_ldap_source_search_finish;
1333     server_class->export_async = seahorse_ldap_source_export_async;
1334     server_class->export_finish = seahorse_ldap_source_export_finish;
1335     server_class->import_async = seahorse_ldap_source_import_async;
1336     server_class->import_finish = seahorse_ldap_source_import_finish;
1337 }
1338 
1339 /**
1340  * seahorse_ldap_source_new
1341  * @uri: The server to connect to
1342  *
1343  * Creates a new key source for an LDAP PGP server.
1344  *
1345  * Returns: A new LDAP Key Source
1346  */
1347 SeahorseLDAPSource *
seahorse_ldap_source_new(const char * uri)1348 seahorse_ldap_source_new (const char *uri)
1349 {
1350     g_return_val_if_fail (seahorse_ldap_is_valid_uri (uri), NULL);
1351 
1352     return g_object_new (SEAHORSE_TYPE_LDAP_SOURCE, "uri", uri, NULL);
1353 }
1354 
1355 /**
1356  * seahorse_ldap_is_valid_uri
1357  * @uri: The uri to check
1358  *
1359  * Returns: Whether the passed uri is valid for an ldap key source
1360  */
1361 gboolean
seahorse_ldap_is_valid_uri(const char * uri)1362 seahorse_ldap_is_valid_uri (const char *uri)
1363 {
1364     LDAPURLDesc *url;
1365     int r;
1366 
1367     g_return_val_if_fail (uri && *uri, FALSE);
1368 
1369     r = ldap_url_parse (uri, &url);
1370     if (r == LDAP_URL_SUCCESS) {
1371         /* Some checks to make sure it's a simple URI */
1372         if (!(url->lud_host && url->lud_host[0]) ||
1373             (url->lud_dn && url->lud_dn[0]) ||
1374             (url->lud_attrs || url->lud_attrs))
1375             r = LDAP_URL_ERR_PARAM;
1376 
1377         ldap_free_urldesc (url);
1378     }
1379 
1380     return r == LDAP_URL_SUCCESS;
1381 }
1382 
1383 #endif /* WITH_LDAP */
1384