~ubuntu-branches/ubuntu/maverick/evolution-data-server/maverick-proposed

« back to all changes in this revision

Viewing changes to servers/exchange/lib/e2k-global-catalog.c

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-05-17 17:02:06 UTC
  • mfrom: (1.1.79 upstream) (1.6.12 experimental)
  • Revision ID: james.westby@ubuntu.com-20100517170206-4ufr52vwrhh26yh0
Tags: 2.30.1-1ubuntu1
* Merge from debian experimental. Remaining change:
  (LP: #42199, #229669, #173703, #360344, #508494)
  + debian/control:
    - add Vcs-Bzr tag
    - don't use libgnome
    - Use Breaks instead of Conflicts against evolution 2.25 and earlier.
  + debian/evolution-data-server.install,
    debian/patches/45_libcamel_providers_version.patch:
    - use the upstream versioning, not a Debian-specific one 
  + debian/libedata-book1.2-dev.install, debian/libebackend-1.2-dev.install,
    debian/libcamel1.2-dev.install, debian/libedataserverui1.2-dev.install:
    - install html documentation
  + debian/rules:
    - don't build documentation it's shipped with the tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
 
 
3
 
/* Copyright (C) 2001-2004 Novell, Inc.
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or
6
 
 * modify it under the terms of version 2 of the GNU Lesser General Public
7
 
 * License as published by the Free Software Foundation.
8
 
 *
9
 
 * This program is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
 * General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Lesser General Public
15
 
 * License along with this program; if not, write to the
16
 
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 
 * Boston, MA 02110-1301, USA.
18
 
 */
19
 
 
20
 
#ifdef HAVE_CONFIG_H
21
 
#include "config.h"
22
 
#endif
23
 
 
24
 
#include "e2k-global-catalog-ldap.h"
25
 
#include "e2k-sid.h"
26
 
#include "e2k-utils.h"
27
 
 
28
 
#include <pthread.h>
29
 
#include <stdlib.h>
30
 
#include <string.h>
31
 
#include <sys/time.h>
32
 
 
33
 
#ifdef HAVE_LDAP_NTLM_BIND
34
 
#include "xntlm.h"
35
 
#endif
36
 
 
37
 
#ifdef E2K_DEBUG
38
 
static gboolean e2k_gc_debug = FALSE;
39
 
#define E2K_GC_DEBUG_MSG(x) if (e2k_gc_debug) printf x
40
 
#else
41
 
#define E2K_GC_DEBUG_MSG(x)
42
 
#endif
43
 
 
44
 
struct _E2kGlobalCatalogPrivate {
45
 
        GMutex *ldap_lock;
46
 
        LDAP *ldap;
47
 
 
48
 
        GPtrArray *entries;
49
 
        GHashTable *entry_cache, *server_cache;
50
 
 
51
 
        gchar *server, *user, *nt_domain, *password;
52
 
        E2kAutoconfigGalAuthPref auth;
53
 
};
54
 
 
55
 
#define PARENT_TYPE G_TYPE_OBJECT
56
 
static GObjectClass *parent_class = NULL;
57
 
 
58
 
static void finalize (GObject *);
59
 
static gint get_gc_connection (E2kGlobalCatalog *gc, E2kOperation *op);
60
 
 
61
 
static void
62
 
class_init (GObjectClass *object_class)
63
 
{
64
 
#ifdef E2K_DEBUG
65
 
        gchar *e2k_debug = getenv ("E2K_DEBUG");
66
 
 
67
 
        if (e2k_debug && atoi (e2k_debug) > 3)
68
 
                e2k_gc_debug = TRUE;
69
 
#endif
70
 
 
71
 
        /* For some reason, sasl_client_init (called by ldap_init
72
 
         * below) takes a *really* long time to scan the sasl modules
73
 
         * when running under gdb. We're not using sasl anyway, so...
74
 
         */
75
 
        g_setenv ("SASL_PATH", "", TRUE);
76
 
 
77
 
        parent_class = g_type_class_ref (PARENT_TYPE);
78
 
 
79
 
        /* virtual method override */
80
 
        object_class->finalize = finalize;
81
 
}
82
 
 
83
 
static void
84
 
init (GObject *object)
85
 
{
86
 
        E2kGlobalCatalog *gc = E2K_GLOBAL_CATALOG (object);
87
 
 
88
 
        gc->priv = g_new0 (E2kGlobalCatalogPrivate, 1);
89
 
        gc->priv->ldap_lock = g_mutex_new ();
90
 
        gc->priv->entries = g_ptr_array_new ();
91
 
        gc->priv->entry_cache = g_hash_table_new (e2k_ascii_strcase_hash,
92
 
                                                  e2k_ascii_strcase_equal);
93
 
        gc->priv->server_cache = g_hash_table_new (g_str_hash, g_str_equal);
94
 
}
95
 
 
96
 
static void
97
 
free_entry (E2kGlobalCatalogEntry *entry)
98
 
{
99
 
        gint i;
100
 
 
101
 
        g_free (entry->dn);
102
 
        g_free (entry->display_name);
103
 
 
104
 
        if (entry->sid)
105
 
                g_object_unref (entry->sid);
106
 
 
107
 
        g_free (entry->email);
108
 
        g_free (entry->mailbox);
109
 
 
110
 
        if (entry->delegates) {
111
 
                for (i = 0; i < entry->delegates->len; i++)
112
 
                        g_free (entry->delegates->pdata[i]);
113
 
                g_ptr_array_free (entry->delegates, TRUE);
114
 
        }
115
 
        if (entry->delegators) {
116
 
                for (i = 0; i < entry->delegators->len; i++)
117
 
                        g_free (entry->delegators->pdata[i]);
118
 
                g_ptr_array_free (entry->delegators, TRUE);
119
 
        }
120
 
 
121
 
        g_free (entry);
122
 
}
123
 
 
124
 
static void
125
 
free_server (gpointer key, gpointer value, gpointer data)
126
 
{
127
 
        g_free (key);
128
 
        g_free (value);
129
 
}
130
 
 
131
 
static void
132
 
finalize (GObject *object)
133
 
{
134
 
        E2kGlobalCatalog *gc = E2K_GLOBAL_CATALOG (object);
135
 
        gint i;
136
 
 
137
 
        if (gc->priv) {
138
 
                if (gc->priv->ldap)
139
 
                        ldap_unbind (gc->priv->ldap);
140
 
 
141
 
                for (i = 0; i < gc->priv->entries->len; i++)
142
 
                        free_entry (gc->priv->entries->pdata[i]);
143
 
                g_ptr_array_free (gc->priv->entries, TRUE);
144
 
 
145
 
                g_hash_table_foreach (gc->priv->server_cache, free_server, NULL);
146
 
                g_hash_table_destroy (gc->priv->server_cache);
147
 
 
148
 
                g_free (gc->priv->server);
149
 
                g_free (gc->priv->user);
150
 
                g_free (gc->priv->nt_domain);
151
 
                if (gc->priv->password) {
152
 
                        memset (gc->priv->password, 0, strlen (gc->priv->password));
153
 
                        g_free (gc->priv->password);
154
 
                }
155
 
 
156
 
                g_mutex_free (gc->priv->ldap_lock);
157
 
 
158
 
                g_free (gc->priv);
159
 
                gc->priv = NULL;
160
 
        }
161
 
 
162
 
        g_free (gc->domain);
163
 
        gc->domain = NULL;
164
 
 
165
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
166
 
}
167
 
 
168
 
E2K_MAKE_TYPE (e2k_global_catalog, E2kGlobalCatalog, class_init, init, PARENT_TYPE)
169
 
 
170
 
static gint
171
 
gc_ldap_result (LDAP *ldap, E2kOperation *op,
172
 
                gint msgid, LDAPMessage **msg)
173
 
{
174
 
        struct timeval tv;
175
 
        gint status, ldap_error;
176
 
 
177
 
        tv.tv_sec = 1;
178
 
        tv.tv_usec = 0;
179
 
        *msg = NULL;
180
 
        do {
181
 
                status = ldap_result (ldap, msgid, TRUE, &tv, msg);
182
 
                if (status == -1) {
183
 
                        ldap_get_option (ldap, LDAP_OPT_ERROR_NUMBER,
184
 
                                         &ldap_error);
185
 
                        return ldap_error;
186
 
                }
187
 
        } while (status == 0 && !e2k_operation_is_cancelled (op));
188
 
 
189
 
        if (e2k_operation_is_cancelled (op)) {
190
 
                ldap_abandon (ldap, msgid);
191
 
                return LDAP_USER_CANCELLED;
192
 
        } else
193
 
                return LDAP_SUCCESS;
194
 
}
195
 
 
196
 
static gint
197
 
gc_search (E2kGlobalCatalog *gc, E2kOperation *op,
198
 
           const gchar *base, gint scope, const gchar *filter,
199
 
           const gchar **attrs, LDAPMessage **msg)
200
 
{
201
 
        gint ldap_error, msgid, try;
202
 
 
203
 
        for (try = 0; try < 2; try++) {
204
 
                ldap_error = get_gc_connection (gc, op);
205
 
                if (ldap_error != LDAP_SUCCESS)
206
 
                        return ldap_error;
207
 
                ldap_error = ldap_search_ext (gc->priv->ldap, base, scope,
208
 
                                              filter, (gchar **)attrs,
209
 
                                              FALSE, NULL, NULL, NULL, 0,
210
 
                                              &msgid);
211
 
                if (ldap_error == LDAP_SERVER_DOWN)
212
 
                        continue;
213
 
                else if (ldap_error != LDAP_SUCCESS)
214
 
                        return ldap_error;
215
 
 
216
 
                ldap_error = gc_ldap_result (gc->priv->ldap, op, msgid, msg);
217
 
                if (ldap_error == LDAP_SERVER_DOWN)
218
 
                        continue;
219
 
                else if (ldap_error != LDAP_SUCCESS)
220
 
                        return ldap_error;
221
 
 
222
 
                return LDAP_SUCCESS;
223
 
        }
224
 
 
225
 
        return LDAP_SERVER_DOWN;
226
 
}
227
 
 
228
 
#ifdef HAVE_LDAP_NTLM_BIND
229
 
static gint
230
 
ntlm_bind (E2kGlobalCatalog *gc, E2kOperation *op, LDAP *ldap)
231
 
{
232
 
        LDAPMessage *msg;
233
 
        gint ldap_error, msgid, err;
234
 
        gchar *nonce, *default_domain;
235
 
        GByteArray *ba;
236
 
        struct berval ldap_buf;
237
 
 
238
 
        /* Create and send NTLM request */
239
 
        ba = xntlm_negotiate ();
240
 
        ldap_buf.bv_len = ba->len;
241
 
        ldap_buf.bv_val = (gchar *) ba->data;
242
 
        ldap_error = ldap_ntlm_bind (ldap, "NTLM", LDAP_AUTH_NTLM_REQUEST,
243
 
                                     &ldap_buf, NULL, NULL, &msgid);
244
 
        g_byte_array_free (ba, TRUE);
245
 
        if (ldap_error != LDAP_SUCCESS) {
246
 
                E2K_GC_DEBUG_MSG(("GC: Failure sending first NTLM bind message: 0x%02x\n", ldap_error));
247
 
                return ldap_error;
248
 
        }
249
 
 
250
 
        /* Extract challenge */
251
 
        ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
252
 
        if (ldap_error != LDAP_SUCCESS) {
253
 
                E2K_GC_DEBUG_MSG(("GC: Could not parse first NTLM bind response\n"));
254
 
                return ldap_error;
255
 
        }
256
 
        ldap_error = ldap_parse_ntlm_bind_result (ldap, msg, &ldap_buf);
257
 
        ldap_msgfree (msg);
258
 
        if (ldap_error != LDAP_SUCCESS) {
259
 
                E2K_GC_DEBUG_MSG(("GC: Could not parse NTLM bind response: 0x%02x\n", ldap_error));
260
 
                return ldap_error;
261
 
        }
262
 
 
263
 
        if (!xntlm_parse_challenge (ldap_buf.bv_val, ldap_buf.bv_len,
264
 
                                    &nonce, &default_domain,
265
 
                                    &gc->domain)) {
266
 
                E2K_GC_DEBUG_MSG(("GC: Could not find nonce in NTLM bind response\n"));
267
 
                ber_memfree (ldap_buf.bv_val);
268
 
 
269
 
                return LDAP_DECODING_ERROR;
270
 
        }
271
 
        ber_memfree (ldap_buf.bv_val);
272
 
 
273
 
        /* Create and send response */
274
 
        ba = xntlm_authenticate (nonce, gc->priv->nt_domain ? gc->priv->nt_domain : default_domain,
275
 
                                 gc->priv->user, gc->priv->password, NULL);
276
 
        ldap_buf.bv_len = ba->len;
277
 
        ldap_buf.bv_val = (gchar *) ba->data;
278
 
        ldap_error = ldap_ntlm_bind (ldap, "NTLM", LDAP_AUTH_NTLM_RESPONSE,
279
 
                                     &ldap_buf, NULL, NULL, &msgid);
280
 
        g_byte_array_free (ba, TRUE);
281
 
        g_free (nonce);
282
 
        g_free (default_domain);
283
 
        if (ldap_error != LDAP_SUCCESS) {
284
 
                E2K_GC_DEBUG_MSG(("GC: Failure sending second NTLM bind message: 0x%02x\n", ldap_error));
285
 
                return ldap_error;
286
 
        }
287
 
 
288
 
        /* And get the final result */
289
 
        ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
290
 
        if (ldap_error != LDAP_SUCCESS) {
291
 
                E2K_GC_DEBUG_MSG(("GC: Could not parse second NTLM bind response\n"));
292
 
                return ldap_error;
293
 
        }
294
 
        ldap_error = ldap_parse_result (ldap, msg, &err, NULL, NULL,
295
 
                                        NULL, NULL, TRUE);
296
 
        if (ldap_error != LDAP_SUCCESS) {
297
 
                E2K_GC_DEBUG_MSG(("GC: Could not parse second NTLM bind response: 0x%02x\n", ldap_error));
298
 
                return ldap_error;
299
 
        }
300
 
 
301
 
        return err;
302
 
}
303
 
#endif
304
 
 
305
 
static gint
306
 
connect_ldap (E2kGlobalCatalog *gc, E2kOperation *op, LDAP *ldap)
307
 
{
308
 
        gint ldap_error;
309
 
        gchar *nt_name;
310
 
#ifdef G_OS_WIN32
311
 
        SEC_WINNT_AUTH_IDENTITY_W auth;
312
 
#endif
313
 
 
314
 
        /* authenticate */
315
 
#ifdef HAVE_LDAP_NTLM_BIND
316
 
        if ((gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_DEFAULT || gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_NTLM)) {
317
 
                ldap_error = ntlm_bind (gc, op, ldap);
318
 
                if (ldap_error == LDAP_SUCCESS) {
319
 
                        E2K_GC_DEBUG_MSG(("GC: connected via NTLM\n\n"));
320
 
                        return LDAP_SUCCESS;
321
 
                } else if (gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_NTLM) {
322
 
                        E2K_GC_DEBUG_MSG(("GC: user setup NTLM, but it failed 0x%02x\n", ldap_error));
323
 
                        return ldap_error;
324
 
                }
325
 
        }
326
 
#endif
327
 
 
328
 
        if (gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_NTLM) {
329
 
                E2K_GC_DEBUG_MSG(("GC: user setup NTLM, but we do not have it\n"));
330
 
                /* a little hack */
331
 
                return LDAP_AUTH_METHOD_NOT_SUPPORTED;
332
 
        }
333
 
 
334
 
        nt_name = gc->priv->nt_domain ?
335
 
                g_strdup_printf ("%s\\%s", gc->priv->nt_domain, gc->priv->user) :
336
 
                g_strdup (gc->priv->user);
337
 
#ifndef G_OS_WIN32
338
 
        ldap_error = ldap_simple_bind_s (ldap, nt_name, gc->priv->password);
339
 
#else
340
 
        auth.User = g_utf8_to_utf16 (gc->priv->user, -1, NULL, NULL, NULL);
341
 
        auth.UserLength = wcslen (auth.User);
342
 
        auth.Domain = gc->priv->nt_domain ?
343
 
                g_utf8_to_utf16 (gc->priv->nt_domain, -1, NULL, NULL, NULL) :
344
 
                g_utf8_to_utf16 ("", -1, NULL, NULL, NULL);
345
 
        auth.DomainLength = wcslen (auth.Domain);
346
 
        auth.Password = g_utf8_to_utf16 (gc->priv->password, -1, NULL, NULL, NULL);
347
 
        auth.PasswordLength = wcslen (auth.Password);
348
 
        auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
349
 
        ldap_error = ldap_bind_s (ldap, nt_name, &auth, gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_BASIC ? LDAP_AUTH_SIMPLE : LDAP_AUTH_NTLM);
350
 
        g_free (auth.Password);
351
 
        g_free (auth.Domain);
352
 
        g_free (auth.User);
353
 
#endif
354
 
        g_free (nt_name);
355
 
 
356
 
        if (ldap_error != LDAP_SUCCESS)
357
 
                g_warning ("LDAP authentication failed (0x%02x (%s))", ldap_error, ldap_err2string (ldap_error) ? ldap_err2string (ldap_error) : "Unknown error");
358
 
        else
359
 
                E2K_GC_DEBUG_MSG(("GC: connected\n\n"));
360
 
 
361
 
        return ldap_error;
362
 
}
363
 
 
364
 
static gint
365
 
get_ldap_connection (E2kGlobalCatalog *gc, E2kOperation *op,
366
 
                     const gchar *server, gint port,
367
 
                     LDAP **ldap)
368
 
{
369
 
        gint ldap_opt, ldap_error;
370
 
 
371
 
        E2K_GC_DEBUG_MSG(("\nGC: Connecting to ldap://%s:%d/\n", server, port));
372
 
 
373
 
        *ldap = ldap_init (server, port);
374
 
        if (!*ldap) {
375
 
                E2K_GC_DEBUG_MSG(("GC: failed\n\n"));
376
 
                g_warning ("Could not connect to ldap://%s:%d/",
377
 
                           server, port);
378
 
                return LDAP_SERVER_DOWN;
379
 
        }
380
 
 
381
 
        /* Set options */
382
 
        ldap_opt = LDAP_DEREF_ALWAYS;
383
 
        ldap_set_option (*ldap, LDAP_OPT_DEREF, &ldap_opt);
384
 
        ldap_opt = gc->response_limit;
385
 
        ldap_set_option (*ldap, LDAP_OPT_SIZELIMIT, &ldap_opt);
386
 
        ldap_opt = LDAP_VERSION3;
387
 
        ldap_set_option (*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_opt);
388
 
 
389
 
        ldap_error = connect_ldap (gc, op, *ldap);
390
 
        if (ldap_error != LDAP_SUCCESS) {
391
 
                ldap_unbind (*ldap);
392
 
                *ldap = NULL;
393
 
        }
394
 
        return ldap_error;
395
 
}
396
 
 
397
 
static gint
398
 
get_gc_connection (E2kGlobalCatalog *gc, E2kOperation *op)
399
 
{
400
 
        gint err;
401
 
 
402
 
        if (gc->priv->ldap) {
403
 
                ldap_get_option (gc->priv->ldap, LDAP_OPT_ERROR_NUMBER, &err);
404
 
                if (err != LDAP_SERVER_DOWN)
405
 
                        return LDAP_SUCCESS;
406
 
 
407
 
                return connect_ldap (gc, op, gc->priv->ldap);
408
 
        } else {
409
 
                return get_ldap_connection (gc, op,
410
 
                                            gc->priv->server, 3268,
411
 
                                            &gc->priv->ldap);
412
 
        }
413
 
}
414
 
 
415
 
/**
416
 
 * e2k_global_catalog_get_ldap:
417
 
 * @gc: the global catalog
418
 
 * @op: pointer to an initialized #E2kOperation to use for cancellation
419
 
 * @ldap_error: set the value returned from get_ldap_connection if not NULL
420
 
 *
421
 
 * Returns a new LDAP handle. The caller must ldap_unbind() it when it
422
 
 * is done.
423
 
 *
424
 
 * Return value: an LDAP handle, or %NULL if it can't connect
425
 
 **/
426
 
LDAP *
427
 
e2k_global_catalog_get_ldap (E2kGlobalCatalog *gc, E2kOperation *op, gint *ldap_error)
428
 
{
429
 
        LDAP *ldap;
430
 
        gint err;
431
 
 
432
 
        g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), NULL);
