* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / ldap.c
blobcee3f9f16b6ee6cfb0eaff3aced1342aabb40ff6
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/ldap.h"
17 #include "../pith/state.h"
18 #include "../pith/conf.h"
19 #include "../pith/status.h"
20 #include "../pith/util.h"
21 #include "../pith/imap.h"
22 #include "../pith/busy.h"
23 #include "../pith/signal.h"
24 #include "../pith/ablookup.h"
25 #include "../pith/readfile.h"
28 * Until we can think of a better way to do this. If user exits from an
29 * ldap address selection screen we want to return -1 so that call_builder
30 * will stay on the same line. Might want to use another return value
31 * for the builder so that call_builder more fully understands what we're
32 * up to.
34 int wp_exit;
35 int wp_nobail;
38 #ifdef ENABLE_LDAP
40 * Hook to allow user input on whether or not to save chosen LDAP result
42 void (*pith_opt_save_ldap_entry)(struct pine *, LDAP_CHOOSE_S *, int);
46 * Internal prototypes
48 LDAP_SERV_RES_S *ldap_lookup(LDAP_SERV_S *, char *, CUSTOM_FILT_S *, WP_ERR_S *, int);
49 LDAP_SERV_S *copy_ldap_serv_info(LDAP_SERV_S *);
50 int our_ldap_get_lderrno(LDAP *, char **, char **);
51 int our_ldap_set_lderrno(LDAP *, int, char *, char *);
52 #endif /* ENABLE_LDAP */
56 * This function does white pages lookups.
58 * Args string -- the string to use in the lookup
60 * Returns NULL -- lookup failed
61 * Address -- A single address is returned if lookup was successful.
63 ADDRESS *
64 wp_lookups(char *string, WP_ERR_S *wp_err, int recursing)
66 ADDRESS *ret_a = NULL;
67 char ebuf[200];
68 #ifdef ENABLE_LDAP
69 LDAP_SERV_RES_S *free_when_done = NULL;
70 LDAPLookupStyle style;
71 LDAP_CHOOSE_S *winning_e = NULL;
72 LDAP_SERV_S *info = NULL;
73 static char *fakedomain = "@";
74 char *tmp_a_string;
75 int auwe_rv = 0;
78 * Runtime ldap lookup of addrbook entry.
80 if(!strncmp(string, RUN_LDAP, LEN_RL)){
81 LDAP_SERV_RES_S *head_of_result_list;
83 info = break_up_ldap_server(string+LEN_RL);
84 head_of_result_list = ldap_lookup(info, "", NULL, wp_err, 1);
86 if(head_of_result_list){
87 if(!wp_exit)
88 auwe_rv = ask_user_which_entry(head_of_result_list, string,
89 &winning_e,
90 wp_err,
91 wp_err->wp_err_occurred
92 ? DisplayIfOne : DisplayIfTwo);
94 if(auwe_rv != -5)
95 free_when_done = head_of_result_list;
97 else{
98 wp_err->wp_err_occurred = 1;
99 if(wp_err->error){
100 q_status_message(SM_ORDER, 3, 5, wp_err->error);
101 display_message('x');
102 fs_give((void **)&wp_err->error);
105 /* try using backup email address */
106 if(info && info->mail && *info->mail){
107 tmp_a_string = cpystr(info->mail);
108 rfc822_parse_adrlist(&ret_a, tmp_a_string, fakedomain);
109 fs_give((void **)&tmp_a_string);
111 wp_err->error =
112 cpystr(_("Directory lookup failed, using backup email address"));
114 else{
116 * Do this so the awful LDAP: ... string won't show up
117 * in the composer. This shouldn't actually happen in
118 * real life, so we're not too concerned about it. If we
119 * were we'd want to recover the nickname we started with
120 * somehow, or something like that.
122 ret_a = mail_newaddr();
123 ret_a->mailbox = cpystr("missing-username");
124 wp_err->error = cpystr(_("Directory lookup failed, no backup email address available"));
127 q_status_message(SM_ORDER, 3, 5, wp_err->error);
128 display_message('x');
131 else{
132 style = F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
133 ? DisplayIfOne : DisplayIfTwo;
134 auwe_rv = ldap_lookup_all(string, as.n_serv, recursing, style, NULL,
135 &winning_e, wp_err, &free_when_done);
138 if(winning_e && auwe_rv != -5){
139 ret_a = address_from_ldap(winning_e);
141 if(pith_opt_save_ldap_entry && ret_a && F_ON(F_ADD_LDAP_TO_ABOOK, ps_global) && !info)
142 (*pith_opt_save_ldap_entry)(ps_global, winning_e, 1);
145 fs_give((void **)&winning_e);
148 /* Info's only set in the RUN_LDAP case */
149 if(info){
150 if(ret_a && ret_a->host){
151 ADDRESS *backup = NULL;
153 if(info->mail && *info->mail)
154 rfc822_parse_adrlist(&backup, info->mail, fakedomain);
156 if(!backup || !address_is_same(ret_a, backup)){
157 if(wp_err->error){
158 q_status_message(SM_ORDER, 3, 5, wp_err->error);
159 display_message('x');
160 fs_give((void **)&wp_err->error);
163 snprintf(ebuf, sizeof(ebuf),
164 _("Warning: current address different from saved address (%s)"),
165 info->mail);
166 wp_err->error = cpystr(ebuf);
167 q_status_message(SM_ORDER, 3, 5, wp_err->error);
168 display_message('x');
171 if(backup)
172 mail_free_address(&backup);
175 free_ldap_server_info(&info);
178 if(free_when_done)
179 free_ldap_result_list(&free_when_done);
180 #endif /* ENABLE_LDAP */
182 if(ret_a){
183 if(ret_a->mailbox){ /* indicates there was a MAIL attribute */
184 if(!ret_a->host || !ret_a->host[0]){
185 if(ret_a->host)
186 fs_give((void **)&ret_a->host);
188 ret_a->host = cpystr("missing-hostname");
189 wp_err->wp_err_occurred = 1;
190 if(wp_err->error)
191 fs_give((void **)&wp_err->error);
193 wp_err->error = cpystr(_("Missing hostname in LDAP address"));
194 q_status_message(SM_ORDER, 3, 5, wp_err->error);
195 display_message('x');
198 if(!ret_a->mailbox[0]){
199 if(ret_a->mailbox)
200 fs_give((void **)&ret_a->mailbox);
202 ret_a->mailbox = cpystr("missing-username");
203 wp_err->wp_err_occurred = 1;
204 if(wp_err->error)
205 fs_give((void **)&wp_err->error);
207 wp_err->error = cpystr(_("Missing username in LDAP address"));
208 q_status_message(SM_ORDER, 3, 5, wp_err->error);
209 display_message('x');
212 else{
213 wp_err->wp_err_occurred = 1;
215 if(wp_err->error)
216 fs_give((void **)&wp_err->error);
218 snprintf(ebuf, sizeof(ebuf), _("No email address available for \"%s\""),
219 (ret_a->personal && *ret_a->personal)
220 ? ret_a->personal
221 : "selected entry");
222 wp_err->error = cpystr(ebuf);
223 q_status_message(SM_ORDER, 3, 5, wp_err->error);
224 display_message('x');
225 mail_free_address(&ret_a);
226 ret_a = NULL;
230 return(ret_a);
234 #ifdef ENABLE_LDAP
237 * Goes through all servers looking up string.
239 * Args string -- String to search for
240 * who -- Which servers to look on
241 * style -- Sometimes we want to display no matter what, sometimes
242 * only if more than one entry, sometimes only if email
243 * addresses, ...
245 * The possible styles are:
246 * AlwaysDisplayAndMailRequired: This happens when user ^T's from
247 * composer to address book screen, and
248 * then queries directory server.
249 * AlwaysDisplay: This happens when user is browsing
250 * in address book maintenance screen and
251 * then queries directory server.
252 * DisplayIfOne: These two come from implicit lookups
253 * DisplayIfTwo: from build_address. If the compose rejects
254 * unqualified feature is on we get the
255 * DisplayIfOne, otherwise IfTwo.
257 * cust -- Use this custom filter instead of configured filters
258 * winning_e -- Return value
259 * wp_err -- Error handling
260 * free_when_done -- Caller needs to free this
262 * Returns -- value returned by ask_user_which_entry
265 ldap_lookup_all(char *string, int who, int recursing, LDAPLookupStyle style,
266 CUSTOM_FILT_S *cust, LDAP_CHOOSE_S **winning_e,
267 WP_ERR_S *wp_err, LDAP_SERV_RES_S **free_when_done)
269 int retval = -1;
270 LDAP_SERV_RES_S *head_of_result_list = NULL;
272 wp_exit = wp_nobail = 0;
273 if(free_when_done)
274 *free_when_done = NULL;
276 head_of_result_list = ldap_lookup_all_work(string, who, recursing,
277 cust, wp_err);
279 if(!wp_exit)
280 retval = ask_user_which_entry(head_of_result_list, string, winning_e,
281 wp_err,
282 (wp_err->wp_err_occurred &&
283 style == DisplayIfTwo) ? DisplayIfOne
284 : style);
287 * Because winning_e probably points into the result list
288 * we need to leave the result list alone and have the caller
289 * free it after they are done with winning_e.
291 if(retval != -5 && free_when_done)
292 *free_when_done = head_of_result_list;
294 return(retval);
299 * Goes through all servers looking up string.
301 * Args string -- String to search for
302 * who -- Which servers to look on
303 * cust -- Use this custom filter instead of configured filters
304 * wp_err -- Error handling
306 * Returns -- list of results that needs to be freed by caller
308 LDAP_SERV_RES_S *
309 ldap_lookup_all_work(char *string, int who, int recursing,
310 CUSTOM_FILT_S *cust, WP_ERR_S *wp_err)
312 int i;
313 LDAP_SERV_RES_S *serv_res;
314 LDAP_SERV_RES_S *rr, *head_of_result_list = NULL;
316 /* If there is at least one server */
317 if(ps_global->VAR_LDAP_SERVERS && ps_global->VAR_LDAP_SERVERS[0] &&
318 ps_global->VAR_LDAP_SERVERS[0][0]){
319 int how_many_servers;
321 for(i = 0; ps_global->VAR_LDAP_SERVERS[i] &&
322 ps_global->VAR_LDAP_SERVERS[i][0]; i++)
325 how_many_servers = i;
327 /* For each server in list */
328 for(i = 0; !wp_exit && ps_global->VAR_LDAP_SERVERS[i] &&
329 ps_global->VAR_LDAP_SERVERS[i][0]; i++){
330 LDAP_SERV_S *info;
332 dprint((6, "ldap_lookup_all_work: lookup on server (%.256s)\n",
333 ps_global->VAR_LDAP_SERVERS[i]));
334 info = NULL;
335 if(who == -1 || who == i || who == as.n_serv)
336 info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]);
339 * Who tells us which servers to look on.
340 * Who == -1 means all servers.
341 * Who == 0 means server[0].
342 * Who == 1 means server[1].
343 * Who == as.n_serv means query on those with impl set.
345 if(!(who == -1 || who == i ||
346 (who == as.n_serv && !recursing && info && info->impl) ||
347 (who == as.n_serv && recursing && info && info->rhs))){
349 if(info)
350 free_ldap_server_info(&info);
352 continue;
355 dprint((6, "ldap_lookup_all_work: ldap_lookup (server: %.20s...)(string: %s)\n",
356 ps_global->VAR_LDAP_SERVERS[i], string));
357 serv_res = ldap_lookup(info, string, cust,
358 wp_err, how_many_servers > 1);
359 if(serv_res){
360 /* Add new one to end of list so they come in the right order */
361 for(rr = head_of_result_list; rr && rr->next; rr = rr->next)
364 if(rr)
365 rr->next = serv_res;
366 else
367 head_of_result_list = serv_res;
370 if(info)
371 free_ldap_server_info(&info);
375 return(head_of_result_list);
380 * Do an LDAP lookup to the server described in the info argument.
382 * Args info -- LDAP info for server.
383 * string -- String to lookup.
384 * cust -- Possible custom filter description.
385 * wp_err -- We set this is we get a white pages error.
386 * name_in_error -- Caller sets this if they want us to include the server
387 * name in error messages.
389 * Returns Results of lookup, NULL if lookup failed.
391 LDAP_SERV_RES_S *
392 ldap_lookup(LDAP_SERV_S *info, char *string, CUSTOM_FILT_S *cust,
393 WP_ERR_S *wp_err, int name_in_error)
395 char ebuf[900];
396 char buf[900];
397 char *serv, *base, *serv_errstr, *s, *t;
398 char *mailattr, *snattr, *gnattr, *cnattr;
399 int we_cancel = 0, we_turned_on = 0;
400 LDAP_SERV_RES_S *serv_res = NULL;
401 LDAP *ld = NULL;
402 long pwdtrial = 0L;
403 int ld_errnum;
404 char *ld_errstr;
407 if(!info)
408 return(serv_res);
410 serv = cpystr((info->serv && *info->serv) ? info->serv : "?");
412 if(name_in_error)
413 snprintf(ebuf, sizeof(ebuf), " (%s)",
414 (info->nick && *info->nick) ? info->nick : serv);
415 else
416 ebuf[0] = '\0';
418 serv_errstr = cpystr(ebuf);
419 base = cpystr(info->base ? info->base : "");
421 if(info->port < 0)
422 info->port = LDAP_PORT;
424 if(info->type < 0)
425 info->type = DEF_LDAP_TYPE;
427 if(info->srch < 0)
428 info->srch = DEF_LDAP_SRCH;
430 if(info->time < 0)
431 info->time = DEF_LDAP_TIME;
433 if(info->size < 0)
434 info->size = DEF_LDAP_SIZE;
436 if(info->scope < 0)
437 info->scope = DEF_LDAP_SCOPE;
439 mailattr = (info->mailattr && info->mailattr[0]) ? info->mailattr
440 : DEF_LDAP_MAILATTR;
441 snattr = (info->snattr && info->snattr[0]) ? info->snattr
442 : DEF_LDAP_SNATTR;
443 gnattr = (info->gnattr && info->gnattr[0]) ? info->gnattr
444 : DEF_LDAP_GNATTR;
445 cnattr = (info->cnattr && info->cnattr[0]) ? info->cnattr
446 : DEF_LDAP_CNATTR;
449 * We may want to keep ldap handles open, but at least for
450 * now, re-open them every time.
453 dprint((3, "ldap_lookup(%s,%d)\n", serv ? serv : "?", info->port));
455 snprintf(ebuf, sizeof(ebuf), "Searching%s%s%s on %s",
456 (string && *string) ? " for \"" : "",
457 (string && *string) ? string : "",
458 (string && *string) ? "\"" : "",
459 serv);
460 we_turned_on = intr_handling_on(); /* this erases keymenu */
461 we_cancel = busy_cue(ebuf, NULL, 0);
462 if(wp_err->mangled)
463 *(wp_err->mangled) = 1;
465 #ifdef _SOLARIS_SDK
466 if(info->tls || info->tlsmust)
467 ldapssl_client_init(NULL, NULL);
468 if((ld = ldap_init(serv, info->port)) == NULL)
469 #else
470 #if (LDAPAPI >= 11)
471 #ifdef _WINDOWS
472 if((ld = ldap_init(serv, info->port)) == NULL)
473 #else
474 #ifdef SMIME_SSLCERTS
475 /* If we are attempting a ldaps secure connection, we need to tell
476 * ldap that we have certificates. There are many ways to do so.
477 * OpenLDAP has many ways to configure this through configuration
478 * files. For example the global (to the system) ldap.conf file, or the
479 * personal ldaprc or .ldaprc files. Setting the location of the
480 * certificates must happen at the time we call ldap_initialize, we
481 * cannot set it up before that call, nor after, so what we are going
482 * to do is to test for a .ldaprc file in the home directory. If such
483 * file exists we read it, if not we create it and if it does not have
484 * a line for the location of the certificates in the system, we add one.
485 * (so we ignore all other configuration files)
487 if(info->ldaps && ps_global->home_dir){
488 int done = 0;
489 char *text, *tls_conf;
490 char filename[MAXPATH+1];
492 build_path(filename, ps_global->home_dir, ".ldaprc", sizeof(filename));
494 if((text = read_file(filename, 0)) != NULL)
495 while(done == 0
496 && (tls_conf = strstr(text, "TLS_CACERTDIR")) != NULL
497 && (tls_conf == text || *(tls_conf - 1) == '\n')){
498 tls_conf += 13; /* 13 = strlen("TLS_CACERTDIR") */
499 while (isspace(*tls_conf))
500 tls_conf++;
501 if(!strncmp(tls_conf, SMIME_SSLCERTS, strlen(SMIME_SSLCERTS)))
502 done++;
505 if(!done){
506 STORE_S *so;
508 if((so = so_get(FileStar, filename, WRITE_ACCESS)) != NULL){
509 if(text != NULL){
510 so_puts(so, text);
511 so_puts(so, NEWLINE);
513 so_puts(so, "TLS_CACERTDIR");
514 so_puts(so, " ");
515 so_puts(so, SMIME_SSLCERTS);
516 so_puts(so, NEWLINE);
517 so_give(&so);
520 if(text != NULL)
521 fs_give((void **)&text);
523 #endif /* SMIME_SSLCERTS */
525 tmp_20k_buf[0] = '\0';
526 s = serv;
527 do {
528 if ((t = strchr(s, ' ')) != NULL) *t = '\0';
529 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
530 SIZEOF_20KBUF - strlen(tmp_20k_buf), "%s://%s",
531 info->ldaps ? "ldaps" : "ldap", s);
532 if (strchr(s, ':') == NULL){
533 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
534 SIZEOF_20KBUF - strlen(tmp_20k_buf),
535 "%s%d", ":", info->port);
537 if(t != NULL){
538 *t = ' ';
539 for( ; *t == ' '; t++);
540 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
541 SIZEOF_20KBUF - strlen(tmp_20k_buf), "%s", " ");
543 s = t;
544 } while (s != NULL);
546 tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
548 if(ldap_initialize(&ld, tmp_20k_buf) != LDAP_SUCCESS)
549 #endif
550 #else
551 if((ld = ldap_open(serv, info->port)) == NULL)
552 #endif
553 #endif
555 /* TRANSLATORS: All of the three args together are an error message */
556 snprintf(ebuf, sizeof(ebuf), _("Access to LDAP server failed: %s%s(%s)"),
557 errno ? error_description(errno) : "",
558 errno ? " " : "",
559 serv);
560 wp_err->wp_err_occurred = 1;
561 if(wp_err->error)
562 fs_give((void **)&wp_err->error);
564 wp_err->error = cpystr(ebuf);
565 if(we_cancel)
566 cancel_busy_cue(-1);
568 q_status_message(SM_ORDER, 3, 5, wp_err->error);
569 display_message('x');
570 dprint((2, "%s\n", ebuf));
572 else if(!ps_global->intr_pending){
573 int proto = 3, tlsmustbail = 0;
574 char *pwd = NULL, user[NETMAXUSER];
575 #ifdef _WINDOWS
576 char *passwd = NULL;
577 #else
578 struct berval passwd = { 0 };
579 #endif
580 char hostbuf[1024];
581 NETMBX mb;
582 #ifndef _WINDOWS
583 int rc;
584 #endif
586 memset(&mb, 0, sizeof(mb));
588 #ifdef _SOLARIS_SDK
589 if(info->tls || info->tlsmust)
590 rc = ldapssl_install_routines(ld);
591 #endif
593 if(ldap_v3_is_supported(ld) &&
594 our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
595 dprint((5, "ldap: using version 3 protocol\n"));
599 * If we don't set RESTART then the select() waiting for the answer
600 * in libldap will be interrupted and stopped by our busy_cue.
602 our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
605 * If we need to authenticate, get the password
607 if(info->binddn && info->binddn[0]){
608 char pmt[500];
609 char *space;
611 snprintf(hostbuf, sizeof(hostbuf), "{%s}dummy", info->serv ? info->serv : "?");
614 * We don't handle multiple space-delimited hosts well.
615 * We don't know which we're asking for a password for.
616 * We're not connected yet so we can't know.
618 if((space=strindex(hostbuf, ' ')) != NULL)
619 *space = '\0';
621 mail_valid_net_parse_work(hostbuf, &mb, info->ldaps ? "ldaps" : "ldap");
622 mb.port = info->port;
623 mb.tlsflag = (info->tls || info->tlsmust) ? 1 : 0;
624 mb.sslflag = info->ldaps ? 1 : 0;
626 try_password_again:
628 if(mb.tlsflag
629 && (pwdtrial > 0 ||
630 #ifndef _WINDOWS
631 #ifdef _SOLARIS_SDK
632 (rc == LDAP_SUCCESS)
633 #else /* !_SOLARIS_SDK */
634 ((rc=ldap_start_tls_s(ld, NULL, NULL)) == LDAP_SUCCESS)
635 #endif /* !_SOLARIS_SDK */
636 #else /* _WINDOWS */
637 0 /* TODO: find a way to do this in Windows */
638 #endif /* _WINDOWS */
640 mb.tlsflag = 1;
641 else
642 mb.tlsflag = 0;
644 if((info->tls || info->tlsmust) && !mb.tlsflag){
645 q_status_message(SM_ORDER, 3, 5, "Not able to start TLS encryption for LDAP server");
646 if(info->tlsmust)
647 tlsmustbail++;
650 if(!tlsmustbail){
651 snprintf(pmt, sizeof(pmt), " %s", (info->nick && *info->nick) ? info->nick : serv);
652 mm_login_work(&mb, user, &pwd, pwdtrial, pmt, info->binddn);
653 if(pwd && pwd[0]){
654 #ifdef _WINDOWS
655 passwd = pwd;
656 #else
657 passwd.bv_len = strlen(pwd);
658 passwd.bv_val = pwd;
659 #endif
666 * LDAPv2 requires the bind. v3 doesn't require it but we want
667 * to tell the server we're v3 if the server supports v3, and if the
668 * server doesn't support v3 the bind is required.
670 if(tlsmustbail
671 #ifdef _WINDOWS
672 || ldap_simple_bind_s(ld, info->binddn, passwd) != LDAP_SUCCESS){
673 #else
674 || ldap_sasl_bind_s(ld, info->binddn, LDAP_SASL_SIMPLE, &passwd, NULL, NULL, NULL) != LDAP_SUCCESS){
675 #endif
676 wp_err->wp_err_occurred = 1;
678 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
680 if(!tlsmustbail && info->binddn && info->binddn[0] && pwdtrial < 2L
681 && ld_errnum == LDAP_INVALID_CREDENTIALS){
682 pwdtrial++;
683 q_status_message(SM_ORDER, 3, 5, _("Invalid password"));
684 goto try_password_again;
687 snprintf(ebuf, sizeof(ebuf), _("LDAP server failed: %s%s%s%s"),
688 ldap_err2string(ld_errnum),
689 serv_errstr,
690 (ld_errstr && *ld_errstr) ? ": " : "",
691 (ld_errstr && *ld_errstr) ? ld_errstr : "");
693 if(wp_err->error)
694 fs_give((void **)&wp_err->error);
696 if(we_cancel)
697 cancel_busy_cue(-1);
698 #ifdef _WINDOWS
699 ldap_unbind(ld);
700 #else
701 ldap_unbind_ext(ld, NULL, NULL);
702 #endif
703 wp_err->error = cpystr(ebuf);
704 q_status_message(SM_ORDER, 3, 5, wp_err->error);
705 display_message('x');
706 dprint((2, "%s\n", ebuf));
708 else if(!ps_global->intr_pending){
709 int srch_res = LDAP_SUCCESS, args, slen, flen;
710 #define TEMPLATELEN 512
711 char filt_template[TEMPLATELEN + 1];
712 char filt_format[2*TEMPLATELEN + 1];
713 char filter[2*TEMPLATELEN + 1];
714 char scp[2*TEMPLATELEN + 1];
715 char *p, *q;
716 LDAPMessage *res = NULL;
717 int intr_happened = 0;
718 int tl;
720 tl = (info->time == 0) ? info->time : info->time + 10;
722 our_ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &tl);
723 our_ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &info->size);
726 * If a custom filter has been passed in and it doesn't include a
727 * request to combine it with the configured filter, then replace
728 * any configured filter with the passed in filter.
730 if(cust && cust->filt && !cust->combine){
731 if(info->cust)
732 fs_give((void **)&info->cust);
734 info->cust = cpystr(cust->filt);
737 if(info->cust && *info->cust){ /* use custom filter if present */
738 strncpy(filt_template, info->cust, sizeof(filt_template));
739 filt_template[sizeof(filt_template)-1] = '\0';
741 else{ /* else use configured filter */
742 switch(info->type){
743 case LDAP_TYPE_SUR:
744 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", snattr);
745 break;
746 case LDAP_TYPE_GIVEN:
747 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", gnattr);
748 break;
749 case LDAP_TYPE_EMAIL:
750 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", mailattr);
751 break;
752 case LDAP_TYPE_CN_EMAIL:
753 snprintf(filt_template, sizeof(filt_template), "(|(%s=%%s)(%s=%%s))", cnattr,
754 mailattr);
755 break;
756 case LDAP_TYPE_SUR_GIVEN:
757 snprintf(filt_template, sizeof(filt_template), "(|(%s=%%s)(%s=%%s))",
758 snattr, gnattr);
759 break;
760 case LDAP_TYPE_SEVERAL:
761 snprintf(filt_template, sizeof(filt_template),
762 "(|(%s=%%s)(%s=%%s)(%s=%%s)(%s=%%s))",
763 cnattr, mailattr, snattr, gnattr);
764 break;
765 default:
766 case LDAP_TYPE_CN:
767 snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", cnattr);
768 break;
772 /* just copy if custom */
773 if(info->cust && *info->cust)
774 info->srch = LDAP_SRCH_EQUALS;
776 p = filt_template;
777 q = filt_format;
778 memset((void *)filt_format, 0, sizeof(filt_format));
779 args = 0;
780 while(*p && (q - filt_format) + 4 < sizeof(filt_format)){
781 if(*p == '%' && *(p+1) == 's'){
782 args++;
783 switch(info->srch){
784 /* Exact match */
785 case LDAP_SRCH_EQUALS:
786 *q++ = *p++;
787 *q++ = *p++;
788 break;
790 /* Append wildcard after %s */
791 case LDAP_SRCH_BEGINS:
792 *q++ = *p++;
793 *q++ = *p++;
794 *q++ = '*';
795 break;
797 /* Insert wildcard before %s */
798 case LDAP_SRCH_ENDS:
799 *q++ = '*';
800 *q++ = *p++;
801 *q++ = *p++;
802 break;
804 /* Put wildcard before and after %s */
805 default:
806 case LDAP_SRCH_CONTAINS:
807 *q++ = '*';
808 *q++ = *p++;
809 *q++ = *p++;
810 *q++ = '*';
811 break;
814 else
815 *q++ = *p++;
818 if(q - filt_format < sizeof(filt_format))
819 *q = '\0';
821 filt_format[sizeof(filt_format)-1] = '\0';
824 * If combine is lit we put the custom filter and the filt_format
825 * filter and combine them with an &.
827 if(cust && cust->filt && cust->combine){
828 char *combined;
829 size_t l;
831 l = strlen(filt_format) + strlen(cust->filt) + 3;
832 combined = (char *) fs_get((l+1) * sizeof(char));
833 snprintf(combined, l+1, "(&%s%s)", cust->filt, filt_format);
834 strncpy(filt_format, combined, sizeof(filt_format));
835 filt_format[sizeof(filt_format)-1] = '\0';
836 fs_give((void **) &combined);
840 * Ad hoc attempt to make "Steve Hubert" match
841 * Steven Hubert but not Steven Shubert.
842 * We replace a <SPACE> with * <SPACE> (not * <SPACE> *).
844 memset((void *)scp, 0, sizeof(scp));
845 if(info->nosub)
846 strncpy(scp, string, sizeof(scp));
847 else{
848 p = string;
849 q = scp;
850 while(*p && (q - scp) + 1 < sizeof(scp)){
851 if(*p == SPACE && *(p+1) != SPACE){
852 *q++ = '*';
853 *q++ = *p++;
855 else
856 *q++ = *p++;
860 scp[sizeof(scp)-1] = '\0';
862 slen = strlen(scp);
863 flen = strlen(filt_format);
864 /* truncate string if it will overflow filter */
865 if(args*slen + flen - 2*args > sizeof(filter)-1)
866 scp[(sizeof(filter)-1 - flen)/args] = '\0';
869 * Replace %s's with scp.
871 switch(args){
872 case 0:
873 snprintf(filter, sizeof(filter), "%s", filt_format);
874 break;
875 case 1:
876 snprintf(filter, sizeof(filter), filt_format, scp);
877 break;
878 case 2:
879 snprintf(filter, sizeof(filter), filt_format, scp, scp);
880 break;
881 case 3:
882 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp);
883 break;
884 case 4:
885 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp);
886 break;
887 case 5:
888 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp);
889 break;
890 case 6:
891 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp);
892 break;
893 case 7:
894 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp);
895 break;
896 case 8:
897 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
898 scp);
899 break;
900 case 9:
901 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
902 scp, scp);
903 break;
904 case 10:
905 default:
906 snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
907 scp, scp, scp);
908 break;
911 /* replace double *'s with single *'s in filter */
912 for(p = q = filter; *p; p++)
913 if(*p != '*' || p == filter || *(p-1) != '*')
914 *q++ = *p;
916 *q = '\0';
918 (void) removing_double_quotes(base);
919 dprint((5, "about to ldap_search(\"%s\", %s)\n",
920 base ? base : "?", filter ? filter : "?"));
921 if(ps_global->intr_pending)
922 srch_res = LDAP_PROTOCOL_ERROR;
923 else{
924 int msgid;
925 time_t start_time;
926 struct timeval tv = {0}, *tvp = NULL;
928 memset((void *)&tv, 0, sizeof(struct timeval));
929 tv.tv_sec = info->time;
930 tvp = &tv;
932 start_time = time((time_t *)0);
934 dprint((6, "ldap_lookup: calling ldap_search\n"));
935 #ifdef _WINDOWS
936 msgid = ldap_search(ld, base, info->scope, filter, NULL, 0);
937 #else
938 if(ldap_search_ext(ld, base, info->scope, filter, NULL, 0,
939 NULL, NULL, tvp, info->size, &msgid) != LDAP_SUCCESS)
940 msgid = -1;
941 #endif
943 if(msgid == -1)
944 srch_res = our_ldap_get_lderrno(ld, NULL, NULL);
945 else{
946 int lres;
948 * Warning: struct timeval is not portable. However, since it is
949 * part of LDAP api it must be portable to all platforms LDAP
950 * has been ported to.
952 struct timeval t;
954 t.tv_sec = 1; t.tv_usec = 0;
956 do {
957 if(ps_global->intr_pending)
958 intr_happened = 1;
960 dprint((6, "ldap_result(id=%d): ", msgid));
961 if((lres=ldap_result(ld, msgid, LDAP_MSG_ALL, &t, &res)) == -1){
962 /* error */
963 srch_res = our_ldap_get_lderrno(ld, NULL, NULL);
964 dprint((6, "error (-1 returned): ld_errno=%d\n",
965 srch_res));
967 else if(lres == 0){ /* timeout, no results available */
968 if(intr_happened){
969 #ifdef _WINDOWS
970 ldap_abandon(ld, msgid);
971 #else
972 ldap_abandon_ext(ld, msgid, NULL, NULL);
973 #endif
974 srch_res = LDAP_PROTOCOL_ERROR;
975 if(our_ldap_get_lderrno(ld, NULL, NULL) == LDAP_SUCCESS)
976 our_ldap_set_lderrno(ld, LDAP_PROTOCOL_ERROR, NULL, NULL);
978 dprint((6, "timeout, intr: srch_res=%d\n",
979 srch_res));
981 else if(info->time > 0 &&
982 ((long)time((time_t *)0) - start_time) > info->time){
983 /* try for partial results */
984 t.tv_sec = 0; t.tv_usec = 0;
985 lres = ldap_result(ld, msgid, LDAP_MSG_RECEIVED, &t, &res);
986 if(lres > 0 && lres != LDAP_RES_SEARCH_RESULT){
987 srch_res = LDAP_SUCCESS;
988 dprint((6, "partial result: lres=0x%x\n", lres));
990 else{
991 if(lres == 0)
992 #ifdef _WINDOWS
993 ldap_abandon(ld, msgid);
994 #else
995 ldap_abandon_ext(ld, msgid, NULL, NULL);
996 #endif
998 srch_res = LDAP_TIMEOUT;
999 if(our_ldap_get_lderrno(ld, NULL, NULL) == LDAP_SUCCESS)
1000 our_ldap_set_lderrno(ld, LDAP_TIMEOUT, NULL, NULL);
1002 dprint((6,
1003 "timeout, total_time (%d), srch_res=%d\n",
1004 info->time, srch_res));
1007 else{
1008 dprint((6, "timeout\n"));
1011 else{
1012 #ifdef _WINDOWS
1013 srch_res = ldap_result2error(ld, res, 0);
1014 dprint((6, "lres=0x%x, srch_res=%d\n", lres,
1015 srch_res));
1016 #else
1017 int err;
1018 char *dn, *text, **ref;
1019 LDAPControl **srv;
1021 dn = text = NULL; ref = NULL; srv = NULL;
1022 srch_res = ldap_parse_result(ld, res,
1023 &err, &dn, &text, &ref, &srv, 0);
1024 dprint((6, "lres=0x%x, srch_res=%d, dn=%s, text=%s\n", lres,
1025 srch_res, dn ? dn : "", text ? text : ""));
1026 if(dn) ber_memfree(dn);
1027 if(text) ber_memfree(text);
1028 if(ref) ber_memvfree((void **) ref);
1029 if(srv) ldap_controls_free(srv);
1030 #endif
1032 }while(lres == 0 &&
1033 !(intr_happened ||
1034 (info->time > 0 &&
1035 ((long)time((time_t *)0) - start_time) > info->time)));
1039 if(intr_happened){
1040 wp_exit = 1;
1041 if(we_cancel)
1042 cancel_busy_cue(-1);
1044 if(wp_err->error)
1045 fs_give((void **)&wp_err->error);
1046 else{
1047 q_status_message(SM_ORDER, 0, 1, "Interrupt");
1048 display_message('x');
1049 fflush(stdout);
1052 if(res)
1053 ldap_msgfree(res);
1054 if(ld)
1055 #ifdef _WINDOWS
1056 ldap_unbind(ld);
1057 #else
1058 ldap_unbind_ext(ld, NULL, NULL);
1059 #endif
1061 res = NULL; ld = NULL;
1063 else if(srch_res != LDAP_SUCCESS &&
1064 srch_res != LDAP_TIMELIMIT_EXCEEDED &&
1065 srch_res != LDAP_RESULTS_TOO_LARGE &&
1066 srch_res != LDAP_TIMEOUT &&
1067 srch_res != LDAP_SIZELIMIT_EXCEEDED){
1068 wp_err->wp_err_occurred = 1;
1070 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
1072 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s%s%s%s"),
1073 ldap_err2string(ld_errnum),
1074 serv_errstr,
1075 (ld_errstr && *ld_errstr) ? ": " : "",
1076 (ld_errstr && *ld_errstr) ? ld_errstr : "");
1078 if(wp_err->error)
1079 fs_give((void **)&wp_err->error);
1081 wp_err->error = cpystr(ebuf);
1082 if(we_cancel)
1083 cancel_busy_cue(-1);
1085 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1086 display_message('x');
1087 dprint((2, "%s\n", ebuf));
1088 if(res)
1089 ldap_msgfree(res);
1090 if(ld)
1091 #ifdef _WINDOWS
1092 ldap_unbind(ld);
1093 #else
1094 ldap_unbind_ext(ld, NULL, NULL);
1095 #endif
1097 res = NULL; ld = NULL;
1099 else{
1100 int cnt;
1102 cnt = ldap_count_entries(ld, res);
1104 if(cnt > 0){
1106 if(srch_res == LDAP_TIMELIMIT_EXCEEDED ||
1107 srch_res == LDAP_RESULTS_TOO_LARGE ||
1108 srch_res == LDAP_TIMEOUT ||
1109 srch_res == LDAP_SIZELIMIT_EXCEEDED){
1110 wp_err->wp_err_occurred = 1;
1111 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
1113 snprintf(ebuf, sizeof(ebuf), _("LDAP partial results: %s%s%s%s"),
1114 ldap_err2string(ld_errnum),
1115 serv_errstr,
1116 (ld_errstr && *ld_errstr) ? ": " : "",
1117 (ld_errstr && *ld_errstr) ? ld_errstr : "");
1118 dprint((2, "%s\n", ebuf));
1119 if(wp_err->error)
1120 fs_give((void **)&wp_err->error);
1122 wp_err->error = cpystr(ebuf);
1123 if(we_cancel)
1124 cancel_busy_cue(-1);
1126 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1127 display_message('x');
1130 dprint((5, "Matched %d entries on %s\n",
1131 cnt, serv ? serv : "?"));
1133 serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
1134 memset((void *)serv_res, 0, sizeof(*serv_res));
1135 serv_res->ld = ld;
1136 serv_res->res = res;
1137 serv_res->info_used = copy_ldap_serv_info(info);
1138 /* Save by reference? */
1139 if(info->ref){
1140 snprintf(buf, sizeof(buf), "%s:%s", serv, comatose(info->port));
1141 serv_res->serv = cpystr(buf);
1143 else
1144 serv_res->serv = NULL;
1146 serv_res->next = NULL;
1148 else{
1149 if(srch_res == LDAP_TIMELIMIT_EXCEEDED ||
1150 srch_res == LDAP_RESULTS_TOO_LARGE ||
1151 srch_res == LDAP_TIMEOUT ||
1152 srch_res == LDAP_SIZELIMIT_EXCEEDED){
1153 wp_err->wp_err_occurred = 1;
1154 wp_err->ldap_errno = srch_res;
1156 ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
1158 snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s%s%s%s"),
1159 ldap_err2string(ld_errnum),
1160 serv_errstr,
1161 (ld_errstr && *ld_errstr) ? ": " : "",
1162 (ld_errstr && *ld_errstr) ? ld_errstr : "");
1164 if(wp_err->error)
1165 fs_give((void **)&wp_err->error);
1167 wp_err->error = cpystr(ebuf);
1168 if(we_cancel)
1169 cancel_busy_cue(-1);
1171 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1172 display_message('x');
1173 dprint((2, "%s\n", ebuf));
1176 dprint((5, "Matched 0 entries on %s\n",
1177 serv ? serv : "?"));
1178 if(res)
1179 ldap_msgfree(res);
1180 if(ld)
1181 #ifdef _WINDOWS
1182 ldap_unbind(ld);
1183 #else
1184 ldap_unbind_ext(ld, NULL, NULL);
1185 #endif
1187 res = NULL; ld = NULL;
1191 if(pwd)
1192 fs_give((void **) &pwd);
1195 if(we_cancel)
1196 cancel_busy_cue(-1);
1198 if(we_turned_on)
1199 intr_handling_off();
1201 if(serv)
1202 fs_give((void **)&serv);
1203 if(base)
1204 fs_give((void **)&base);
1205 if(serv_errstr)
1206 fs_give((void **)&serv_errstr);
1208 return(serv_res);
1213 * Given a list of entries, present them to user so user may
1214 * select one.
1216 * Args head -- The head of the list of results
1217 * orig -- The string the user was searching for
1218 * result -- Returned pointer to chosen LDAP_SEARCH_WINNER or NULL
1219 * wp_err -- Error handling
1220 * style --
1222 * Returns 0 ok
1223 * -1 Exit chosen by user
1224 * -2 None of matched entries had an email address
1225 * -3 No matched entries
1226 * -4 Goback to Abook List chosen by user
1227 * -5 caller shouldn't free head
1230 ask_user_which_entry(LDAP_SERV_RES_S *head, char *orig, LDAP_CHOOSE_S **result,
1231 WP_ERR_S *wp_err, LDAPLookupStyle style)
1233 ADDR_CHOOSE_S ac;
1234 char t[200];
1235 int retval;
1237 dprint((3, "ask_user_which(style=%s)\n",
1238 style == AlwaysDisplayAndMailRequired ? "AlwaysDisplayAndMailRequired" :
1239 style == AlwaysDisplay ? "AlwaysDisplay" :
1240 style == DisplayIfTwo ? "DisplayIfTwo" :
1241 style == DisplayForURL ? "DisplayForURL" :
1242 style == DisplayIfOne ? "DisplayIfOne" : "?"));
1245 * Set up a screen for user to choose one entry.
1248 if(style == AlwaysDisplay || style == DisplayForURL)
1249 snprintf(t, sizeof(t), "SEARCH RESULTS INDEX");
1250 else{
1251 int len;
1253 len = strlen(orig);
1254 snprintf(t, sizeof(t), _("SELECT ONE ADDRESS%s%s%s"),
1255 (orig && *orig && len < 40) ? " FOR \"" : "",
1256 (orig && *orig && len < 40) ? orig : "",
1257 (orig && *orig && len < 40) ? "\"" : "");
1260 memset(&ac, 0, sizeof(ADDR_CHOOSE_S));
1261 ac.title = cpystr(t);
1262 ac.res_head = head;
1264 retval = ldap_addr_select(ps_global, &ac, result, style, wp_err, orig);
1266 switch(retval){
1267 case 0: /* Ok */
1268 break;
1270 case -1: /* Exit chosen by user */
1271 wp_exit = 1;
1272 break;
1274 case -4: /* GoBack to AbookList chosen by user */
1275 break;
1277 case -5:
1278 wp_nobail = 1;
1279 break;
1281 case -2:
1282 if(style != AlwaysDisplay){
1283 if(wp_err->error)
1284 fs_give((void **)&wp_err->error);
1286 wp_err->error =
1287 cpystr(_("None of the names matched on directory server has an email address"));
1288 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1289 display_message('x');
1292 break;
1294 case -3:
1295 if(style == AlwaysDisplayAndMailRequired){
1296 if(wp_err->error)
1297 fs_give((void **)&wp_err->error);
1299 wp_err->error = cpystr(_("No matches on directory server"));
1300 q_status_message(SM_ORDER, 3, 5, wp_err->error);
1301 display_message('x');
1304 break;
1307 fs_give((void **)&ac.title);
1309 return(retval);
1313 ADDRESS *
1314 address_from_ldap(LDAP_CHOOSE_S *winning_e)
1316 ADDRESS *ret_a = NULL;
1318 if(winning_e){
1319 char *a;
1320 BerElement *ber;
1322 ret_a = mail_newaddr();
1323 for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
1324 a != NULL;
1325 a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
1326 int i;
1327 char *p;
1328 struct berval **vals;
1330 dprint((9, "attribute: %s\n", a ? a : "?"));
1331 if(!ret_a->personal &&
1332 strcmp(a, winning_e->info_used->cnattr) == 0){
1333 dprint((9, "Got cnattr:"));
1334 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
1335 for(i = 0; i < ldap_count_values_len(vals); i++)
1336 dprint((9, " %s\n",
1337 vals[i] ? vals[i]->bv_val : "?"));
1339 if(ALPINE_LDAP_can_use(vals))
1340 ret_a->personal = cpystr(vals[0]->bv_val);
1342 ldap_value_free_len(vals);
1344 else if(!ret_a->mailbox &&
1345 strcmp(a, winning_e->info_used->mailattr) == 0){
1346 dprint((9, "Got mailattr:"));
1347 vals = ldap_get_values_len(winning_e->ld, winning_e->selected_entry, a);
1348 for(i = 0; i < ldap_count_values_len(vals); i++)
1349 dprint((9, " %s\n",
1350 vals[i] ? vals[i]->bv_val : "?"));
1352 /* use first one */
1353 if(ALPINE_LDAP_can_use(vals)){
1354 if((p = strindex(vals[0]->bv_val, '@')) != NULL){
1355 ret_a->host = cpystr(p+1);
1356 *p = '\0';
1359 ret_a->mailbox = cpystr(vals[0]->bv_val);
1362 ldap_value_free_len(vals);
1365 our_ldap_memfree(a);
1369 return(ret_a);
1374 * Break up the ldap-server string stored in the pinerc into its
1375 * parts. The structure is allocated here and should be freed by the caller.
1377 * The original string looks like
1378 * <servername>[:port] <SPACE> "/base=<base>/impl=1/..."
1380 * Args serv_str -- The original string from the pinerc to parse.
1382 * Returns A pointer to a structure with filled in answers.
1384 * Some of the members have defaults. If port is -1, that means to use
1385 * the default LDAP_PORT. If base is NULL, use "". Type and srch have
1386 * defaults defined in alpine.h. If cust is non-NULL, it overrides type and
1387 * srch.
1389 LDAP_SERV_S *
1390 break_up_ldap_server(char *serv_str)
1392 char *lserv;
1393 char *q, *p, *tail;
1394 int i, only_one = 1;
1395 LDAP_SERV_S *info = NULL;
1397 if(!serv_str)
1398 return(info);
1400 info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
1403 * Initialize to defaults.
1405 memset((void *)info, 0, sizeof(*info));
1406 info->port = -1;
1407 info->srch = -1;
1408 info->type = -1;
1409 info->time = -1;
1410 info->size = -1;
1411 info->scope = -1;
1413 /* copy the whole string to work on */
1414 lserv = cpystr(serv_str);
1415 if(lserv)
1416 removing_trailing_white_space(lserv);
1418 if(!lserv || !*lserv || *lserv == '"'){
1419 if(lserv)
1420 fs_give((void **)&lserv);
1422 if(info)
1423 free_ldap_server_info(&info);
1425 return(NULL);
1428 tail = lserv;
1429 while((tail = strindex(tail, SPACE)) != NULL){
1430 tail++;
1431 if(*tail == '"' || *tail == '/'){
1432 *(tail-1) = '\0';
1433 break;
1435 else
1436 only_one = 0;
1439 /* tail is the part after server[:port] <SPACE> */
1440 if(tail && *tail){
1441 removing_leading_white_space(tail);
1442 (void)removing_double_quotes(tail);
1445 /* get the optional port number */
1446 if(only_one && (q = strindex(lserv, ':')) != NULL){
1447 int ldapport = -1;
1449 *q = '\0';
1450 if((ldapport = atoi(q+1)) >= 0)
1451 info->port = ldapport;
1454 /* use lserv for serv even though it has a few extra bytes alloced */
1455 info->serv = lserv;
1457 if(tail && *tail){
1458 /* get the search base */
1459 if((q = srchstr(tail, "/base=")) != NULL)
1460 info->base = remove_backslash_escapes(q+6);
1462 if((q = srchstr(tail, "/binddn=")) != NULL)
1463 info->binddn = remove_backslash_escapes(q+8);
1465 /* get the implicit parameter */
1466 if((q = srchstr(tail, "/impl=1")) != NULL)
1467 info->impl = 1;
1469 /* get the rhs parameter */
1470 if((q = srchstr(tail, "/rhs=1")) != NULL)
1471 info->rhs = 1;
1473 /* get the ref parameter */
1474 if((q = srchstr(tail, "/ref=1")) != NULL)
1475 info->ref = 1;
1477 /* get the nosub parameter */
1478 if((q = srchstr(tail, "/nosub=1")) != NULL)
1479 info->nosub = 1;
1481 /* get the tls parameter */
1482 if((q = srchstr(tail, "/tls=1")) != NULL)
1483 info->tls = 1;
1485 /* get the tlsmust parameter */
1486 if((q = srchstr(tail, "/tlsm=1")) != NULL)
1487 info->tlsmust = 1;
1489 /* get the ldaps parameter */
1490 if((q = srchstr(tail, "/ldaps=1")) != NULL)
1491 info->ldaps = 1;
1493 /* get the search type value */
1494 if((q = srchstr(tail, "/type=")) != NULL){
1495 NAMEVAL_S *v;
1497 q += 6;
1498 if((p = strindex(q, '/')) != NULL)
1499 *p = '\0';
1501 for(i = 0; (v = ldap_search_types(i)); i++)
1502 if(!strucmp(q, v->name)){
1503 info->type = v->value;
1504 break;
1507 if(p)
1508 *p = '/';
1511 /* get the search rule value */
1512 if((q = srchstr(tail, "/srch=")) != NULL){
1513 NAMEVAL_S *v;
1515 q += 6;
1516 if((p = strindex(q, '/')) != NULL)
1517 *p = '\0';
1519 for(i = 0; (v = ldap_search_rules(i)); i++)
1520 if(!strucmp(q, v->name)){
1521 info->srch = v->value;
1522 break;
1525 if(p)
1526 *p = '/';
1529 /* get the scope */
1530 if((q = srchstr(tail, "/scope=")) != NULL){
1531 NAMEVAL_S *v;
1533 q += 7;
1534 if((p = strindex(q, '/')) != NULL)
1535 *p = '\0';
1537 for(i = 0; (v = ldap_search_scope(i)); i++)
1538 if(!strucmp(q, v->name)){
1539 info->scope = v->value;
1540 break;
1543 if(p)
1544 *p = '/';
1547 /* get the time limit */
1548 if((q = srchstr(tail, "/time=")) != NULL){
1549 q += 6;
1550 if((p = strindex(q, '/')) != NULL)
1551 *p = '\0';
1553 /* This one's a number */
1554 if(*q){
1555 char *err;
1557 err = strtoval(q, &i, 0, 500, 0, tmp_20k_buf, SIZEOF_20KBUF, "ldap timelimit");
1558 if(err){
1559 dprint((1, "%s\n", err ? err : "?"));
1561 else
1562 info->time = i;
1565 if(p)
1566 *p = '/';
1569 /* get the size limit */
1570 if((q = srchstr(tail, "/size=")) != NULL){
1571 q += 6;
1572 if((p = strindex(q, '/')) != NULL)
1573 *p = '\0';
1575 /* This one's a number */
1576 if(*q){
1577 char *err;
1579 err = strtoval(q, &i, 0, 500, 0, tmp_20k_buf, SIZEOF_20KBUF, "ldap sizelimit");
1580 if(err){
1581 dprint((1, "%s\n", err ? err : "?"));
1583 else
1584 info->size = i;
1587 if(p)
1588 *p = '/';
1591 /* get the custom search filter */
1592 if((q = srchstr(tail, "/cust=")) != NULL)
1593 info->cust = remove_backslash_escapes(q+6);
1595 /* get the nickname */
1596 if((q = srchstr(tail, "/nick=")) != NULL)
1597 info->nick = remove_backslash_escapes(q+6);
1599 /* get the mail attribute name */
1600 if((q = srchstr(tail, "/matr=")) != NULL)
1601 info->mailattr = remove_backslash_escapes(q+6);
1603 /* get the sn attribute name */
1604 if((q = srchstr(tail, "/satr=")) != NULL)
1605 info->snattr = remove_backslash_escapes(q+6);
1607 /* get the gn attribute name */
1608 if((q = srchstr(tail, "/gatr=")) != NULL)
1609 info->gnattr = remove_backslash_escapes(q+6);
1611 /* get the cn attribute name */
1612 if((q = srchstr(tail, "/catr=")) != NULL)
1613 info->cnattr = remove_backslash_escapes(q+6);
1615 /* get the backup mail address */
1616 if((q = srchstr(tail, "/mail=")) != NULL)
1617 info->mail = remove_backslash_escapes(q+6);
1620 return(info);
1624 void
1625 free_ldap_server_info(LDAP_SERV_S **info)
1627 if(info && *info){
1628 if((*info)->serv)
1629 fs_give((void **)&(*info)->serv);
1631 if((*info)->base)
1632 fs_give((void **)&(*info)->base);
1634 if((*info)->cust)
1635 fs_give((void **)&(*info)->cust);
1637 if((*info)->binddn)
1638 fs_give((void **)&(*info)->binddn);
1640 if((*info)->nick)
1641 fs_give((void **)&(*info)->nick);
1643 if((*info)->mail)
1644 fs_give((void **)&(*info)->mail);
1646 if((*info)->mailattr)
1647 fs_give((void **)&(*info)->mailattr);
1649 if((*info)->snattr)
1650 fs_give((void **)&(*info)->snattr);
1652 if((*info)->gnattr)
1653 fs_give((void **)&(*info)->gnattr);
1655 if((*info)->cnattr)
1656 fs_give((void **)&(*info)->cnattr);
1658 fs_give((void **)info);
1659 *info = NULL;
1664 LDAP_SERV_S *
1665 copy_ldap_serv_info(LDAP_SERV_S *src)
1667 LDAP_SERV_S *info = NULL;
1669 if(src){
1670 info = (LDAP_SERV_S *) fs_get(sizeof(*info));
1673 * Initialize to defaults.
1675 memset((void *)info, 0, sizeof(*info));
1677 info->serv = src->serv ? cpystr(src->serv) : NULL;
1678 info->base = src->base ? cpystr(src->base) : NULL;
1679 info->cust = src->cust ? cpystr(src->cust) : NULL;
1680 info->binddn = src->binddn ? cpystr(src->binddn) : NULL;
1681 info->nick = src->nick ? cpystr(src->nick) : NULL;
1682 info->mail = src->mail ? cpystr(src->mail) : NULL;
1683 info->mailattr = cpystr((src->mailattr && src->mailattr[0])
1684 ? src->mailattr : DEF_LDAP_MAILATTR);
1685 info->snattr = cpystr((src->snattr && src->snattr[0])
1686 ? src->snattr : DEF_LDAP_SNATTR);
1687 info->gnattr = cpystr((src->gnattr && src->gnattr[0])
1688 ? src->gnattr : DEF_LDAP_GNATTR);
1689 info->cnattr = cpystr((src->cnattr && src->cnattr[0])
1690 ? src->cnattr : DEF_LDAP_CNATTR);
1692 info->port = (src->port < 0) ? LDAP_PORT : src->port;
1693 info->time = (src->time < 0) ? DEF_LDAP_TIME : src->time;
1694 info->size = (src->size < 0) ? DEF_LDAP_SIZE : src->size;
1695 info->type = (src->type < 0) ? DEF_LDAP_TYPE : src->type;
1696 info->srch = (src->srch < 0) ? DEF_LDAP_SRCH : src->srch;
1697 info->scope = (src->scope < 0) ? DEF_LDAP_SCOPE : src->scope;
1698 info->impl = src->impl;
1699 info->rhs = src->rhs;
1700 info->ref = src->ref;
1701 info->nosub = src->nosub;
1702 info->tls = src->tls;
1705 return(info);
1709 void
1710 free_ldap_result_list(LDAP_SERV_RES_S **r)
1712 if(r && *r){
1713 free_ldap_result_list(&(*r)->next);
1714 if((*r)->res)
1715 ldap_msgfree((*r)->res);
1716 if((*r)->ld)
1717 #ifdef _WINDOWS
1718 ldap_unbind((*r)->ld);
1719 #else
1720 ldap_unbind_ext((*r)->ld, NULL, NULL);
1721 #endif
1722 if((*r)->info_used)
1723 free_ldap_server_info(&(*r)->info_used);
1724 if((*r)->serv)
1725 fs_give((void **) &(*r)->serv);
1727 fs_give((void **) r);
1733 * Mask API differences.
1735 void
1736 our_ldap_memfree(void *a)
1738 #if (LDAPAPI >= 15)
1739 if(a)
1740 ldap_memfree(a);
1741 #endif
1746 * Mask API differences.
1748 void
1749 our_ldap_dn_memfree(void *a)
1751 #if defined(_WINDOWS)
1752 if(a)
1753 ldap_memfree(a);
1754 #else
1755 #if (LDAPAPI >= 15)
1756 if(a)
1757 ldap_memfree(a);
1758 #else
1759 if(a)
1760 free(a);
1761 #endif
1762 #endif
1767 * More API masking.
1770 our_ldap_get_lderrno(LDAP *ld, char **m, char **s)
1772 int ret = 0;
1774 #if (LDAPAPI >= 2000)
1775 if(ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&ret) == 0){
1776 if(s)
1777 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, (void *)s);
1779 #elif (LDAPAPI >= 15)
1780 ret = ldap_get_lderrno(ld, m, s);
1781 #else
1782 ret = ld->ld_errno;
1783 if(s)
1784 *s = ld->ld_error;
1785 #endif
1787 return(ret);
1792 * More API masking.
1795 our_ldap_set_lderrno(LDAP *ld, int e, char *m, char *s)
1797 int ret;
1799 #if (LDAPAPI >= 2000)
1800 if(ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&e) == 0)
1801 ret = LDAP_SUCCESS;
1802 else
1803 (void)ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&ret);
1804 #elif (LDAPAPI >= 15)
1805 ret = ldap_set_lderrno(ld, e, m, s);
1806 #else
1807 /* this is all we care about */
1808 ld->ld_errno = e;
1809 ret = LDAP_SUCCESS;
1810 #endif
1812 return(ret);
1817 * More API masking.
1820 our_ldap_set_option(LDAP *ld, int option, void *optdata)
1822 int ret;
1824 #if (LDAPAPI >= 15)
1825 ret = ldap_set_option(ld, option, optdata);
1826 #else
1827 switch(option){
1828 case LDAP_OPT_TIMELIMIT:
1829 ld->ld_timelimit = *(int *)optdata;
1830 break;
1832 case LDAP_OPT_SIZELIMIT:
1833 ld->ld_sizelimit = *(int *)optdata;
1834 break;
1836 case LDAP_OPT_RESTART:
1837 if((int)optdata)
1838 ld->ld_options |= LDAP_OPT_RESTART;
1839 else
1840 ld->ld_options &= ~LDAP_OPT_RESTART;
1842 break;
1845 * Does nothing here. There is only one protocol version supported.
1847 case LDAP_OPT_PROTOCOL_VERSION:
1848 ret = -1;
1849 break;
1851 default:
1852 alpine_panic("LDAP function not implemented");
1854 #endif
1856 return(ret);
1861 * Returns 1 if we can use LDAP version 3 protocol.
1864 ldap_v3_is_supported(LDAP *ld)
1866 return(1);
1869 struct tl_table {
1870 char *ldap_ese;
1871 char *translated;
1874 static struct tl_table ldap_trans_table[]={
1876 * TRANSLATORS: This is a list of LDAP attributes with translations to present
1877 * to the user. For example the attribute mail is Email Address and the attribute
1878 * cn is Name.
1880 {"mail", N_("Email Address")},
1881 #define LDAP_MAIL_ATTR 0
1882 {"sn", N_("Surname")},
1883 #define LDAP_SN_ATTR 1
1884 {"givenName", N_("Given Name")},
1885 #define LDAP_GN_ATTR 2
1886 {"cn", N_("Name")},
1887 #define LDAP_CN_ATTR 3
1888 {"electronicmail", N_("Email Address")},
1889 #define LDAP_EMAIL_ATTR 4
1890 {"o", N_("Organization")},
1891 {"ou", N_("Unit")},
1892 {"c", N_("Country")},
1893 {"st", N_("State or Province")},
1894 {"l", N_("Locality")},
1895 {"objectClass", N_("Object Class")},
1896 {"title", N_("Title")},
1897 {"departmentNumber", N_("Department")},
1898 {"postalAddress", N_("Postal Address")},
1899 {"homePostalAddress", N_("Home Address")},
1900 {"mailStop", N_("Mail Stop")},
1901 {"telephoneNumber", N_("Voice Telephone")},
1902 {"homePhone", N_("Home Telephone")},
1903 {"officePhone", N_("Office Telephone")},
1904 {"facsimileTelephoneNumber", N_("FAX Telephone")},
1905 {"mobile", N_("Mobile Telephone")},
1906 {"voiceMailTelephoneNumber", N_("Voice Mail")},
1907 {"pager", N_("Pager")},
1908 {"roomNumber", N_("Room Number")},
1909 {"uid", N_("User ID")},
1910 {"userCertificate", N_("User Certificate")},
1911 {NULL, NULL}
1914 char *
1915 ldap_translate(char *a, LDAP_SERV_S *info_used)
1917 char *s, *ret_a = a;
1918 int i;
1920 if((s = strchr(a, ';')) != NULL)
1921 *s = '\0';
1923 if(info_used){
1924 if(info_used->mailattr && strucmp(info_used->mailattr, a) == 0)
1925 ret_a = _(ldap_trans_table[LDAP_MAIL_ATTR].translated);
1926 else if(info_used->snattr && strucmp(info_used->snattr, a) == 0)
1927 ret_a = _(ldap_trans_table[LDAP_SN_ATTR].translated);
1928 else if(info_used->gnattr && strucmp(info_used->gnattr, a) == 0)
1929 ret_a = _(ldap_trans_table[LDAP_GN_ATTR].translated);
1930 else if(info_used->cnattr && strucmp(info_used->cnattr, a) == 0)
1931 ret_a = _(ldap_trans_table[LDAP_CN_ATTR].translated);
1934 for(i = 0; ldap_trans_table[i].ldap_ese; i++){
1935 if(info_used)
1936 switch(i){
1937 case LDAP_MAIL_ATTR:
1938 case LDAP_SN_ATTR:
1939 case LDAP_GN_ATTR:
1940 case LDAP_CN_ATTR:
1941 case LDAP_EMAIL_ATTR:
1942 continue;
1945 if(strucmp(ldap_trans_table[i].ldap_ese, a) == 0)
1946 ret_a = _(ldap_trans_table[i].translated);
1949 if(s)
1950 *s = ';';
1952 return ret_a;
1955 char **
1956 berval_to_array(struct berval **v)
1958 char **rv = NULL;
1959 int i, len;
1961 if(v == NULL) return rv;
1962 len = ldap_count_values_len(v);
1964 rv = fs_get((len+1)*sizeof(char *));
1965 for(i = 0; i < len; i++)
1966 if(ALPINE_LDAP_can_use_num(v, i))
1967 rv[i] = cpystr(v[i]->bv_val);
1968 else
1969 rv[i] = NULL;
1970 rv[len] = NULL;
1972 return rv;
1975 #endif /* ENABLE_LDAP */