* Bug fix: Tcp and http debug information is not printed unless the
[alpine.git] / pith / ldap.c
bloba091fc26aed7824f41517f641dc74461ce28082e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: ldap.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/ldap.h"
21 #include "../pith/state.h"
22 #include "../pith/conf.h"
23 #include "../pith/status.h"
24 #include "../pith/util.h"
25 #include "../pith/imap.h"
26 #include "../pith/busy.h"
27 #include "../pith/signal.h"
28 #include "../pith/ablookup.h"
29 #include "../pith/readfile.h"
32 * Until we can think of a better way to do this. If user exits from an
33 * ldap address selection screen we want to return -1 so that call_builder
34 * will stay on the same line. Might want to use another return value
35 * for the builder so that call_builder more fully understands what we're
36 * up to.
38 int wp_exit;
39 int wp_nobail;
42 #ifdef ENABLE_LDAP
44 * Hook to allow user input on whether or not to save chosen LDAP result
46 void (*pith_opt_save_ldap_entry)(struct pine *, LDAP_CHOOSE_S *, int);
50 * Internal prototypes
52 LDAP_SERV_RES_S *ldap_lookup(LDAP_SERV_S *, char *, CUSTOM_FILT_S *, WP_ERR_S *, int);
53 LDAP_SERV_S *copy_ldap_serv_info(LDAP_SERV_S *);
54 int our_ldap_get_lderrno(LDAP *, char **, char **);
55 int our_ldap_set_lderrno(LDAP *, int, char *, char *);
56 #endif /* ENABLE_LDAP */
60 * This function does white pages lookups.
62 * Args string -- the string to use in the lookup
64 * Returns NULL -- lookup failed
65 * Address -- A single address is returned if lookup was successful.
67 ADDRESS *
68 wp_lookups(char *string, WP_ERR_S *wp_err, int recursing)
70 ADDRESS *ret_a = NULL;
71 char ebuf[200];
72 #ifdef ENABLE_LDAP
73 LDAP_SERV_RES_S *free_when_done = NULL;
74 LDAPLookupStyle style;
75 LDAP_CHOOSE_S *winning_e = NULL;
76 LDAP_SERV_S *info = NULL;
77 static char *fakedomain = "@";
78 char *tmp_a_string;
79 int auwe_rv = 0;
82 * Runtime ldap lookup of addrbook entry.
84 if(!strncmp(string, RUN_LDAP, LEN_RL)){
85 LDAP_SERV_RES_S *head_of_result_list;
87 info = break_up_ldap_server(string+LEN_RL);
88 head_of_result_list = ldap_lookup(info, "", NULL, wp_err, 1);
90 if(head_of_result_list){
91 if(!wp_exit)
92 auwe_rv = ask_user_which_entry(head_of_result_list, string,
93 &winning_e,
94 wp_err,
95 wp_err->wp_err_occurred
96 ? DisplayIfOne : DisplayIfTwo);
98 if(auwe_rv != -5)
99 free_when_done = head_of_result_list;
101 else{
102 wp_err->wp_err_occurred = 1;
103 if(wp_err->error){
104 q_status_message(SM_ORDER, 3, 5, wp_err->error);
105 display_message('x');
106 fs_give((void **)&wp_err->error);
109 /* try using backup email address */
110 if(info && info->mail && *info->mail){
111 tmp_a_string = cpystr(info->mail);
112 rfc822_parse_adrlist(&ret_a, tmp_a_string, fakedomain);
113 fs_give((void **)&tmp_a_string);
115 wp_err->error =
116 cpystr(_("Directory lookup failed, using backup email address"));
118 else{
120 * Do this so the awful LDAP: ... string won't show up
121 * in the composer. This shouldn't actually happen in
122 * real life, so we're not too concerned about it. If we
123 * were we'd want to recover the nickname we started with
124 * somehow, or something like that.
126 ret_a = mail_newaddr();
127 ret_a->mailbox = cpystr("missing-username");
128 wp_err->error = cpystr(_("Directory lookup failed, no backup email address available"));
131 q_status_message(SM_ORDER, 3, 5, wp_err->error);
132 display_message('x');
135 else{
136 style = F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
137 ? DisplayIfOne : DisplayIfTwo;
138 auwe_rv = ldap_lookup_all(string, as.n_serv, recursing, style, NULL,
139 &winning_e, wp_err, &free_when_done);
142 if(winning_e && auwe_rv != -5){
143 ret_a = address_from_ldap(winning_e);
145 if(pith_opt_save_ldap_entry && ret_a && F_ON(F_ADD_LDAP_TO_ABOOK, ps_global) && !info)
146 (*pith_opt_save_ldap_entry)(ps_global, winning_e, 1);
149 fs_give((void **)&winning_e);
152 /* Info's only set in the RUN_LDAP case */
153 if(info){
154 if(ret_a && ret_a->host){
155 ADDRESS *backup = NULL;
157 if(info->mail && *info->mail)
158 rfc822_parse_adrlist(&backup, info->mail, fakedomain);
160 if(!backup || !address_is_same(ret_a, backup)){
161 if(wp_err->error){
162 q_status_message(SM_ORDER, 3, 5, wp_err->error);
163 display_message('x');
164 fs_give((void **)&wp_err->error);
167 snprintf(ebuf, sizeof(ebuf),
168 _("Warning: current address different from saved address (%s)"),
169 info->mail);
170 wp_err->error = cpystr(ebuf);
171 q_status_message(SM_ORDER, 3, 5, wp_err->error);
172 display_message('x');
175 if(backup)
176 mail_free_address(&backup);
179 free_ldap_server_info(&info);
182 if(free_when_done)
183 free_ldap_result_list(&free_when_done);
184 #endif /* ENABLE_LDAP */
186 if(ret_a){
187 if(ret_a->mailbox){ /* indicates there was a MAIL attribute */
188 if(!ret_a->host || !ret_a->host[0]){
189 if(ret_a->host)
190 fs_give((void **)&ret_a->host);
192 ret_a->host = cpystr("missing-hostname");
193 wp_err->wp_err_occurred = 1;
194 if(wp_err->error)
195 fs_give((void **)&wp_err->error);
197 wp_err->error = cpystr(_("Missing hostname in LDAP address"));
198 q_status_message(SM_ORDER, 3, 5, wp_err->error);
199 display_message('x');
202 if(!ret_a->mailbox[0]){
203 if(ret_a->mailbox)
204 fs_give((void **)&ret_a->mailbox);
206 ret_a->mailbox = cpystr("missing-username");
207 wp_err->wp_err_occurred = 1;
208 if(wp_err->error)
209 fs_give((void **)&wp_err->error);
211 wp_err->error = cpystr(_("Missing username in LDAP address"));
212 q_status_message(SM_ORDER, 3, 5, wp_err->error);
213 display_message('x');
216 else{
217 wp_err->wp_err_occurred = 1;
219 if(wp_err->error)
220 fs_give((void **)&wp_err->error);
222 snprintf(ebuf, sizeof(ebuf), _("No email address available for \"%s\""),
223 (ret_a->personal && *ret_a->personal)
224 ? ret_a->personal
225 : "selected entry");
226 wp_err->error = cpystr(ebuf);
227 q_status_message(SM_ORDER, 3, 5, wp_err->error);
228 display_message('x');
229 mail_free_address(&ret_a);
230 ret_a = NULL;
234 return(ret_a);
238 #ifdef ENABLE_LDAP
241 * Goes through all servers looking up string.
243 * Args string -- String to search for
244 * who -- Which servers to look on
245 * style -- Sometimes we want to display no matter what, sometimes
246 * only if more than one entry, sometimes only if email
247 * addresses, ...
249 * The possible styles are:
250 * AlwaysDisplayAndMailRequired: This happens when user ^T's from
251 * composer to address book screen, and
252 * then queries directory server.
253 * AlwaysDisplay: This happens when user is browsing
254 * in address book maintenance screen and
255 * then queries directory server.
256 * DisplayIfOne: These two come from implicit lookups
257 * DisplayIfTwo: from build_address. If the compose rejects
258 * unqualified feature is on we get the
259 * DisplayIfOne, otherwise IfTwo.
261 * cust -- Use this custom filter instead of configured filters
262 * winning_e -- Return value
263 * wp_err -- Error handling
264 * free_when_done -- Caller needs to free this
266 * Returns -- value returned by ask_user_which_entry
269 ldap_lookup_all(char *string, int who, int recursing, LDAPLookupStyle style,
270 CUSTOM_FILT_S *cust, LDAP_CHOOSE_S **winning_e,
271 WP_ERR_S *wp_err, LDAP_SERV_RES_S **free_when_done)
273 int retval = -1;
274 LDAP_SERV_RES_S *head_of_result_list = NULL;
276 wp_exit = wp_nobail = 0;
277 if(free_when_done)
278 *free_when_done = NULL;
280 head_of_result_list = ldap_lookup_all_work(string, who, recursing,
281 cust, wp_err);
283 if(!wp_exit)
284 retval = ask_user_which_entry(head_of_result_list, string, winning_e,
285 wp_err,
286 (wp_err->wp_err_occurred &&
287 style == DisplayIfTwo) ? DisplayIfOne
288 : style);
291 * Because winning_e probably points into the result list
292 * we need to leave the result list alone and have the caller
293 * free it after they are done with winning_e.
295 if(retval != -5 && free_when_done)
296 *free_when_done = head_of_result_list;
298 return(retval);
303 * Goes through all servers looking up string.
305 * Args string -- String to search for
306 * who -- Which servers to look on
307 * cust -- Use this custom filter instead of configured filters
308 * wp_err -- Error handling
310 * Returns -- list of results that needs to be freed by caller
312 LDAP_SERV_RES_S *
313 ldap_lookup_all_work(char *string, int who, int recursing,
314 CUSTOM_FILT_S *cust, WP_ERR_S *wp_err)
316 int i;
317 LDAP_SERV_RES_S *serv_res;
318 LDAP_SERV_RES_S *rr, *head_of_result_list = NULL;
320 /* If there is at least one server */
321 if(ps_global->VAR_LDAP_SERVERS && ps_global->VAR_LDAP_SERVERS[0] &&
322 ps_global->VAR_LDAP_SERVERS[0][0]){
323 int how_many_servers;
325 for(i = 0; ps_global->VAR_LDAP_SERVERS[i] &&
326 ps_global->VAR_LDAP_SERVERS[i][0]; i++)
329 how_many_servers = i;
331 /* For each server in list */
332 for(i = 0; !wp_exit && ps_global->VAR_LDAP_SERVERS[i] &&
333 ps_global->VAR_LDAP_SERVERS[i][0]; i++){
334 LDAP_SERV_S *info;
336 dprint((6, "ldap_lookup_all_work: lookup on server (%.256s)\n",
337 ps_global->VAR_LDAP_SERVERS[i]));
338 info = NULL;
339 if(who == -1 || who == i || who == as.n_serv)
340 info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]);
343 * Who tells us which servers to look on.
344 * Who == -1 means all servers.
345 * Who == 0 means server[0].
346 * Who == 1 means server[1].
347 * Who == as.n_serv means query on those with impl set.
349 if(!(who == -1 || who == i ||
350 (who == as.n_serv && !recursing && info && info->impl) ||
351 (who == as.n_serv && recursing && info && info->rhs))){
353 if(info)
354 free_ldap_server_info(&info);
356 continue;
359 dprint((6, "ldap_lookup_all_work: ldap_lookup (server: %.20s...)(string: %s)\n",
360 ps_global->VAR_LDAP_SERVERS[i], string));
361 serv_res = ldap_lookup(info, string, cust,
362 wp_err, how_many_servers > 1);
363 if(serv_res){
364 /* Add new one to end of list so they come in the right order */
365 for(rr = head_of_result_list; rr && rr->next; rr = rr->next)
368 if(rr)
369 rr->next = serv_res;
370 else
371 head_of_result_list = serv_res;
374 if(info)
375 free_ldap_server_info(&info);
379 return(head_of_result_list);
384 * Do an LDAP lookup to the server described in the info argument.
386 * Args info -- LDAP info for server.
387 * string -- String to lookup.
388 * cust -- Possible custom filter description.
389 * wp_err -- We set this is we get a white pages error.
390 * name_in_error -- Caller sets this if they want us to include the server
391 * name in error messages.
393 * Returns Results of lookup, NULL if lookup failed.
395 LDAP_SERV_RES_S *
396 ldap_lookup(LDAP_SERV_S *info, char *string, CUSTOM_FILT_S *cust,
397 WP_ERR_S *wp_err, int name_in_error)
399 char ebuf[900];
400 char buf[900];
401 char *serv, *base, *serv_errstr, *s, *t;
402 char *mailattr, *snattr, *gnattr, *cnattr;
403 int we_cancel = 0, we_turned_on = 0;
404 LDAP_SERV_RES_S *serv_res = NULL;
405 LDAP *ld = NULL;
406 long pwdtrial = 0L;
407 int ld_errnum;
408 char *ld_errstr;
411 if(!info)
412 return(serv_res);
414 serv = cpystr((info->serv && *info->serv) ? info->serv : "?");
416 if(name_in_error)
417 snprintf(ebuf, sizeof(ebuf), " (%s)",
418 (info->nick && *info->nick) ? info->nick : serv);
419 else
420 ebuf[0] = '\0';
422 serv_errstr = cpystr(ebuf);
423 base = cpystr(info->base ? info->base : "");
425 if(info->port < 0)
426 info->port = LDAP_PORT;
428 if(info->type < 0)
429 info->type = DEF_LDAP_TYPE;
431 if(info->srch < 0)
432 info->srch = DEF_LDAP_SRCH;
434 if(info->time < 0)
435 info->time = DEF_LDAP_TIME;
437 if(info->size < 0)
438 info->size = DEF_LDAP_SIZE;
440 if(info->scope < 0)
441 info->scope = DEF_LDAP_SCOPE;
443 mailattr = (info->mailattr && info->mailattr[0]) ? info->mailattr
444 : DEF_LDAP_MAILATTR;
445 snattr = (info->snattr && info->snattr[0]) ? info->snattr
446 : DEF_LDAP_SNATTR;
447 gnattr = (info->gnattr && info->gnattr[0]) ? info->gnattr
448 : DEF_LDAP_GNATTR;
449 cnattr = (info->cnattr && info->cnattr[0]) ? info->cnattr
450 : DEF_LDAP_CNATTR;
453 * We may want to keep ldap handles open, but at least for
454 * now, re-open them every time.
457 dprint((3, "ldap_lookup(%s,%d)\n", serv ? serv : "?", info->port));
459 snprintf(ebuf, sizeof(ebuf), "Searching%s%s%s on %s",
460 (string && *string) ? " for \"" : "",
461 (string && *string) ? string : "",
462 (string && *string) ? "\"" : "",
463 serv);
464 we_turned_on = intr_handling_on(); /* this erases keymenu */
465 we_cancel = busy_cue(ebuf, NULL, 0);
466 if(wp_err->mangled)
467 *(wp_err->mangled) = 1;
469 #ifdef _SOLARIS_SDK
470 if(info->tls || info->tlsmust)
471 ldapssl_client_init(NULL, NULL);
472 if((ld = ldap_init(serv, info->port)) == NULL)
473 #else
474 #if (LDAPAPI >= 11)
475 #ifdef _WINDOWS
476 if((ld = ldap_init(serv, info->port)) == NULL)
477 #else
478 #ifdef SMIME_SSLCERTS
479 /* If we are attempting a ldaps secure connection, we need to tell
480 * ldap that we have certificates. There are many ways to do so.
481 * OpenLDAP has many ways to configure this through configuration
482 * files. For example the global (to the system) ldap.conf file, or the
483 * personal ldaprc or .ldaprc files. Setting the location of the
484 * certificates must happen at the time we call ldap_initialize, we
485 * cannot set it up before that call, nor after, so what we are going
486 * to do is to test for a .ldaprc file in the home directory. If such
487 * file exists we read it, if not we create it and if it does not have
488 * a line for the location of the certificates in the system, we add one.
489 * (so we ignore all other configuration files)
491 if(info->ldaps && ps_global->home_dir){
492 int done = 0;
493 char *text, *tls_conf;
494 char filename[MAXPATH+1];
496 build_path(filename, ps_global->home_dir, ".ldaprc", sizeof(filename));
498 if((text = read_file(filename, 0)) != NULL)
499 while(done == 0
500 && (tls_conf = strstr(text, "TLS_CACERTDIR")) != NULL
501 && (tls_conf == text || *(tls_conf - 1) == '\n')){
502 tls_conf += 13; /* 13 = strlen("TLS_CACERTDIR") */
503 while (isspace(*tls_conf))
504 tls_conf++;
505 if(!strncmp(tls_conf, SMIME_SSLCERTS, strlen(SMIME_SSLCERTS)))
506 done++;
509 if(!done){
510 STORE_S *so;
512 if((so = so_get(FileStar, filename, WRITE_ACCESS)) != NULL){
513 if(text != NULL){
514 so_puts(so, text);
515 so_puts(so, NEWLINE);
517 so_puts(so, "TLS_CACERTDIR");
518 so_puts(so, " ");
519 so_puts(so, SMIME_SSLCERTS);
520 so_puts(so, NEWLINE);
521 so_give(&so);
524 if(text != NULL)
525 fs_give((void **)&text);
527 #endif /* SMIME_SSLCERTS */
529 tmp_20k_buf[0] = '\0';
530 s = serv;
531 do {
532 if ((t = strchr(s, ' ')) != NULL) *t = '\0';
533 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
534 SIZEOF_20KBUF - strlen(tmp_20k_buf), "%s://%s",
535 info->ldaps ? "ldaps" : "ldap", s);
536 if (strchr(s, ':') == NULL){
537 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
538 SIZEOF_20KBUF - strlen(tmp_20k_buf),
539 "%s%d", ":", info->port);
541 if(t != NULL){
542 *t = ' ';
543 for( ; *t == ' '; t++);
544 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
545 SIZEOF_20KBUF - strlen(tmp_20k_buf), "%s", " ");
547 s = t;
548 } while (s != NULL);
550 tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
552 if(ldap_initialize(&ld, tmp_20k_buf) != LDAP_SUCCESS)
553 #endif
554 #else
555 if((ld = ldap_open(serv, info->port)) == NULL)
556 #endif
557 #endif
559 /* TRANSLATORS: All of the three args together are an error message */
560 snprintf(ebuf, sizeof(ebuf), _("Access to LDAP server failed: %s%s(%s)"),
561 errno ? error_description(errno) : "",
562 errno ? " " : "",
563 serv);
564 wp_err->wp_err_occurred = 1;
565 if(wp_err->error)
566 fs_give((void **)&wp_err->error);
568 wp_err->error = cpystr(ebuf);
569 if(we_cancel)
570 cancel_busy_cue(-1);
572 q_status_message(SM_ORDER, 3, 5, wp_err->error);
573 display_message('x');
574 dprint((2, "%s\n", ebuf));
576 else if(!ps_global->intr_pending){
577 int proto = 3, tlsmustbail = 0;
578 char *pwd = NULL, user[NETMAXUSER];
579 #ifdef _WINDOWS
580 char *passwd = NULL;
581 #else
582 struct berval passwd = { 0 };
583 #endif
584 char hostbuf[1024];
585 NETMBX mb;
586 #ifndef _WINDOWS
587 int rc;
588 #endif
590 memset(&mb, 0, sizeof(mb));
592 #ifdef _SOLARIS_SDK
593 if(info->tls || info->tlsmust)
594 rc = ldapssl_install_routines(ld);
595 #endif
597 if(ldap_v3_is_supported(ld) &&
598 our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
599 dprint((5, "ldap: using version 3 protocol\n"));
603 * If we don't set RESTART then the select() waiting for the answer
604 * in libldap will be interrupted and stopped by our busy_cue.
606 our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
609 * If we need to authenticate, get the password
611 if(info->binddn && info->binddn[0]){
612 char pmt[500];
613 char *space;
615 snprintf(hostbuf, sizeof(hostbuf), "{%s}dummy", info->serv ? info->serv : "?");
618 * We don't handle multiple space-delimited hosts well.
619 * We don't know which we're asking for a password for.
620 * We're not connected yet so we can't know.
622 if((space=strindex(hostbuf, ' ')) != NULL)
623 *space = '\0';
625 mail_valid_net_parse_work(hostbuf, &mb, info->ldaps ? "ldaps" : "ldap");
626 mb.port = info->port;
627 mb.tlsflag = (info->tls || info->tlsmust) ? 1 : 0;
628 mb.sslflag = info->ldaps ? 1 : 0;
630 try_password_again:
632 if(mb.tlsflag
633 && (pwdtrial > 0 ||
634 #ifndef _WINDOWS
635 #ifdef _SOLARIS_SDK
636 (rc == LDAP_SUCCESS)
637 #else /* !_SOLARIS_SDK */
638 ((rc=ldap_start_tls_s(ld, NULL, NULL)) == LDAP_SUCCESS)
639 #endif /* !_SOLARIS_SDK */
640 #else /* _WINDOWS */
641 0 /* TODO: find a way to do this in Windows */
642 #endif /* _WINDOWS */
644 mb.tlsflag = 1;
645 else
646 mb.tlsflag = 0;
648 if((info->tls || info->tlsmust) && !mb.tlsflag){
649 q_status_message(SM_ORDER, 3, 5, "Not able to start TLS encryption for LDAP server");
650 if(info->tlsmust)
651 tlsmustbail++;
654 if(!tlsmustbail){
655 snprintf(pmt, sizeof(pmt), " %s", (info->nick && *info->nick) ? info->nick : serv);
656 mm_login_work(&mb, user, &pwd, pwdtrial, pmt, info->binddn);
657 if(pwd && pwd[0]){
658 #ifdef _WINDOWS
659 passwd = pwd;
660 #else
661 passwd.bv_len = strlen(pwd);
662 passwd.bv_val = pwd;
663 #endif
670 * LDAPv2 requires the bind. v3 doesn't require it but we want
671 * to tell the server we're v3 if the server supports v3, and if the
672 * server doesn't support v3 the bind is required.
674 if(tlsmustbail
675 #ifdef _WINDOWS
676 || ldap_simple_bind_s(ld, info->binddn, passwd) != LDAP_SUCCESS){
677 #else
678 || ldap_sasl_bind_s(ld, info->binddn, LDAP_SASL_SIMPLE, &passwd, NULL, NULL, NULL) != LDAP_SUCCESS){
679 #endif
680 wp_err->wp_err_occurred = 1;
682 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
684 if(!tlsmustbail && info->binddn && info->binddn[0] && pwdtrial < 2L
685 && ld_errnum == LDAP_INVALID_CREDENTIALS){
686 pwdtrial++;
687 q_status_message(SM_ORDER, 3, 5, _("Invalid password"));
688 goto try_password_again;
691 snprintf(ebuf, sizeof(ebuf), _("LDAP server failed: %s%s%s%s"),
692 ldap_err2string(ld_errnum),
693 serv_errstr,
694 (ld_errstr && *ld_errstr) ? ": " : "",
695 (ld_errstr && *ld_errstr) ? ld_errstr : "");
697 if(wp_err->error)
698 fs_give((void **)&wp_err->error);
700 if(we_cancel)
701 cancel_busy_cue(-1);
702 #ifdef _WINDOWS
703 ldap_unbind(ld);
704 #else
705 ldap_unbind_ext(ld, NULL, NULL);
706 #endif
707 wp_err->error = cpystr(ebuf);
708 q_status_message(SM_ORDER, 3, 5, wp_err->error);
709 display_message('x');
710 dprint((2, "%s\n", ebuf));
712 else if(!ps_global->intr_pending){
713 int srch_res = LDAP_SUCCESS, args, slen, flen;
714 #define TEMPLATELEN 512
715 char filt_template[TEMPLATELEN + 1];
716 char filt_format[2*TEMPLATELEN + 1];
717 char filter[2*TEMPLATELEN + 1];
718 char scp[2*TEMPLATELEN + 1];
719 char *p, *q;
720 LDAPMessage *res = NULL;
721 int intr_happened = 0;
722 int tl;
724 tl = (info->time == 0) ? info->time : info->time + 10;
726 our_ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &tl);
727 our_ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &info->size);
730 * If a custom filter has been passed in and it doesn't include a
731 * request to combine it with the configured filter, then replace
732 * any configured filter with the passed in filter.
734 if(cust && cust->filt && !cust->combine){
735 if(info->cust)
736 fs_give((void **)&info->cust);
738 info->cust = cpystr(cust->filt);
741 if(info->cust && *info->cust){ /* use custom filter if present */
742 strncpy(filt_template, info->cust, sizeof(filt_template));
743 filt_template[sizeof(filt_template)-1] = '\0';
745 else{ /* else use configured filter */
746 switch(info->type){
747 case LDAP_TYPE_SUR:
748 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", snattr);
749 break;
750 case LDAP_TYPE_GIVEN:
751 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", gnattr);
752 break;
753 case LDAP_TYPE_EMAIL:
754 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", mailattr);
755 break;
756 case LDAP_TYPE_CN_EMAIL:
757 snprintf(filt_template, sizeof(filt_template), "(|(%s=%%s)(%s=%%s))", cnattr,
758 mailattr);
759 break;
760 case LDAP_TYPE_SUR_GIVEN:
761 snprintf(filt_template, sizeof(filt_template), "(|(%s=%%s)(%s=%%s))",
762 snattr, gnattr);
763 break;
764 case LDAP_TYPE_SEVERAL:
765 snprintf(filt_template, sizeof(filt_template),
766 "(|(%s=%%s)(%s=%%s)(%s=%%s)(%s=%%s))",
767 cnattr, mailattr, snattr, gnattr);
768 break;
769 default:
770 case LDAP_TYPE_CN:
771 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", cnattr);
772 break;
776 /* just copy if custom */
777 if(info->cust && *info->cust)
778 info->srch = LDAP_SRCH_EQUALS;
780 p = filt_template;
781 q = filt_format;
782 memset((void *)filt_format, 0, sizeof(filt_format));
783 args = 0;
784 while(*p && (q - filt_format) + 4 < sizeof(filt_format)){
785 if(*p == '%' && *(p+1) == 's'){
786 args++;
787 switch(info->srch){
788 /* Exact match */
789 case LDAP_SRCH_EQUALS:
790 *q++ = *p++;
791 *q++ = *p++;
792 break;
794 /* Append wildcard after %s */
795 case LDAP_SRCH_BEGINS:
796 *q++ = *p++;
797 *q++ = *p++;
798 *q++ = '*';
799 break;
801 /* Insert wildcard before %s */
802 case LDAP_SRCH_ENDS:
803 *q++ = '*';
804 *q++ = *p++;
805 *q++ = *p++;
806 break;
808 /* Put wildcard before and after %s */
809 default:
810 case LDAP_SRCH_CONTAINS:
811 *q++ = '*';
812 *q++ = *p++;
813 *q++ = *p++;
814 *q++ = '*';
815 break;
818 else
819 *q++ = *p++;
822 if(q - filt_format < sizeof(filt_format))
823 *q = '\0';
825 filt_format[sizeof(filt_format)-1] = '\0';
828 * If combine is lit we put the custom filter and the filt_format
829 * filter and combine them with an &.
831 if(cust && cust->filt && cust->combine){
832 char *combined;
833 size_t l;
835 l = strlen(filt_format) + strlen(cust->filt) + 3;
836 combined = (char *) fs_get((l+1) * sizeof(char));
837 snprintf(combined, l+1, "(&%s%s)", cust->filt, filt_format);
838 strncpy(filt_format, combined, sizeof(filt_format));
839 filt_format[sizeof(filt_format)-1] = '\0';
840 fs_give((void **) &combined);
844 * Ad hoc attempt to make "Steve Hubert" match
845 * Steven Hubert but not Steven Shubert.
846 * We replace a <SPACE> with * <SPACE> (not * <SPACE> *).
848 memset((void *)scp, 0, sizeof(scp));
849 if(info->nosub)
850 strncpy(scp, string, sizeof(scp));
851 else{
852 p = string;
853 q = scp;
854 while(*p && (q - scp) + 1 < sizeof(scp)){
855 if(*p == SPACE && *(p+1) != SPACE){
856 *q++ = '*';
857 *q++ = *p++;
859 else
860 *q++ = *p++;
864 scp[sizeof(scp)-1] = '\0';
866 slen = strlen(scp);
867 flen = strlen(filt_format);
868 /* truncate string if it will overflow filter */
869 if(args*slen + flen - 2*args > sizeof(filter)-1)
870 scp[(sizeof(filter)-1 - flen)/args] = '\0';
873 * Replace %s's with scp.
875 switch(args){
876 case 0:
877 snprintf(filter, sizeof(filter), "%s", filt_format);
878 break;
879 case 1:
880 snprintf(filter, sizeof(filter), filt_format, scp);
881 break;
882 case 2:
883 snprintf(filter, sizeof(filter), filt_format, scp, scp);
884 break;
885 case 3:
886 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp);
887 break;
888 case 4:
889 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp);
890 break;
891 case 5:
892 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp);
893 break;
894 case 6:
895 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp);
896 break;
897 case 7:
898 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp);
899 break;
900 case 8:
901 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
902 scp);
903 break;
904 case 9:
905 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
906 scp, scp);
907 break;
908 case 10:
909 default:
910 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
911 scp, scp, scp);
912 break;
915 /* replace double *'s with single *'s in filter */
916 for(p = q = filter; *p; p++)
917 if(*p != '*' || p == filter || *(p-1) != '*')
918 *q++ = *p;
920 *q = '\0';
922 (void) removing_double_quotes(base);
923 dprint((5, "about to ldap_search(\"%s\", %s)\n",
924 base ? base : "?", filter ? filter : "?"));
925 if(ps_global->intr_pending)
926 srch_res = LDAP_PROTOCOL_ERROR;
927 else{
928 int msgid;
929 time_t start_time;
930 struct timeval tv = {0}, *tvp = NULL;
932 memset((void *)&tv, 0, sizeof(struct timeval));
933 tv.tv_sec = info->time;
934 tvp = &tv;
936 start_time = time((time_t *)0);
938 dprint((6, "ldap_lookup: calling ldap_search\n"));
939 #ifdef _WINDOWS
940 msgid = ldap_search(ld, base, info->scope, filter, NULL, 0);
941 #else
942 if(ldap_search_ext(ld, base, info->scope, filter, NULL, 0,
943 NULL, NULL, tvp, info->size, &msgid) != LDAP_SUCCESS)
944 msgid = -1;
945 #endif
947 if(msgid == -1)
948 srch_res = our_ldap_get_lderrno(ld, NULL, NULL);
949 else{
950 int lres;
952 * Warning: struct timeval is not portable. However, since it is
953 * part of LDAP api it must be portable to all platforms LDAP
954 * has been ported to.
956 struct timeval t;
958 t.tv_sec = 1; t.tv_usec = 0;
960 do {
961 if(ps_global->intr_pending)
962 intr_happened = 1;
964 dprint((6, "ldap_result(id=%d): ", msgid));
965 if((lres=ldap_result(ld, msgid, LDAP_MSG_ALL, &t, &res)) == -1){
966 /* error */
967 srch_res = our_ldap_get_lderrno(ld, NULL, NULL);
968 dprint((6, "error (-1 returned): ld_errno=%d\n",
969 srch_res));
971 else if(lres == 0){ /* timeout, no results available */
972 if(intr_happened){
973 #ifdef _WINDOWS
974 ldap_abandon(ld, msgid);
975 #else
976 ldap_abandon_ext(ld, msgid, NULL, NULL);
977 #endif
978 srch_res = LDAP_PROTOCOL_ERROR;
979 if(our_ldap_get_lderrno(ld, NULL, NULL) == LDAP_SUCCESS)
980 our_ldap_set_lderrno(ld, LDAP_PROTOCOL_ERROR, NULL, NULL);
982 dprint((6, "timeout, intr: srch_res=%d\n",
983 srch_res));
985 else if(info->time > 0 &&
986 ((long)time((time_t *)0) - start_time) > info->time){
987 /* try for partial results */
988 t.tv_sec = 0; t.tv_usec = 0;
989 lres = ldap_result(ld, msgid, LDAP_MSG_RECEIVED, &t, &res);
990 if(lres > 0 && lres != LDAP_RES_SEARCH_RESULT){
991 srch_res = LDAP_SUCCESS;
992 dprint((6, "partial result: lres=0x%x\n", lres));
994 else{
995 if(lres == 0)
996 #ifdef _WINDOWS
997 ldap_abandon(ld, msgid);
998 #else
999 ldap_abandon_ext(ld, msgid, NULL, NULL);
1000 #endif
1002 srch_res = LDAP_TIMEOUT;
1003 if(our_ldap_get_lderrno(ld, NULL, NULL) == LDAP_SUCCESS)
1004 our_ldap_set_lderrno(ld, LDAP_TIMEOUT, NULL, NULL);
1006 dprint((6,
1007 "timeout, total_time (%d), srch_res=%d\n",
1008 info->time, srch_res));
1011 else{
1012 dprint((6, "timeout\n"));
1015 else{
1016 #ifdef _WINDOWS
1017 srch_res = ldap_result2error(ld, res, 0);
1018 dprint((6, "lres=0x%x, srch_res=%d\n", lres,
1019 srch_res));
1020 #else
1021 int err;
1022 char *dn, *text, **ref;
1023 LDAPControl **srv;
1025 dn = text = NULL; ref = NULL; srv = NULL;
1026 srch_res = ldap_parse_result(ld, res,
1027 &err, &dn, &text, &ref, &srv, 0);
1028 dprint((6, "lres=0x%x, srch_res=%d, dn=%s, text=%s\n", lres,
1029 srch_res, dn ? dn : "", text ? text : ""));
1030 if(dn) ber_memfree(dn);
1031 if(text) ber_memfree(text);
1032 if(ref) ber_memvfree((void **) ref);
1033 if(srv) ldap_controls_free(srv);
1034 #endif
1036 }while(lres == 0 &&
1037 !(intr_happened ||
1038 (info->time > 0 &&
1039 ((long)time((time_t *)0) - start_time) > info->time)));
1043 if(intr_happened){
1044 wp_exit = 1;
1045 if(we_cancel)
1046 cancel_busy_cue(-1);
1048 if(wp_err->error)
1049 fs_give((void **)&wp_err->error);
1050 else{
1051 q_status_message(SM_ORDER, 0, 1, "Interrupt");
1052 display_message('x');
1053 fflush(stdout);
1056 if(res)
1057 ldap_msgfree(res);
1058 if(ld)
1059 #ifdef _WINDOWS
1060 ldap_unbind(ld);
1061 #else
1062 ldap_unbind_ext(ld, NULL, NULL);
1063 #endif
1065 res = NULL; ld = NULL;
1067 else if(srch_res != LDAP_SUCCESS &&
1068 srch_res != LDAP_TIMELIMIT_EXCEEDED &&
1069 srch_res != LDAP_RESULTS_TOO_LARGE &&
1070 srch_res != LDAP_TIMEOUT &&
1071 srch_res != LDAP_SIZELIMIT_EXCEEDED){
1072 wp_err->wp_err_occurred = 1;
1074 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
1076 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s%s%s%s"),
1077 ldap_err2string(ld_errnum),
1078 serv_errstr,
1079 (ld_errstr && *ld_errstr) ? ": " : "",
1080 (ld_errstr && *ld_errstr) ? ld_errstr : "");
1082 if(wp_err->error)
1083 fs_give((void **)&wp_err->error);
1085 wp_err->error = cpystr(ebuf);
1086 if(we_cancel)
1087 cancel_busy_cue(-1);
1089 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1090 display_message('x');
1091 dprint((2, "%s\n", ebuf));
1092 if(res)
1093 ldap_msgfree(res);
1094 if(ld)
1095 #ifdef _WINDOWS
1096 ldap_unbind(ld);
1097 #else
1098 ldap_unbind_ext(ld, NULL, NULL);
1099 #endif
1101 res = NULL; ld = NULL;
1103 else{
1104 int cnt;
1106 cnt = ldap_count_entries(ld, res);
1108 if(cnt > 0){
1110 if(srch_res == LDAP_TIMELIMIT_EXCEEDED ||
1111 srch_res == LDAP_RESULTS_TOO_LARGE ||
1112 srch_res == LDAP_TIMEOUT ||
1113 srch_res == LDAP_SIZELIMIT_EXCEEDED){
1114 wp_err->wp_err_occurred = 1;
1115 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
1117 snprintf(ebuf, sizeof(ebuf), _("LDAP partial results: %s%s%s%s"),
1118 ldap_err2string(ld_errnum),
1119 serv_errstr,
1120 (ld_errstr && *ld_errstr) ? ": " : "",
1121 (ld_errstr && *ld_errstr) ? ld_errstr : "");
1122 dprint((2, "%s\n", ebuf));
1123 if(wp_err->error)
1124 fs_give((void **)&wp_err->error);
1126 wp_err->error = cpystr(ebuf);
1127 if(we_cancel)
1128 cancel_busy_cue(-1);
1130 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1131 display_message('x');
1134 dprint((5, "Matched %d entries on %s\n",
1135 cnt, serv ? serv : "?"));
1137 serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
1138 memset((void *)serv_res, 0, sizeof(*serv_res));
1139 serv_res->ld = ld;
1140 serv_res->res = res;
1141 serv_res->info_used = copy_ldap_serv_info(info);
1142 /* Save by reference? */
1143 if(info->ref){
1144 snprintf(buf, sizeof(buf), "%s:%s", serv, comatose(info->port));
1145 serv_res->serv = cpystr(buf);
1147 else
1148 serv_res->serv = NULL;
1150 serv_res->next = NULL;
1152 else{
1153 if(srch_res == LDAP_TIMELIMIT_EXCEEDED ||
1154 srch_res == LDAP_RESULTS_TOO_LARGE ||
1155 srch_res == LDAP_TIMEOUT ||
1156 srch_res == LDAP_SIZELIMIT_EXCEEDED){
1157 wp_err->wp_err_occurred = 1;
1158 wp_err->ldap_errno = srch_res;
1160 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
1162 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s%s%s%s"),
1163 ldap_err2string(ld_errnum),
1164 serv_errstr,
1165 (ld_errstr && *ld_errstr) ? ": " : "",
1166 (ld_errstr && *ld_errstr) ? ld_errstr : "");
1168 if(wp_err->error)
1169 fs_give((void **)&wp_err->error);
1171 wp_err->error = cpystr(ebuf);
1172 if(we_cancel)
1173 cancel_busy_cue(-1);
1175 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1176 display_message('x');
1177 dprint((2, "%s\n", ebuf));
1180 dprint((5, "Matched 0 entries on %s\n",
1181 serv ? serv : "?"));
1182 if(res)
1183 ldap_msgfree(res);
1184 if(ld)
1185 #ifdef _WINDOWS
1186 ldap_unbind(ld);
1187 #else
1188 ldap_unbind_ext(ld, NULL, NULL);
1189 #endif
1191 res = NULL; ld = NULL;
1195 if(pwd)
1196 fs_give((void **) &pwd);
1199 if(we_cancel)
1200 cancel_busy_cue(-1);
1202 if(we_turned_on)
1203 intr_handling_off();
1205 if(serv)
1206 fs_give((void **)&serv);
1207 if(base)
1208 fs_give((void **)&base);
1209 if(serv_errstr)
1210 fs_give((void **)&serv_errstr);
1212 return(serv_res);
1217 * Given a list of entries, present them to user so user may
1218 * select one.
1220 * Args head -- The head of the list of results
1221 * orig -- The string the user was searching for
1222 * result -- Returned pointer to chosen LDAP_SEARCH_WINNER or NULL
1223 * wp_err -- Error handling
1224 * style --
1226 * Returns 0 ok
1227 * -1 Exit chosen by user
1228 * -2 None of matched entries had an email address
1229 * -3 No matched entries
1230 * -4 Goback to Abook List chosen by user
1231 * -5 caller shouldn't free head
1234 ask_user_which_entry(LDAP_SERV_RES_S *head, char *orig, LDAP_CHOOSE_S **result,
1235 WP_ERR_S *wp_err, LDAPLookupStyle style)
1237 ADDR_CHOOSE_S ac;
1238 char t[200];
1239 int retval;
1241 dprint((3, "ask_user_which(style=%s)\n",
1242 style == AlwaysDisplayAndMailRequired ? "AlwaysDisplayAndMailRequired" :
1243 style == AlwaysDisplay ? "AlwaysDisplay" :
1244 style == DisplayIfTwo ? "DisplayIfTwo" :
1245 style == DisplayForURL ? "DisplayForURL" :
1246 style == DisplayIfOne ? "DisplayIfOne" : "?"));
1249 * Set up a screen for user to choose one entry.
1252 if(style == AlwaysDisplay || style == DisplayForURL)
1253 snprintf(t, sizeof(t), "SEARCH RESULTS INDEX");
1254 else{
1255 int len;
1257 len = strlen(orig);
1258 snprintf(t, sizeof(t), _("SELECT ONE ADDRESS%s%s%s"),
1259 (orig && *orig && len < 40) ? " FOR \"" : "",
1260 (orig && *orig && len < 40) ? orig : "",
1261 (orig && *orig && len < 40) ? "\"" : "");
1264 memset(&ac, 0, sizeof(ADDR_CHOOSE_S));
1265 ac.title = cpystr(t);
1266 ac.res_head = head;
1268 retval = ldap_addr_select(ps_global, &ac, result, style, wp_err, orig);
1270 switch(retval){
1271 case 0: /* Ok */
1272 break;
1274 case -1: /* Exit chosen by user */
1275 wp_exit = 1;
1276 break;
1278 case -4: /* GoBack to AbookList chosen by user */
1279 break;
1281 case -5:
1282 wp_nobail = 1;
1283 break;
1285 case -2:
1286 if(style != AlwaysDisplay){
1287 if(wp_err->error)
1288 fs_give((void **)&wp_err->error);
1290 wp_err->error =
1291 cpystr(_("None of the names matched on directory server has an email address"));
1292 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1293 display_message('x');
1296 break;
1298 case -3:
1299 if(style == AlwaysDisplayAndMailRequired){
1300 if(wp_err->error)
1301 fs_give((void **)&wp_err->error);
1303 wp_err->error = cpystr(_("No matches on directory server"));
1304 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1305 display_message('x');
1308 break;
1311 fs_give((void **)&ac.title);
1313 return(retval);
1317 ADDRESS *
1318 address_from_ldap(LDAP_CHOOSE_S *winning_e)
1320 ADDRESS *ret_a = NULL;
1322 if(winning_e){
1323 char *a;
1324 BerElement *ber;
1326 ret_a = mail_newaddr();
1327 for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
1328 a != NULL;
1329 a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
1330 int i;
1331 char *p;
1332 struct berval **vals;
1334 dprint((9, "attribute: %s\n", a ? a : "?"));
1335 if(!ret_a->personal &&
1336 strcmp(a, winning_e->info_used->cnattr) == 0){
1337 dprint((9, "Got cnattr:"));
1338 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
1339 for(i = 0; i < ldap_count_values_len(vals); i++)
1340 dprint((9, " %s\n",
1341 vals[i] ? vals[i]->bv_val : "?"));
1343 if(ALPINE_LDAP_can_use(vals))
1344 ret_a->personal = cpystr(vals[0]->bv_val);
1346 ldap_value_free_len(vals);
1348 else if(!ret_a->mailbox &&
1349 strcmp(a, winning_e->info_used->mailattr) == 0){
1350 dprint((9, "Got mailattr:"));
1351 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
1352 for(i = 0; i < ldap_count_values_len(vals); i++)
1353 dprint((9, " %s\n",
1354 vals[i] ? vals[i]->bv_val : "?"));
1356 /* use first one */
1357 if(ALPINE_LDAP_can_use(vals)){
1358 if((p = strindex(vals[0]->bv_val, '@')) != NULL){
1359 ret_a->host = cpystr(p+1);
1360 *p = '\0';
1363 ret_a->mailbox = cpystr(vals[0]->bv_val);
1366 ldap_value_free_len(vals);
1369 our_ldap_memfree(a);
1373 return(ret_a);
1378 * Break up the ldap-server string stored in the pinerc into its
1379 * parts. The structure is allocated here and should be freed by the caller.
1381 * The original string looks like
1382 * <servername>[:port] <SPACE> "/base=<base>/impl=1/..."
1384 * Args serv_str -- The original string from the pinerc to parse.
1386 * Returns A pointer to a structure with filled in answers.
1388 * Some of the members have defaults. If port is -1, that means to use
1389 * the default LDAP_PORT. If base is NULL, use "". Type and srch have
1390 * defaults defined in alpine.h. If cust is non-NULL, it overrides type and
1391 * srch.
1393 LDAP_SERV_S *
1394 break_up_ldap_server(char *serv_str)
1396 char *lserv;
1397 char *q, *p, *tail;
1398 int i, only_one = 1;
1399 LDAP_SERV_S *info = NULL;
1401 if(!serv_str)
1402 return(info);
1404 info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
1407 * Initialize to defaults.
1409 memset((void *)info, 0, sizeof(*info));
1410 info->port = -1;
1411 info->srch = -1;
1412 info->type = -1;
1413 info->time = -1;
1414 info->size = -1;
1415 info->scope = -1;
1417 /* copy the whole string to work on */
1418 lserv = cpystr(serv_str);
1419 if(lserv)
1420 removing_trailing_white_space(lserv);
1422 if(!lserv || !*lserv || *lserv == '"'){
1423 if(lserv)
1424 fs_give((void **)&lserv);
1426 if(info)
1427 free_ldap_server_info(&info);
1429 return(NULL);
1432 tail = lserv;
1433 while((tail = strindex(tail, SPACE)) != NULL){
1434 tail++;
1435 if(*tail == '"' || *tail == '/'){
1436 *(tail-1) = '\0';
1437 break;
1439 else
1440 only_one = 0;
1443 /* tail is the part after server[:port] <SPACE> */
1444 if(tail && *tail){
1445 removing_leading_white_space(tail);
1446 (void)removing_double_quotes(tail);
1449 /* get the optional port number */
1450 if(only_one && (q = strindex(lserv, ':')) != NULL){
1451 int ldapport = -1;
1453 *q = '\0';
1454 if((ldapport = atoi(q+1)) >= 0)
1455 info->port = ldapport;
1458 /* use lserv for serv even though it has a few extra bytes alloced */
1459 info->serv = lserv;
1461 if(tail && *tail){
1462 /* get the search base */
1463 if((q = srchstr(tail, "/base=")) != NULL)
1464 info->base = remove_backslash_escapes(q+6);
1466 if((q = srchstr(tail, "/binddn=")) != NULL)
1467 info->binddn = remove_backslash_escapes(q+8);
1469 /* get the implicit parameter */
1470 if((q = srchstr(tail, "/impl=1")) != NULL)
1471 info->impl = 1;
1473 /* get the rhs parameter */
1474 if((q = srchstr(tail, "/rhs=1")) != NULL)
1475 info->rhs = 1;
1477 /* get the ref parameter */
1478 if((q = srchstr(tail, "/ref=1")) != NULL)
1479 info->ref = 1;
1481 /* get the nosub parameter */
1482 if((q = srchstr(tail, "/nosub=1")) != NULL)
1483 info->nosub = 1;
1485 /* get the tls parameter */
1486 if((q = srchstr(tail, "/tls=1")) != NULL)
1487 info->tls = 1;
1489 /* get the tlsmust parameter */
1490 if((q = srchstr(tail, "/tlsm=1")) != NULL)
1491 info->tlsmust = 1;
1493 /* get the ldaps parameter */
1494 if((q = srchstr(tail, "/ldaps=1")) != NULL)
1495 info->ldaps = 1;
1497 /* get the search type value */
1498 if((q = srchstr(tail, "/type=")) != NULL){
1499 NAMEVAL_S *v;
1501 q += 6;
1502 if((p = strindex(q, '/')) != NULL)
1503 *p = '\0';
1505 for(i = 0; (v = ldap_search_types(i)); i++)
1506 if(!strucmp(q, v->name)){
1507 info->type = v->value;
1508 break;
1511 if(p)
1512 *p = '/';
1515 /* get the search rule value */
1516 if((q = srchstr(tail, "/srch=")) != NULL){
1517 NAMEVAL_S *v;
1519 q += 6;
1520 if((p = strindex(q, '/')) != NULL)
1521 *p = '\0';
1523 for(i = 0; (v = ldap_search_rules(i)); i++)
1524 if(!strucmp(q, v->name)){
1525 info->srch = v->value;
1526 break;
1529 if(p)
1530 *p = '/';
1533 /* get the scope */
1534 if((q = srchstr(tail, "/scope=")) != NULL){
1535 NAMEVAL_S *v;
1537 q += 7;
1538 if((p = strindex(q, '/')) != NULL)
1539 *p = '\0';
1541 for(i = 0; (v = ldap_search_scope(i)); i++)
1542 if(!strucmp(q, v->name)){
1543 info->scope = v->value;
1544 break;
1547 if(p)
1548 *p = '/';
1551 /* get the time limit */
1552 if((q = srchstr(tail, "/time=")) != NULL){
1553 q += 6;
1554 if((p = strindex(q, '/')) != NULL)
1555 *p = '\0';
1557 /* This one's a number */
1558 if(*q){
1559 char *err;
1561 err = strtoval(q, &i, 0, 500, 0, tmp_20k_buf, SIZEOF_20KBUF, "ldap timelimit");
1562 if(err){
1563 dprint((1, "%s\n", err ? err : "?"));
1565 else
1566 info->time = i;
1569 if(p)
1570 *p = '/';
1573 /* get the size limit */
1574 if((q = srchstr(tail, "/size=")) != NULL){
1575 q += 6;
1576 if((p = strindex(q, '/')) != NULL)
1577 *p = '\0';
1579 /* This one's a number */
1580 if(*q){
1581 char *err;
1583 err = strtoval(q, &i, 0, 500, 0, tmp_20k_buf, SIZEOF_20KBUF, "ldap sizelimit");
1584 if(err){
1585 dprint((1, "%s\n", err ? err : "?"));
1587 else
1588 info->size = i;
1591 if(p)
1592 *p = '/';
1595 /* get the custom search filter */
1596 if((q = srchstr(tail, "/cust=")) != NULL)
1597 info->cust = remove_backslash_escapes(q+6);
1599 /* get the nickname */
1600 if((q = srchstr(tail, "/nick=")) != NULL)
1601 info->nick = remove_backslash_escapes(q+6);
1603 /* get the mail attribute name */
1604 if((q = srchstr(tail, "/matr=")) != NULL)
1605 info->mailattr = remove_backslash_escapes(q+6);
1607 /* get the sn attribute name */
1608 if((q = srchstr(tail, "/satr=")) != NULL)
1609 info->snattr = remove_backslash_escapes(q+6);
1611 /* get the gn attribute name */
1612 if((q = srchstr(tail, "/gatr=")) != NULL)
1613 info->gnattr = remove_backslash_escapes(q+6);
1615 /* get the cn attribute name */
1616 if((q = srchstr(tail, "/catr=")) != NULL)
1617 info->cnattr = remove_backslash_escapes(q+6);
1619 /* get the backup mail address */
1620 if((q = srchstr(tail, "/mail=")) != NULL)
1621 info->mail = remove_backslash_escapes(q+6);
1624 return(info);
1628 void
1629 free_ldap_server_info(LDAP_SERV_S **info)
1631 if(info && *info){
1632 if((*info)->serv)
1633 fs_give((void **)&(*info)->serv);
1635 if((*info)->base)
1636 fs_give((void **)&(*info)->base);
1638 if((*info)->cust)
1639 fs_give((void **)&(*info)->cust);
1641 if((*info)->binddn)
1642 fs_give((void **)&(*info)->binddn);
1644 if((*info)->nick)
1645 fs_give((void **)&(*info)->nick);
1647 if((*info)->mail)
1648 fs_give((void **)&(*info)->mail);
1650 if((*info)->mailattr)
1651 fs_give((void **)&(*info)->mailattr);
1653 if((*info)->snattr)
1654 fs_give((void **)&(*info)->snattr);
1656 if((*info)->gnattr)
1657 fs_give((void **)&(*info)->gnattr);
1659 if((*info)->cnattr)
1660 fs_give((void **)&(*info)->cnattr);
1662 fs_give((void **)info);
1663 *info = NULL;
1668 LDAP_SERV_S *
1669 copy_ldap_serv_info(LDAP_SERV_S *src)
1671 LDAP_SERV_S *info = NULL;
1673 if(src){
1674 info = (LDAP_SERV_S *) fs_get(sizeof(*info));
1677 * Initialize to defaults.
1679 memset((void *)info, 0, sizeof(*info));
1681 info->serv = src->serv ? cpystr(src->serv) : NULL;
1682 info->base = src->base ? cpystr(src->base) : NULL;
1683 info->cust = src->cust ? cpystr(src->cust) : NULL;
1684 info->binddn = src->binddn ? cpystr(src->binddn) : NULL;
1685 info->nick = src->nick ? cpystr(src->nick) : NULL;
1686 info->mail = src->mail ? cpystr(src->mail) : NULL;
1687 info->mailattr = cpystr((src->mailattr && src->mailattr[0])
1688 ? src->mailattr : DEF_LDAP_MAILATTR);
1689 info->snattr = cpystr((src->snattr && src->snattr[0])
1690 ? src->snattr : DEF_LDAP_SNATTR);
1691 info->gnattr = cpystr((src->gnattr && src->gnattr[0])
1692 ? src->gnattr : DEF_LDAP_GNATTR);
1693 info->cnattr = cpystr((src->cnattr && src->cnattr[0])
1694 ? src->cnattr : DEF_LDAP_CNATTR);
1696 info->port = (src->port < 0) ? LDAP_PORT : src->port;
1697 info->time = (src->time < 0) ? DEF_LDAP_TIME : src->time;
1698 info->size = (src->size < 0) ? DEF_LDAP_SIZE : src->size;
1699 info->type = (src->type < 0) ? DEF_LDAP_TYPE : src->type;
1700 info->srch = (src->srch < 0) ? DEF_LDAP_SRCH : src->srch;
1701 info->scope = (src->scope < 0) ? DEF_LDAP_SCOPE : src->scope;
1702 info->impl = src->impl;
1703 info->rhs = src->rhs;
1704 info->ref = src->ref;
1705 info->nosub = src->nosub;
1706 info->tls = src->tls;
1709 return(info);
1713 void
1714 free_ldap_result_list(LDAP_SERV_RES_S **r)
1716 if(r && *r){
1717 free_ldap_result_list(&(*r)->next);
1718 if((*r)->res)
1719 ldap_msgfree((*r)->res);
1720 if((*r)->ld)
1721 #ifdef _WINDOWS
1722 ldap_unbind((*r)->ld);
1723 #else
1724 ldap_unbind_ext((*r)->ld, NULL, NULL);
1725 #endif
1726 if((*r)->info_used)
1727 free_ldap_server_info(&(*r)->info_used);
1728 if((*r)->serv)
1729 fs_give((void **) &(*r)->serv);
1731 fs_give((void **) r);
1737 * Mask API differences.
1739 void
1740 our_ldap_memfree(void *a)
1742 #if (LDAPAPI >= 15)
1743 if(a)
1744 ldap_memfree(a);
1745 #endif
1750 * Mask API differences.
1752 void
1753 our_ldap_dn_memfree(void *a)
1755 #if defined(_WINDOWS)
1756 if(a)
1757 ldap_memfree(a);
1758 #else
1759 #if (LDAPAPI >= 15)
1760 if(a)
1761 ldap_memfree(a);
1762 #else
1763 if(a)
1764 free(a);
1765 #endif
1766 #endif
1771 * More API masking.
1774 our_ldap_get_lderrno(LDAP *ld, char **m, char **s)
1776 int ret = 0;
1778 #if (LDAPAPI >= 2000)
1779 if(ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&ret) == 0){
1780 if(s)
1781 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, (void *)s);
1783 #elif (LDAPAPI >= 15)
1784 ret = ldap_get_lderrno(ld, m, s);
1785 #else
1786 ret = ld->ld_errno;
1787 if(s)
1788 *s = ld->ld_error;
1789 #endif
1791 return(ret);
1796 * More API masking.
1799 our_ldap_set_lderrno(LDAP *ld, int e, char *m, char *s)
1801 int ret;
1803 #if (LDAPAPI >= 2000)
1804 if(ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&e) == 0)
1805 ret = LDAP_SUCCESS;
1806 else
1807 (void)ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&ret);
1808 #elif (LDAPAPI >= 15)
1809 ret = ldap_set_lderrno(ld, e, m, s);
1810 #else
1811 /* this is all we care about */
1812 ld->ld_errno = e;
1813 ret = LDAP_SUCCESS;
1814 #endif
1816 return(ret);
1821 * More API masking.
1824 our_ldap_set_option(LDAP *ld, int option, void *optdata)
1826 int ret;
1828 #if (LDAPAPI >= 15)
1829 ret = ldap_set_option(ld, option, optdata);
1830 #else
1831 switch(option){
1832 case LDAP_OPT_TIMELIMIT:
1833 ld->ld_timelimit = *(int *)optdata;
1834 break;
1836 case LDAP_OPT_SIZELIMIT:
1837 ld->ld_sizelimit = *(int *)optdata;
1838 break;
1840 case LDAP_OPT_RESTART:
1841 if((int)optdata)
1842 ld->ld_options |= LDAP_OPT_RESTART;
1843 else
1844 ld->ld_options &= ~LDAP_OPT_RESTART;
1846 break;
1849 * Does nothing here. There is only one protocol version supported.
1851 case LDAP_OPT_PROTOCOL_VERSION:
1852 ret = -1;
1853 break;
1855 default:
1856 alpine_panic("LDAP function not implemented");
1858 #endif
1860 return(ret);
1865 * Returns 1 if we can use LDAP version 3 protocol.
1868 ldap_v3_is_supported(LDAP *ld)
1870 return(1);
1873 struct tl_table {
1874 char *ldap_ese;
1875 char *translated;
1878 static struct tl_table ldap_trans_table[]={
1880 * TRANSLATORS: This is a list of LDAP attributes with translations to present
1881 * to the user. For example the attribute mail is Email Address and the attribute
1882 * cn is Name.
1884 {"mail", N_("Email Address")},
1885 #define LDAP_MAIL_ATTR 0
1886 {"sn", N_("Surname")},
1887 #define LDAP_SN_ATTR 1
1888 {"givenName", N_("Given Name")},
1889 #define LDAP_GN_ATTR 2
1890 {"cn", N_("Name")},
1891 #define LDAP_CN_ATTR 3
1892 {"electronicmail", N_("Email Address")},
1893 #define LDAP_EMAIL_ATTR 4
1894 {"o", N_("Organization")},
1895 {"ou", N_("Unit")},
1896 {"c", N_("Country")},
1897 {"st", N_("State or Province")},
1898 {"l", N_("Locality")},
1899 {"objectClass", N_("Object Class")},
1900 {"title", N_("Title")},
1901 {"departmentNumber", N_("Department")},
1902 {"postalAddress", N_("Postal Address")},
1903 {"homePostalAddress", N_("Home Address")},
1904 {"mailStop", N_("Mail Stop")},
1905 {"telephoneNumber", N_("Voice Telephone")},
1906 {"homePhone", N_("Home Telephone")},
1907 {"officePhone", N_("Office Telephone")},
1908 {"facsimileTelephoneNumber", N_("FAX Telephone")},
1909 {"mobile", N_("Mobile Telephone")},
1910 {"pager", N_("Pager")},
1911 {"roomNumber", N_("Room Number")},
1912 {"uid", N_("User ID")},
1913 {NULL, NULL}
1916 char *
1917 ldap_translate(char *a, LDAP_SERV_S *info_used)
1919 int i;
1921 if(info_used){
1922 if(info_used->mailattr && strucmp(info_used->mailattr, a) == 0)
1923 return(_(ldap_trans_table[LDAP_MAIL_ATTR].translated));
1924 else if(info_used->snattr && strucmp(info_used->snattr, a) == 0)
1925 return(_(ldap_trans_table[LDAP_SN_ATTR].translated));
1926 else if(info_used->gnattr && strucmp(info_used->gnattr, a) == 0)
1927 return(_(ldap_trans_table[LDAP_GN_ATTR].translated));
1928 else if(info_used->cnattr && strucmp(info_used->cnattr, a) == 0)
1929 return(_(ldap_trans_table[LDAP_CN_ATTR].translated));
1932 for(i = 0; ldap_trans_table[i].ldap_ese; i++){
1933 if(info_used)
1934 switch(i){
1935 case LDAP_MAIL_ATTR:
1936 case LDAP_SN_ATTR:
1937 case LDAP_GN_ATTR:
1938 case LDAP_CN_ATTR:
1939 case LDAP_EMAIL_ATTR:
1940 continue;
1943 if(strucmp(ldap_trans_table[i].ldap_ese, a) == 0)
1944 return(_(ldap_trans_table[i].translated));
1947 return(a);
1950 char **
1951 berval_to_array(struct berval **v)
1953 char **rv = NULL;
1954 int i, len;
1956 if(v == NULL) return rv;
1957 len = ldap_count_values_len(v);
1959 rv = fs_get((len+1)*sizeof(char *));
1960 for(i = 0; i < len; i++)
1961 if(ALPINE_LDAP_can_use_num(v, i))
1962 rv[i] = cpystr(v[i]->bv_val);
1963 else
1964 rv[i] = NULL;
1965 rv[len] = NULL;
1967 return rv;
1970 #endif /* ENABLE_LDAP */