433
 
 
434
 
        err = get_ldap_connection (gc, op, gc->priv->server, 3268, &ldap);
435
 
 
436
 
        if (ldap_error)
437
 
                *ldap_error = err;
438
 
 
439
 
        return ldap;
440
 
}
441
 
 
442
 
/**
443
 
 * e2k_global_catalog_new:
444
 
 * @server: the GC server name
445
 
 * @response_limit: the maximum number of responses to return from a search
446
 
 * @user: username to authenticate with
447
 
 * @domain: NT domain of @user, or %NULL to autodetect.
448
 
 * @password: password to authenticate with
449
 
 *
450
 
 * Create an object for communicating with the Windows Global Catalog
451
 
 * via LDAP.
452
 
 *
453
 
 * Return value: the new E2kGlobalCatalog. (This call will always succeed.
454
 
 * If the passed-in data is bad, it will fail on a later call.)
455
 
 **/
456
 
E2kGlobalCatalog *
457
 
e2k_global_catalog_new (const gchar *server, gint response_limit,
458
 
                        const gchar *user, const gchar *domain,
459
 
                        const gchar *password, E2kAutoconfigGalAuthPref use_auth)
460
 
{
461
 
        E2kGlobalCatalog *gc;
462
 
 
463
 
        gc = g_object_new (E2K_TYPE_GLOBAL_CATALOG, NULL);
464
 
        gc->priv->server = g_strdup (server);
465
 
        gc->priv->auth = use_auth;
466
 
        gc->priv->user = g_strdup (user);
467
 
        gc->priv->nt_domain = g_strdup (domain);
468
 
        gc->priv->password = g_strdup (password);
469
 
        gc->response_limit = response_limit;
470
 
 
471
 
        return gc;
472
 
}
473
 
 
474
 
static const gchar *
475
 
lookup_mta (E2kGlobalCatalog *gc, E2kOperation *op, const gchar *mta_dn)
476
 
{
477
 
        gchar *hostname, **values;
478
 
        const gchar *attrs[2];
479
 
        LDAPMessage *resp;
480
 
        gint ldap_error, i;
481
 
 
482
 
        /* Skip over "CN=Microsoft MTA," */
483
 
        mta_dn = strchr (mta_dn, ',');
484
 
        if (!mta_dn)
485
 
                return NULL;
486
 
        mta_dn++;
487
 
 
488
 
        hostname = g_hash_table_lookup (gc->priv->server_cache, mta_dn);
489
 
        if (hostname)
490
 
                return hostname;
491
 
 
492
 
        E2K_GC_DEBUG_MSG(("GC:   Finding hostname for %s\n", mta_dn));
493
 
 
494
 
        attrs[0] = "networkAddress";
495
 
        attrs[1] = NULL;
496
 
 
497
 
        ldap_error = gc_search (gc, op, mta_dn, LDAP_SCOPE_BASE,
498
 
                                NULL, attrs, &resp);
499
 
        if (ldap_error != LDAP_SUCCESS) {
500
 
                E2K_GC_DEBUG_MSG(("GC:   lookup failed (0x%02x)\n", ldap_error));
501
 
                return NULL;
502
 
        }
503
 
 
504
 
        values = ldap_get_values (gc->priv->ldap, resp, "networkAddress");
505
 
        ldap_msgfree (resp);
506
 
        if (!values) {
507
 
                E2K_GC_DEBUG_MSG(("GC:   entry has no networkAddress\n"));
508
 
                return NULL;
509
 
        }
510
 
 
511
 
        hostname = NULL;
512
 
        for (i = 0; values[i]; i++) {
513
 
                if (strstr (values[i], "_tcp")) {
514
 
                        hostname = strchr (values[i], ':');
515
 
                        break;
516
 
                }
517
 
        }
518
 
        if (!hostname) {
519
 
                E2K_GC_DEBUG_MSG(("GC:   host is not availble by TCP?\n"));
520
 
                ldap_value_free (values);
521
 
                return NULL;
522
 
        }
523
 
 
524
 
        hostname = g_strdup (hostname + 1);
525
 
        g_hash_table_insert (gc->priv->server_cache, g_strdup (mta_dn), hostname);
526
 
        ldap_value_free (values);
527
 
 
528
 
        E2K_GC_DEBUG_MSG(("GC:   %s\n", hostname));
529
 
        return hostname;
530
 
}
531
 
 
532
 
static void
533
 
get_sid_values (E2kGlobalCatalog *gc, E2kOperation *op,
534
 
                LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
535
 
{
536
 
        gchar **values;
537
 
        struct berval **bsid_values;
538
 
        E2kSidType type;
539
 
 
540
 
        values = ldap_get_values (gc->priv->ldap, msg, "displayName");
541
 
        if (values) {
542
 
                E2K_GC_DEBUG_MSG(("GC: displayName %s\n", values[0]));
543
 
                entry->display_name = g_strdup (values[0]);
544
 
                ldap_value_free (values);
545
 
        }
546
 
 
547
 
        bsid_values = ldap_get_values_len (gc->priv->ldap, msg, "objectSid");
548
 
        if (!bsid_values)
549
 
                return;
550
 
        if (bsid_values[0]->bv_len < 2 ||
551
 
            bsid_values[0]->bv_len != E2K_SID_BINARY_SID_LEN (bsid_values[0]->bv_val)) {
552
 
                E2K_GC_DEBUG_MSG(("GC: invalid SID\n"));
553
 
                return;
554
 
        }
555
 
 
556
 
        values = ldap_get_values (gc->priv->ldap, msg, "objectCategory");
557
 
        if (values && values[0] && !g_ascii_strncasecmp (values[0], "CN=Group", 8))
558
 
                type = E2K_SID_TYPE_GROUP;
559
 
        else if (values && values[0] && !g_ascii_strncasecmp (values[0], "CN=Foreign", 10))
560
 
                type = E2K_SID_TYPE_WELL_KNOWN_GROUP;
561
 
        else /* FIXME? */
562
 
                type = E2K_SID_TYPE_USER;
563
 
        if (values)
564
 
                ldap_value_free (values);
565
 
 
566
 
        entry->sid = e2k_sid_new_from_binary_sid (
567
 
                type, (guint8 *) bsid_values[0]->bv_val, entry->display_name);
568
 
        entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_SID;
569
 
 
570
 
        ldap_value_free_len (bsid_values);
571
 
}
572
 
 
573
 
static void
574
 
get_mail_values (E2kGlobalCatalog *gc, E2kOperation *op,
575
 
                 LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
576
 
{
577
 
        gchar **values, **mtavalues;
578
 
 
579
 
        values = ldap_get_values (gc->priv->ldap, msg, "mail");
580
 
        if (values) {
581
 
                E2K_GC_DEBUG_MSG(("GC: mail %s\n", values[0]));
582
 
                entry->email = g_strdup (values[0]);
583
 
                g_hash_table_insert (gc->priv->entry_cache,
584
 
                                     entry->email, entry);
585
 
                entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_EMAIL;
586
 
                ldap_value_free (values);
587
 
        }
588
 
 
589
 
        values = ldap_get_values (gc->priv->ldap, msg, "mailNickname");
590
 
        mtavalues = ldap_get_values (gc->priv->ldap, msg, "homeMTA");
591
 
        if (values && mtavalues) {
592
 
                E2K_GC_DEBUG_MSG(("GC: mailNickname %s\n", values[0]));
593
 
                E2K_GC_DEBUG_MSG(("GC: homeMTA %s\n", mtavalues[0]));
594
 
                entry->exchange_server = (gchar *)lookup_mta (gc, op, mtavalues[0]);
595
 
                ldap_value_free (mtavalues);
596
 
                if (entry->exchange_server)
597
 
                        entry->mailbox = g_strdup (values[0]);
598
 
                ldap_value_free (values);
599
 
                entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX;
600
 
        }
601
 
 
602
 
        values = ldap_get_values (gc->priv->ldap, msg, "legacyExchangeDN");
603
 
        if (values) {
604
 
                E2K_GC_DEBUG_MSG(("GC: legacyExchangeDN %s\n", values[0]));
605
 
                entry->legacy_exchange_dn = g_strdup (values[0]);
606
 
                g_hash_table_insert (gc->priv->entry_cache,
607
 
                                     entry->legacy_exchange_dn,
608
 
                                     entry);
609
 
                entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN;
610
 
                ldap_value_free (values);
611
 
        }
612
 
}
613
 
 
614
 
static void
615
 
get_delegation_values (E2kGlobalCatalog *gc, E2kOperation *op,
616
 
                       LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
617
 
{
618
 
        gchar **values;
619
 
        gint i;
620
 
 
621
 
        values = ldap_get_values (gc->priv->ldap, msg, "publicDelegates");
622
 
        if (values) {
623
 
                E2K_GC_DEBUG_MSG(("GC: publicDelegates\n"));
624
 
                entry->delegates = g_ptr_array_new ();
625
 
                for (i = 0; values[i]; i++) {
626
 
                        E2K_GC_DEBUG_MSG(("GC:   %s\n", values[i]));
627
 
                        g_ptr_array_add (entry->delegates,
628
 
                                         g_strdup (values[i]));
629
 
                }
630
 
                entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_DELEGATES;
631
 
                ldap_value_free (values);
632
 
        }
633
 
        values = ldap_get_values (gc->priv->ldap, msg, "publicDelegatesBL");
634
 
        if (values) {
635
 
                E2K_GC_DEBUG_MSG(("GC: publicDelegatesBL\n"));
636
 
                entry->delegators = g_ptr_array_new ();
637
 
                for (i = 0; values[i]; i++) {
638
 
                        E2K_GC_DEBUG_MSG(("GC:   %s\n", values[i]));
639
 
                        g_ptr_array_add (entry->delegators,
640
 
                                         g_strdup (values[i]));
641
 
                }
642
 
                entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_DELEGATORS;
643
 
                ldap_value_free (values);
644
 
        }
645
 
}
646
 
 
647
 
static void
648
 
get_quota_values (E2kGlobalCatalog *gc, E2kOperation *op,
649
 
                  LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
650
 
{
651
 
        gchar **quota_setting_values, **quota_limit_values;
652
 
 
653
 
        /* Check if mailbox store default values are used */
654
 
        quota_setting_values = ldap_get_values (gc->priv->ldap, msg, "mDBUseDefaults");
655
 
        if (!quota_setting_values) {
656
 
                entry->quota_warn = entry->quota_nosend = entry->quota_norecv = 0;
657
 
                return;
658
 
        }
659
 
 
660
 
        entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_QUOTA;
661
 
        E2K_GC_DEBUG_MSG(("GC: mDBUseDefaults %s\n", quota_setting_values[0]));
662
 
 
663
 
        if (!strcmp (quota_setting_values[0], "TRUE")) {
664
 
                /* use global mailbox store settings */
665
 
                E2K_GC_DEBUG_MSG(("GC: Using global mailbox store limits\n"));
666
 
        }
667
 
        ldap_value_free (quota_setting_values);
668
 
 
669
 
        quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBStorageQuota");
670
 
        if (quota_limit_values) {
671
 
                entry->quota_warn = atoi(quota_limit_values[0]);
672
 
                E2K_GC_DEBUG_MSG(("GC: mDBStorageQuota %s\n", quota_limit_values[0]));
673
 
                ldap_value_free (quota_limit_values);
674
 
        }
675
 
 
676
 
        quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBOverQuotaLimit");
677
 
        if (quota_limit_values) {
678
 
                entry->quota_nosend = atoi(quota_limit_values[0]);
679
 
                E2K_GC_DEBUG_MSG(("GC: mDBOverQuotaLimit %s\n", quota_limit_values[0]));
680
 
                ldap_value_free (quota_limit_values);
681
 
        }
682
 
 
683
 
        quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBOverHardQuotaLimit");
684
 
        if (quota_limit_values) {
685
 
                entry->quota_norecv = atoi(quota_limit_values[0]);
686
 
                E2K_GC_DEBUG_MSG(("GC: mDBHardQuotaLimit %s\n", quota_limit_values[0]));
687
 
                ldap_value_free (quota_limit_values);
688
 
        }
689
 
}
690
 
 
691
 
static void
692
 
get_account_control_values (E2kGlobalCatalog *gc, E2kOperation *op,
693
 
                            LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
694
 
{
695
 
        gchar **values;
696
 
 
697
 
        values = ldap_get_values (gc->priv->ldap, msg, "userAccountControl");
698
 
        if (values) {
699
 
                entry->user_account_control = atoi(values[0]);
700
 
                E2K_GC_DEBUG_MSG(("GC: userAccountControl %s\n", values[0]));
701
 
                entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL;
702
 
                ldap_value_free (values);
703
 
        }
704
 
 
705
 
}
706
 
 
707
 
/**
708
 
 * e2k_global_catalog_lookup:
709
 
 * @gc: the global catalog
710
 
 * @op: pointer to an #E2kOperation to use for cancellation
711
 
 * @type: the type of information in @key
712
 
 * @key: email address or DN to look up
713
 
 * @flags: the information to look up
714
 
 * @entry_p: pointer to a variable to return the entry in.
715
 
 *
716
 
 * Look up the indicated user in the global catalog and
717
 
 * return their information in *@entry_p.
718
 
 *
719
 
 * Return value: the status of the lookup
720
 
 **/
721
 
E2kGlobalCatalogStatus
722
 
e2k_global_catalog_lookup (E2kGlobalCatalog *gc,
723
 
                           E2kOperation *op,
724
 
                           E2kGlobalCatalogLookupType type,
725
 
                           const gchar *key,
726
 
                           E2kGlobalCatalogLookupFlags flags,
727
 
                           E2kGlobalCatalogEntry **entry_p)
728
 
{
729
 
        E2kGlobalCatalogEntry *entry;
730
 
        GPtrArray *attrs;
731
 
        E2kGlobalCatalogLookupFlags lookup_flags, need_flags = 0;
732
 
        const gchar *base = NULL;
733
 
        gchar *filter = NULL, *dn;
734
 
        gint scope = LDAP_SCOPE_BASE, ldap_error;
735
 
        E2kGlobalCatalogStatus status;
736
 
        LDAPMessage *msg, *resp;
737
 
 
738
 
        g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), E2K_GLOBAL_CATALOG_ERROR);
739
 
        g_return_val_if_fail (key != NULL, E2K_GLOBAL_CATALOG_ERROR);
740
 
 
741
 
        g_mutex_lock (gc->priv->ldap_lock);
742
 
 
743
 
        entry = g_hash_table_lookup (gc->priv->entry_cache, key);
744
 
        if (!entry)
745
 
                entry = g_new0 (E2kGlobalCatalogEntry, 1);
746
 
 
747
 
        attrs = g_ptr_array_new ();
748
 
 
749
 
        if (!entry->display_name)
750
 
                g_ptr_array_add (attrs, (guint8 *) "displayName");
751
 
        if (!entry->email) {
752
 
                g_ptr_array_add (attrs, (guint8 *) "mail");
753
 
                if (flags & E2K_GLOBAL_CATALOG_LOOKUP_EMAIL)
754
 
                        need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_EMAIL;
755
 
        }
756
 
        if (!entry->legacy_exchange_dn) {
757
 
                g_ptr_array_add (attrs, (guint8 *) "legacyExchangeDN");
758
 
                if (flags & E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN)
759
 
                        need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN;
760
 
        }
761
 
 
762
 
        lookup_flags = flags & ~entry->mask;
763
 
 
764
 
        if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_SID) {
765
 
                g_ptr_array_add (attrs, (guint8 *) "objectSid");
766
 
                g_ptr_array_add (attrs, (guint8 *) "objectCategory");
767
 
                need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_SID;
768
 
        }
769
 
        if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX) {
770
 
                g_ptr_array_add (attrs, (guint8 *) "mailNickname");
771
 
                g_ptr_array_add (attrs, (guint8 *) "homeMTA");
772
 
                need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX;
773
 
        }
774
 
        if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_DELEGATES)
775
 
                g_ptr_array_add (attrs, (guint8 *) "publicDelegates");
776
 
        if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_DELEGATORS)
777
 
                g_ptr_array_add (attrs, (guint8 *) "publicDelegatesBL");
778
 
        if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_QUOTA) {
779
 
                g_ptr_array_add (attrs, (guint8 *) "mDBUseDefaults");
780
 
                g_ptr_array_add (attrs, (guint8 *) "mDBStorageQuota");
781
 
                g_ptr_array_add (attrs, (guint8 *) "mDBOverQuotaLimit");
782
 
                g_ptr_array_add (attrs, (guint8 *) "mDBOverHardQuotaLimit");
783
 
        }
784
 
        if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL)
785
 
                g_ptr_array_add (attrs, (guint8 *) "userAccountControl");
786
 
 
787
 
        if (attrs->len == 0) {
788
 
                E2K_GC_DEBUG_MSG(("\nGC: returning cached info for %s\n", key));
789
 
                goto lookedup;
790
 
        }
791
 
 
792
 
        E2K_GC_DEBUG_MSG(("\nGC: looking up info for %s\n", key));
793
 
        g_ptr_array_add (attrs, NULL);
794
 
 
795
 
        switch (type) {
796
 
        case E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL:
797
 
                filter = g_strdup_printf ("(mail=%s)", key);
798
 
                base = LDAP_ROOT_DSE;
799
 
                scope = LDAP_SCOPE_SUBTREE;
800
 
                break;
801
 
 
802
 
        case E2K_GLOBAL_CATALOG_LOOKUP_BY_DN:
803
 
                filter = NULL;
804
 
                base = key;
805
 
                scope = LDAP_SCOPE_BASE;
806
 
                break;
807
 
 
808
 
        case E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN:
809
 
                filter = g_strdup_printf ("(legacyExchangeDN=%s)", key);
810
 
                base = LDAP_ROOT_DSE;
811
 
                scope = LDAP_SCOPE_SUBTREE;
812
 
                break;
813
 
        }
814
 
 
815
 
        ldap_error = gc_search (gc, op, base, scope, filter,
816
 
                                (const gchar **)attrs->pdata, &msg);
817
 
        if (ldap_error == LDAP_USER_CANCELLED) {
818
 
                E2K_GC_DEBUG_MSG(("GC: ldap_search cancelled"));
819
 
                status = E2K_GLOBAL_CATALOG_CANCELLED;
820
 
                goto done;
821
 
        } else if (ldap_error == LDAP_INVALID_CREDENTIALS) {
822
 
                E2K_GC_DEBUG_MSG(("GC: ldap_search auth failed"));
823
 
                status = E2K_GLOBAL_CATALOG_AUTH_FAILED;
824
 
                goto done;
825
 
        } else if (ldap_error != LDAP_SUCCESS) {
826
 
                E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n\n", ldap_error));
827
 
                status = E2K_GLOBAL_CATALOG_ERROR;
828
 
                goto done;
829
 
        }
830
 
 
831
 
        resp = ldap_first_entry (gc->priv->ldap, msg);
832
 
        if (!resp) {
833
 
                E2K_GC_DEBUG_MSG(("GC: no such user\n\n"));
834
 
                status = E2K_GLOBAL_CATALOG_NO_SUCH_USER;
835
 
                ldap_msgfree (msg);
836
 
                goto done;
837
 
        }
838
 
 
839
 
        if (!entry->dn) {
840
 
                dn = ldap_get_dn (gc->priv->ldap, resp);
841
 
                entry->dn = g_strdup (dn);
842
 
                E2K_GC_DEBUG_MSG(("GC: dn = %s\n\n", dn));
843
 
                ldap_memfree (dn);
844
 
                g_ptr_array_add (gc->priv->entries, entry);
845
 
                g_hash_table_insert (gc->priv->entry_cache,
846
 
                                     entry->dn, entry);
847
 
        }
848
 
 
849
 
        get_sid_values (gc, op, resp, entry);
850
 
        get_mail_values (gc, op, resp, entry);
851
 
        get_delegation_values (gc, op, resp, entry);
852
 
        get_quota_values (gc, op, resp, entry);
853
 
        get_account_control_values (gc, op, resp, entry);
854
 
        ldap_msgfree (msg);
855
 
 
856
 
 lookedup:
857
 
        if (need_flags & ~entry->mask) {
858
 
                E2K_GC_DEBUG_MSG(("GC: no data\n\n"));
859
 
                status = E2K_GLOBAL_CATALOG_NO_DATA;
860
 
        } else {
861
 
                E2K_GC_DEBUG_MSG(("\n"));
862
 
                status = E2K_GLOBAL_CATALOG_OK;
863
 
                entry->mask |= lookup_flags;
864
 
                *entry_p = entry;
865
 
        }
866
 
 
867
 
 done:
868
 
        g_free (filter);
869
 
        g_ptr_array_free (attrs, TRUE);
870
 
 
871
 
        if (status != E2K_GLOBAL_CATALOG_OK && !entry->dn)
872
 
                g_free (entry);
873
 
 
874
 
        g_mutex_unlock (gc->priv->ldap_lock);
875
 
        return status;
876
 
}
877
 
 
878
 
struct async_lookup_data {
879
 
        E2kGlobalCatalog *gc;
880
 
        E2kOperation *op;
881
 
        E2kGlobalCatalogLookupType type;
882
 
        gchar *key;
883
 
        E2kGlobalCatalogLookupFlags flags;
884
 
        E2kGlobalCatalogCallback callback;
885
 
        gpointer user_data;
886
 
 
887
 
        E2kGlobalCatalogEntry *entry;
888
 
        E2kGlobalCatalogStatus status;
889
 
};
890
 
 
891
 
static gboolean
892
 
idle_lookup_result (gpointer user_data)
893
 
{
894
 
        struct async_lookup_data *ald = user_data;
895
 
 
896
 
        ald->callback (ald->gc, ald->status, ald->entry, ald->user_data);
897
 
        g_object_unref (ald->gc);
898
 
        g_free (ald->key);
899
 
        g_free (ald);
900
 
        return FALSE;
901
 
}
902
 
 
903
 
static gpointer
904
 
do_lookup_thread (gpointer user_data)
905
 
{
906
 
        struct async_lookup_data *ald = user_data;
907
 
 
908
 
        ald->status = e2k_global_catalog_lookup (ald->gc, ald->op, ald->type,
909
 
                                                 ald->key, ald->flags,
910
 
                                                 &ald->entry);
911
 
        g_idle_add (idle_lookup_result, ald);
912
 
        return NULL;
913
 
}
914
 
 
915
 
/**
916
 
 * e2k_global_catalog_async_lookup:
917
 
 * @gc: the global catalog
918
 
 * @op: pointer to an #E2kOperation to use for cancellation
919
 
 * @type: the type of information in @key
920
 
 * @key: email address or DN to look up
921
 
 * @flags: the information to look up
922
 
 * @callback: the callback to invoke after finding the user
923
 
 * @user_data: data to pass to callback
924
 
 *
925
 
 * Asynchronously look up the indicated user in the global catalog and
926
 
 * return the requested information to the callback.
927
 
 **/
928
 
void
929
 
e2k_global_catalog_async_lookup (E2kGlobalCatalog *gc,
930
 
                                 E2kOperation *op,
931
 
                                 E2kGlobalCatalogLookupType type,
932
 
                                 const gchar *key,
933
 
                                 E2kGlobalCatalogLookupFlags flags,
934
 
                                 E2kGlobalCatalogCallback callback,
935
 
                                 gpointer user_data)
936
 
{
937
 
        struct async_lookup_data *ald;
938
 
        pthread_t pth;
939
 
 
940
 
        ald = g_new0 (struct async_lookup_data, 1);
941
 
        ald->gc = g_object_ref (gc);
942
 
        ald->op = op;
943
 
        ald->type = type;
944
 
        ald->key = g_strdup (key);
945
 
        ald->flags = flags;
946
 
        ald->callback = callback;
947
 
        ald->user_data = user_data;
948
 
 
949
 
        if (pthread_create (&pth, NULL, do_lookup_thread, ald) == -1) {
950
 
                g_warning ("Could not create lookup thread\n");
951
 
                ald->status = E2K_GLOBAL_CATALOG_ERROR;
952
 
                g_idle_add (idle_lookup_result, ald);
953
 
        }
954
 
}
955
 
 
956
 
static const gchar *
957
 
lookup_controlling_ad_server (E2kGlobalCatalog *gc, E2kOperation *op,
958
 
                              const gchar *dn)
959
 
{
960
 
        gchar *hostname, **values, *ad_dn;
961
 
        const gchar *attrs[2];
962
 
        LDAPMessage *resp;
963
 
        gint ldap_error;
964
 
 
965
 
        while (g_ascii_strncasecmp (dn, "DC=", 3) != 0) {
966
 
                dn = strchr (dn, ',');
967
 
                if (!dn)
968
 
                        return NULL;
969
 
                dn++;
970
 
        }
971
 
 
972
 
        hostname = g_hash_table_lookup (gc->priv->server_cache, dn);
973
 
        if (hostname)
974
 
                return hostname;
975
 
 
976
 
        E2K_GC_DEBUG_MSG(("GC:   Finding AD server for %s\n", dn));
977
 
 
978
 
        attrs[0] = "masteredBy";
979
 
        attrs[1] = NULL;
980
 
 
981
 
        ldap_error = gc_search (gc, op, dn, LDAP_SCOPE_BASE, NULL, attrs, &resp);
982
 
        if (ldap_error != LDAP_SUCCESS) {
983
 
                E2K_GC_DEBUG_MSG(("GC:   ldap_search failed: 0x%02x\n", ldap_error));
984
 
                return NULL;
985
 
        }
986
 
 
987
 
        values = ldap_get_values (gc->priv->ldap, resp, "masteredBy");
988
 
        ldap_msgfree (resp);
989
 
        if (!values) {
990
 
                E2K_GC_DEBUG_MSG(("GC:   no known AD server\n\n"));
991
 
                return NULL;
992
 
        }
993
 
 
994
 
        /* Skip over "CN=NTDS Settings," */
995
 
        ad_dn = strchr (values[0], ',');
996
 
        if (!ad_dn) {
997
 
                E2K_GC_DEBUG_MSG(("GC:   bad dn %s\n\n", values[0]));
998
 
                ldap_value_free (values);
999
 
                return NULL;
1000
 
        }
1001
 
        ad_dn++;
1002
 
 
1003
 
        attrs[0] = "dNSHostName";
1004
 
        attrs[1] = NULL;
1005
 
 
1006
 
        ldap_error = gc_search (gc, op, ad_dn, LDAP_SCOPE_BASE, NULL, attrs, &resp);
1007
 
        ldap_value_free (values);
1008
 
 
1009
 
        if (ldap_error != LDAP_SUCCESS) {
1010
 
                E2K_GC_DEBUG_MSG(("GC:   ldap_search failed: 0x%02x\n\n", ldap_error));
1011
 
                return NULL;
1012
 
        }
1013
 
 
1014
 
        values = ldap_get_values (gc->priv->ldap, resp, "dNSHostName");
1015
 
        ldap_msgfree (resp);
1016
 
        if (!values) {
1017
 
                E2K_GC_DEBUG_MSG(("GC:   entry has no dNSHostName\n\n"));
1018
 
                return NULL;
1019
 
        }
1020
 
 
1021
 
        hostname = g_strdup (values[0]);
1022
 
        ldap_value_free (values);
1023
 
 
1024
 
        g_hash_table_insert (gc->priv->server_cache, g_strdup (dn), hostname);
1025
 
 
1026
 
        E2K_GC_DEBUG_MSG(("GC:   %s\n", hostname));
1027
 
        return hostname;
1028
 
}
1029
 
 
1030
 
static gchar *
1031
 
find_domain_dn (gchar *domain)
1032
 
{
1033
 
        GString *dn_value = g_string_new (NULL);
1034
 
        gchar *dn;
1035
 
        gchar  *sub_domain=NULL;
1036
 
 
1037
 
        sub_domain = strtok (domain, ".");
1038
 
        while (sub_domain != NULL) {
1039
 
                g_string_append (dn_value, "DC=");
1040
 
                g_string_append (dn_value, sub_domain);
1041
 
                g_string_append (dn_value, ",");
1042
 
                sub_domain = strtok (NULL, ".");
1043
 
        }
1044
 
        if (dn_value->str[0])
1045
 
                dn = g_strndup (dn_value->str, strlen(dn_value->str) - 1);
1046
 
        else
1047
 
                dn = NULL;
1048
 
        g_string_free (dn_value, TRUE);
1049
 
        return dn;
1050
 
}
1051
 
 
1052
 
gdouble
1053
 
lookup_passwd_max_age (E2kGlobalCatalog *gc, E2kOperation *op)
1054
 
{
1055
 
        gchar **values = NULL, *filter = NULL, *val=NULL;
1056
 
        const gchar *attrs[2];
1057
 
        LDAP *ldap;
1058
 
        LDAPMessage *msg=NULL;
1059
 
        gint ldap_error, msgid;
1060
 
        gdouble maxAge=0;
1061
 
        gchar *dn=NULL;
1062
 
 
1063
 
        attrs[0] = "maxPwdAge";
1064
 
        attrs[1] = NULL;
1065
 
 
1066
 
        filter = g_strdup("objectClass=domainDNS");
1067
 
 
1068
 
        dn = find_domain_dn (gc->domain);
1069
 
 
1070
 
        ldap_error = get_ldap_connection (gc, op, gc->priv->server, LDAP_PORT, &ldap);
1071
 
        if (ldap_error != LDAP_SUCCESS) {
1072
 
                E2K_GC_DEBUG_MSG(("GC: Establishing ldap connection failed : 0x%02x\n\n",
1073
 
                                                                        ldap_error));
1074
 
                return -1;
1075
 
        }
1076
 
 
1077
 
        ldap_error = ldap_search_ext (ldap, dn, LDAP_SCOPE_BASE, filter, (gchar **)attrs,
1078
 
                                      FALSE, NULL, NULL, NULL, 0, &msgid);
1079
 
        if (!ldap_error) {
1080
 
                ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
1081
 
                if (ldap_error) {
1082
 
                        E2K_GC_DEBUG_MSG(("GC: ldap_result failed: 0x%02x\n\n", ldap_error));
1083
 
                        return -1;
1084
 
                }
1085
 
        }
1086
 
        else {
1087
 
                E2K_GC_DEBUG_MSG(("GC: ldap_search failed:0x%02x \n\n", ldap_error));
1088
 
                return -1;
1089
 
        }
1090
 
 
1091
 
        values = ldap_get_values (ldap, msg, "maxPwdAge");
1092
 
        if (!values) {
1093
 
                E2K_GC_DEBUG_MSG(("GC: couldn't retrieve maxPwdAge\n"));
1094
 
                return -1;
1095
 
        }
1096
 
 
1097
 
        if (values[0]) {
1098
 
                val = values[0];
1099
 
                if (*val == '-')
1100
 
                        ++val;
1101
 
                maxAge = strtod (val, NULL);
1102
 
        }
1103
 
 
1104
 
        /*g_hash_table_insert (gc->priv->server_cache, g_strdup (dn), hostname); FIXME? */
1105
 
 
1106
 
        E2K_GC_DEBUG_MSG(("GC:   maxPwdAge = %f\n", maxAge));
1107
 
 
1108
 
        if (msg)
1109
 
                ldap_msgfree (msg);
1110
 
        if (values)
1111
 
                ldap_value_free (values);
1112
 
        ldap_unbind (ldap);
1113
 
        g_free (filter);
1114
 
        g_free (dn);
1115
 
        return maxAge;
1116
 
}
1117
 
 
1118
 
#if defined(G_OS_WIN32) || !defined(LDAP_TYPE_OR_VALUE_EXISTS)
1119
 
#define LDAP_TYPE_OR_VALUE_EXISTS 0x14
1120
 
#endif
1121
 
static E2kGlobalCatalogStatus
1122
 
do_delegate_op (E2kGlobalCatalog *gc, E2kOperation *op, gint deleg_op,
1123
 
                const gchar *self_dn, const gchar *delegate_dn)
1124
 
{
1125
 
        LDAP *ldap;
1126
 
        LDAPMod *mods[2], mod;
1127
 
        const gchar *ad_server;
1128
 
        gchar *values[2];
1129
 
        gint ldap_error, msgid;
1130
 
 
1131
 
        g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), E2K_GLOBAL_CATALOG_ERROR);
1132
 
        g_return_val_if_fail (self_dn != NULL, E2K_GLOBAL_CATALOG_ERROR);
1133
 
        g_return_val_if_fail (delegate_dn != NULL, E2K_GLOBAL_CATALOG_ERROR);
1134
 
 
1135
 
        ad_server = lookup_controlling_ad_server (gc, op, self_dn);
1136
 
        if (!ad_server) {
1137
 
                if (e2k_operation_is_cancelled (op))
1138
 
                        return E2K_GLOBAL_CATALOG_CANCELLED;
1139
 
                else
1140
 
                        return E2K_GLOBAL_CATALOG_ERROR;
1141
 
        }
1142
 
 
1143
 
        ldap_error = get_ldap_connection (gc, op, ad_server, LDAP_PORT, &ldap);
1144
 
        if (ldap_error == LDAP_USER_CANCELLED)
1145
 
                return E2K_GLOBAL_CATALOG_CANCELLED;
1146
 
        else if (ldap_error != LDAP_SUCCESS)
1147
 
                return E2K_GLOBAL_CATALOG_ERROR;
1148
 
 
1149
 
        mod.mod_op = deleg_op;
1150
 
        mod.mod_type = (gchar *) "publicDelegates";
1151
 
        mod.mod_values = values;
1152
 
        values[0] = (gchar *)delegate_dn;
1153
 
        values[1] = NULL;
1154
 
 
1155
 
        mods[0] = &mod;
1156
 
        mods[1] = NULL;
1157
 
 
1158
 
        ldap_error = ldap_modify_ext (ldap, self_dn, mods, NULL, NULL, &msgid);
1159
 
        if (ldap_error == LDAP_SUCCESS) {
1160
 
                LDAPMessage *msg;
1161
 
 
1162
 
                ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
1163
 
                if (ldap_error == LDAP_SUCCESS) {
1164
 
                        ldap_parse_result (ldap, msg, &ldap_error, NULL, NULL,
1165
 
                                           NULL, NULL, TRUE);
1166
 
                }
1167
 
        }
1168
 
        ldap_unbind (ldap);
1169
 
 
1170
 
        switch (ldap_error) {
1171
 
        case LDAP_SUCCESS:
1172
 
                E2K_GC_DEBUG_MSG(("\n"));
1173
 
                return E2K_GLOBAL_CATALOG_OK;
1174
 
 
1175
 
        case LDAP_NO_SUCH_OBJECT:
1176
 
                E2K_GC_DEBUG_MSG(("GC: no such user\n\n"));
1177
 
                return E2K_GLOBAL_CATALOG_NO_SUCH_USER;
1178
 
 
1179
 
        case LDAP_NO_SUCH_ATTRIBUTE:
1180
 
                E2K_GC_DEBUG_MSG(("GC: no such delegate\n\n"));
1181
 
                return E2K_GLOBAL_CATALOG_NO_DATA;
1182
 
 
1183
 
        case LDAP_CONSTRAINT_VIOLATION:
1184
 
                E2K_GC_DEBUG_MSG(("GC: bad delegate\n\n"));
1185
 
                return E2K_GLOBAL_CATALOG_BAD_DATA;
1186
 
 
1187
 
        case LDAP_TYPE_OR_VALUE_EXISTS:
1188
 
                E2K_GC_DEBUG_MSG(("GC: delegate already exists\n\n"));
1189
 
                return E2K_GLOBAL_CATALOG_EXISTS;
1190
 
 
1191
 
        case LDAP_USER_CANCELLED:
1192
 
                E2K_GC_DEBUG_MSG(("GC: cancelled\n\n"));
1193
 
                return E2K_GLOBAL_CATALOG_CANCELLED;
1194
 
 
1195
 
        default:
1196
 
                E2K_GC_DEBUG_MSG(("GC: ldap_modify failed: 0x%02x\n\n", ldap_error));
1197
 
                return E2K_GLOBAL_CATALOG_ERROR;
1198
 
        }
1199
 
}
1200
 
 
1201
 
/**
1202
 
 * e2k_global_catalog_add_delegate:
1203
 
 * @gc: the global catalog
1204
 
 * @op: pointer to an #E2kOperation to use for cancellation
1205
 
 * @self_dn: Active Directory DN of the user to add a delegate to
1206
 
 * @delegate_dn: Active Directory DN of the new delegate
1207
 
 *
1208
 
 * Attempts to make @delegate_dn a delegate of @self_dn.
1209
 
 *
1210
 
 * Return value: %E2K_GLOBAL_CATALOG_OK on success,
1211
 
 * %E2K_GLOBAL_CATALOG_NO_SUCH_USER if @self_dn is invalid,
1212
 
 * %E2K_GLOBAL_CATALOG_BAD_DATA if @delegate_dn is invalid,
1213
 
 * %E2K_GLOBAL_CATALOG_EXISTS if @delegate_dn is already a delegate,
1214
 
 * %E2K_GLOBAL_CATALOG_ERROR on other errors.
1215
 
 **/
1216
 
E2kGlobalCatalogStatus
1217
 
e2k_global_catalog_add_delegate (E2kGlobalCatalog *gc,
1218
 
                                 E2kOperation *op,
1219
 
                                 const gchar *self_dn,
1220
 
                                 const gchar *delegate_dn)
1221
 
{
1222
 
        E2K_GC_DEBUG_MSG(("\nGC: adding %s as delegate for %s\n", delegate_dn, self_dn));
1223
 
 
1224
 
        return do_delegate_op (gc, op, LDAP_MOD_ADD, self_dn, delegate_dn);
1225
 
}
1226
 
 
1227
 
/**
1228
 
 * e2k_global_catalog_remove_delegate:
1229
 
 * @gc: the global catalog
1230
 
 * @op: pointer to an #E2kOperation to use for cancellation
1231
 
 * @self_dn: Active Directory DN of the user to remove a delegate from
1232
 
 * @delegate_dn: Active Directory DN of the delegate to remove
1233
 
 *
1234
 
 * Attempts to remove @delegate_dn as a delegate of @self_dn.
1235
 
 *
1236
 
 * Return value: %E2K_GLOBAL_CATALOG_OK on success,
1237
 
 * %E2K_GLOBAL_CATALOG_NO_SUCH_USER if @self_dn is invalid,
1238
 
 * %E2K_GLOBAL_CATALOG_NO_DATA if @delegate_dn is not a delegate of @self_dn,
1239
 
 * %E2K_GLOBAL_CATALOG_ERROR on other errors.
1240
 
 **/
1241
 
E2kGlobalCatalogStatus
1242
 
e2k_global_catalog_remove_delegate (E2kGlobalCatalog *gc,
1243
 
                                    E2kOperation *op,
1244
 
                                    const gchar *self_dn,
1245
 
                                    const gchar *delegate_dn)
1246
 
{
1247
 
        E2K_GC_DEBUG_MSG(("\nGC: removing %s as delegate for %s\n", delegate_dn, self_dn));
1248
 
 
1249
 
        return do_delegate_op (gc, op, LDAP_MOD_DELETE, self_dn, delegate_dn);
1250
 
}