diff options
Diffstat (limited to 'ldap/clients/dsgw/ldaputil.c')
-rw-r--r-- | ldap/clients/dsgw/ldaputil.c | 1564 |
1 files changed, 1564 insertions, 0 deletions
diff --git a/ldap/clients/dsgw/ldaputil.c b/ldap/clients/dsgw/ldaputil.c new file mode 100644 index 00000000..d574cd1f --- /dev/null +++ b/ldap/clients/dsgw/ldaputil.c @@ -0,0 +1,1564 @@ +/** + * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to + * license terms. Copyright © 2001 Sun Microsystems, Inc. + * Some preexisting portions Copyright © 2001 Netscape Communications Corp. + * All rights reserved. + */ +/* + * ldaputil.c -- LDAP utility functions -- HTTP gateway + * + * Copyright (c) 1996 Netscape Communications Corp. + * All rights reserved. + */ + +#include "dsgw.h" +#include "dbtdsgw.h" +#include "../../include/disptmpl.h" +#ifndef NO_LIBLCACHE +#include <lcache.h> +#endif +#if XP_WIN32 +#include <windows.h> +#include <io.h> +#include <fcntl.h> +#endif + +static dsgwtmplinfo *init_listdisplay( char *tmplname, unsigned long options ); +static int do_search( dsgwtmplinfo *tip, LDAP *ld, char *base, int scope, + char *filter, LDAPMessage **msgpp ); +static void handle_search_results( dsgwtmplinfo *tip, LDAP *ld, int rc, + LDAPMessage *msgp, unsigned long options ); +static int LDAP_CALL LDAP_CALLBACK + get_rebind_credentials( LDAP *ld, char **whop, char **credp, + int *methodp, int freeit, void *arg ); +static void strcpy_special_undo( char *d, char *s ); +static int entry2htmlwrite( void *fp, char *buf, int len ); +static void emit_one_loc_dn( char *dn, char *friendlyname, char *rootname, + int only_one ); +static char *uid2dn( LDAP *ld, char *uid, char *base, int *ldaprc, + char **lderrtxtp, char **errsp ); +static void return_one_attr( LDAP *ld, LDAPMessage *entry, char *attrtype, + char *mimetype, int valindex ); +static void break_up_one_attr( char *attr, char **attrtypep, char **mimetypep, + int *valindexp ); + +/* binddn and bindpasswd are used in get_rebind_credentials() */ +static char *binddn = NULL, *bindpasswd = NULL; + +#ifndef DSGW_NO_SSL +/*static CERTCertDBHandle certdbh;*/ +static char * certdbh; + +#endif + +/* + * initialize various LDAP library things -- any non-NULL parameters are + * initialized and set. If an error occurs, this function will not + * return at all. + * If an LDAP connection was opened, this function will return either + * DSGW_BOUND_ASUSER if a valid cookie was found in the environment + * and we were able to bind to the directory as that user. If no + * cookie was found, or the cookie would not be used to bind, then + * an anonymous bind is performed and DSGW_BOUND_ANONYMOUS is returned. + * If skipac (skip authentication check) is non-zero, then this + * function will always authenticate as NULL. + * + * If we are configured to use a local LDAP database instead of a real + * directory server, we always do an unauthenticated bind but we return + * DSGW_BOUND_ASUSER. This is done to keep our CGIs that check for a + * return code of DSGW_BOUND_ASUSER happy. + * + * If skipauthwarning is set, then we don't display the javascript + * auth warning for searches. - RJP + */ +int +dsgw_init_ldap( LDAP **ldp, LDAPFiltDesc **lfdpp, int skipac, int skipauthwarning ) +{ + char *path; + char *userid, *dn, *rndstr, *passwd, *cookie, *p; + int ret = 0, optval, limit; +#ifdef XP_WIN32 + WSADATA wsadata; +#endif + + /* LDAP search filters */ + if ( lfdpp != NULL ) { + path = dsgw_file2path( gc->gc_configdir, DSGW_FILTERFILE ); + if (( *lfdpp = ldap_init_getfilter( path )) == NULL ) { + dsgw_error( DSGW_ERR_BADCONFIG, path, DSGW_ERROPT_EXIT, 0, NULL ); + } + free( path ); + ret = 0; + } + +#ifdef XP_WIN32 + + if( ret = WSAStartup(0x0101, &wsadata ) != 0 ) + dsgw_error( DSGW_ERR_WSAINIT, NULL, DSGW_ERROPT_EXIT, 0, NULL ); + +#endif /* XP_WIN32 */ + + /* LDAP connection */ + if ( ldp != NULL ) { + if ( gc == NULL ) { + dsgw_error( DSGW_ERR_INTERNAL, + XP_GetClientStr(DBT_ldapInitLcacheInitAttemptedBefor_), + DSGW_ERROPT_EXIT, 0, NULL ); + } + if ( gc->gc_localdbconf == NULL ) { + /* "Real LDAP server" case */ +#ifdef DSGW_NO_SSL + *ldp = ldap_init( gc->gc_ldapserver, gc->gc_ldapport ); +#else /* DSGW_NO_SSL */ + if ( gc->gc_ldapssl ) { + if ( gc->gc_securitypath == NULL ) { + dsgw_error( DSGW_ERR_NOSECPATH, NULL, DSGW_ERROPT_EXIT, + 0, NULL ); + } + if ( ldapssl_client_init( gc->gc_securitypath, + &certdbh ) < 0 ) { + dsgw_error( DSGW_ERR_SSLINIT, gc->gc_securitypath, + DSGW_ERROPT_EXIT, 0, NULL ); + } + *ldp = ldapssl_init( gc->gc_ldapserver, gc->gc_ldapport, 1 ); + dsgw_NSSInitializedAlready = 1; + } else { + *ldp = ldap_init( gc->gc_ldapserver, gc->gc_ldapport ); + } +#endif /* !DSGW_NO_SSL */ + if ( *ldp == NULL ) { + dsgw_error( DSGW_ERR_LDAPINIT, NULL, DSGW_ERROPT_EXIT, 0, + NULL ); + } + + } +#ifndef NO_LIBLCACHE +else { + /* Local DB case */ + if (( *ldp = ldap_init( NULL, 0 )) == NULL ) { + dsgw_error( DSGW_ERR_LDAPINIT, NULL, DSGW_ERROPT_EXIT, 0, + NULL ); + } + if ( lcache_init( *ldp, gc->gc_localdbconf ) != 0 ) { + dsgw_error( DSGW_ERR_LCACHEINIT, strerror(errno), + DSGW_ERROPT_EXIT, 0, NULL ); + } + optval = 1; + (void) ldap_set_option( *ldp, LDAP_OPT_CACHE_ENABLE, &optval ); + optval = LDAP_CACHE_LOCALDB; + (void) ldap_set_option( *ldp, LDAP_OPT_CACHE_STRATEGY, &optval ); + } +#endif + rndstr = dn = NULL; + passwd = dsgw_get_cgi_var( "passwd", DSGW_CGIVAR_OPTIONAL ); + + if (( p = dsgw_get_cgi_var( "ldapsizelimit", DSGW_CGIVAR_OPTIONAL )) + != NULL ) { + limit = atoi( p ); + (void) ldap_set_option( *ldp, LDAP_OPT_SIZELIMIT, &limit ); + } + + if (( p = dsgw_get_cgi_var( "ldaptimelimit", DSGW_CGIVAR_OPTIONAL )) + != NULL ) { + limit = atoi( p ); + (void) ldap_set_option( *ldp, LDAP_OPT_TIMELIMIT, &limit ); + } + + /* + * we don't bother with authentication if: + * the "skipac" flag is non-zero OR + * no "passwd" form element was passed in and we are using local db + */ + if ( !skipac && ( passwd != NULL || gc->gc_localdbconf == NULL )) { + /* + * There are several ways in which authentication might + * happen. + */ + if ( gc->gc_admserv ) { + /* + * We're running under the admin server, so ask libadmin + * for the user's credentials. If a password comes as a form + * field, it overrides value we get from admin server + */ + (void)dsgw_get_adm_identity( *ldp, &userid, &dn, + ( passwd == NULL ) ? &passwd : NULL, DSGW_ERROPT_EXIT ); + +#ifdef DSGW_DEBUG + dsgw_log( "dsgw_init_ldap: run under admserv, user id = %s, " + "dn = %s, passwd = %s, skipac = %d, dn = 0x%x\n", + userid == NULL ? "NULL" : userid, + dn == NULL ? "NULL" : dn, + passwd == NULL ? "NULL" : passwd, + skipac, dn ); +#endif + } else { + /* + * Not running under admin server. The DN and password + * might come in as form fields, or the authentication + * might be accomplished via a client-side cookie which + * gets looked up in the gateway's cookie database. + */ + + /* check for dn/binddn in request */ + if ( passwd != NULL ) { + if (( dn = dsgw_get_escaped_cgi_var( "escapedbinddn", + "binddn", DSGW_CGIVAR_OPTIONAL )) == NULL && + ( dn = dsgw_get_cgi_var( "dn", + DSGW_CGIVAR_OPTIONAL )) == NULL ) { + free( passwd ); + passwd = NULL; + } else { + /* got DN: undo extra level of escaping */ + dsgw_form_unescape( dn ); + } + } + + if ( passwd == NULL ) { + /* Check for a valid authentication cookie */ + cookie = dsgw_get_auth_cookie(); + if ( cookie != NULL ) { + if ( dsgw_parse_cookie( cookie, &rndstr, &dn ) == 0 ) { + int ckrc; + if (( ckrc = dsgw_ckdn2passwd( rndstr, dn, + &passwd )) != 0 ) { + + passwd = NULL; + dn = NULL; + /* + * Delete the cookie and print out the error message. + * dn2passwd_error() returns 1 if the CGI should exit, + * 0 if it should continue. + */ + if (dsgw_dn2passwd_error( ckrc, skipauthwarning )) { + exit( 0 ); + } + + } + } + } + + if ( rndstr != NULL ) { + free( rndstr ); + } + if ( cookie != NULL ) { + free( cookie ); + } + } + } + } + + /* + * try to use LDAP version 3 but fall back to v2 if bind fails + */ + optval = LDAP_VERSION3; + (void)ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION, &optval ); + + /* + * If everything above failed to set the dn/password, then use + * the binddn and bindpw, if any. + */ + if (dn == NULL && passwd == NULL && + strlen(gc->gc_binddn) > 0 && strlen(gc->gc_bindpw) > 0) { + dn = dsgw_ch_strdup(gc->gc_binddn); + passwd = dsgw_ch_strdup(gc->gc_bindpw); + } + + if (( ret = ldap_simple_bind_s( *ldp, dn, passwd )) + == LDAP_PROTOCOL_ERROR ) { + optval = LDAP_VERSION2; + (void)ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION, + &optval ); + ret = ldap_simple_bind_s( *ldp, dn, passwd ); + } + + if ( ret != LDAP_SUCCESS ){ + dsgw_ldap_error( *ldp, DSGW_ERROPT_DURINGBIND ); + + /* Display back button */ + dsgw_form_begin( NULL, NULL ); + dsgw_emits( "\n<CENTER><TABLE border=2 width=\"100%\"><TR>\n" ); + dsgw_emits( "<TD WIDTH=\"100%\" ALIGN=\"center\">\n" ); + dsgw_emitf( "<INPUT TYPE=\"button\" VALUE=\"%s\" " + "onClick=\"history.back()\">\n", + XP_GetClientStr(DBT_goBack_) ); + dsgw_emits( "\n</TABLE></CENTER></FORM>\n" ); + exit(0); + } + + if (( dn != NULL ) && ( passwd != NULL )) { + ret = DSGW_BOUND_ASUSER; + binddn = dn; + bindpasswd = passwd; + ldap_set_rebind_proc( *ldp, get_rebind_credentials, NULL ); + } else if ( gc->gc_localdbconf != NULL ) { + ret = DSGW_BOUND_ASUSER; /* a small, harmless lie */ + } else { + ret = DSGW_BOUND_ANONYMOUS; + } + + } + return ret; +} + + +/* + * get user identity from the admin. server (if running under it) + * if uidp is non-NULL, it is set to point to user's login id. + * if dnp is non-NULL, it is set to point to user's DN. + * if pwdp is non-NULL, it is set to point to user's password. + * Returns: 0 if all goes well, -1 if an error occurs. + * + * Note that ld is used only if dnp != NULL, and then only if the admin server + * returns NULL when asked for the DN. + */ +int +dsgw_get_adm_identity( LDAP *ld, char **uidp, char **dnp, char **pwdp, + int erropts ) +{ + int rc, need_to_get_dn; + char *uid; + static int adm_inited = 0; + + if ( !gc->gc_admserv ) { + dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, + XP_GetClientStr(DBT_notRunningUnderTheAdministration_), + erropts, 0, NULL ); + return( -1 ); + } + + if ( !adm_inited ) { + if ( ADM_InitializePermissions( &rc ) < 0 ) { + dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, + XP_GetClientStr(DBT_couldNotInitializePermissions_), + erropts, 0, NULL ); + return( -1 ); + } + adm_inited = 1; + } + + need_to_get_dn = ( dnp != NULL ); + + if ( need_to_get_dn && ADM_GetUserDNString( &rc, dnp ) < 0 ) { + dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, + XP_GetClientStr(DBT_couldNotMapUsernameToADnErrorFro_), + erropts, 0, NULL ); + return( -1 ); + } + + /* + * get userid if: + * 1. requested by caller (uidp != NULL) + * or 2. DN was requested but Admin Server didn't return the DN + */ + if (( uidp != NULL || ( need_to_get_dn && *dnp == NULL )) && + ( ADM_GetCurrentUsername( &rc, &uid ) < 0 || uid == NULL )) { + dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, + XP_GetClientStr(DBT_couldNotGetCurrentUsername_), erropts, + 0, NULL ); + return( -1 ); + } + + if ( uidp != NULL ) { + *uidp = uid; + } + + if ( need_to_get_dn && *dnp == NULL ) { + /* + * try to map userid to DN using LDAP search + */ + int lderr; + char *errstr, *lderrtxt; + + if (( *dnp = uid2dn( ld, uid, gc->gc_ldapsearchbase, &lderr, + &lderrtxt, &errstr )) == NULL ) { + dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, errstr, erropts, lderr, + lderrtxt ); + return( -1 ); + } + } + + if ( pwdp != NULL && ADM_GetCurrentPassword( &rc, pwdp ) < 0 ) { + dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, + XP_GetClientStr(DBT_couldNotGetCurrentUserPassword_), erropts, + 0, NULL ); + return( -1 ); + } + + return( 0 ); +} + + +void +dsgw_ldap_error( LDAP *ld, int erropts ) +{ + int lderr; + char *lderrtxt = NULL; + + lderr = ldap_get_lderrno( ld, NULL, &lderrtxt ); + dsgw_error( DSGW_ERR_LDAPGENERAL, dsgw_ldaperr2string( lderr ), + erropts, lderr, lderrtxt ); +} + + +struct ldap_searchobj * +dsgw_type2searchobj( struct ldap_searchobj *solistp, char *type ) +{ + struct ldap_searchobj *sop; + + for ( sop = ldap_first_searchobj( solistp ); sop != NULL; + sop = ldap_next_searchobj( solistp, sop )) { + if ( strcasecmp( type, sop->so_objtypeprompt ) == 0 ) { + return( sop ); + } + } + + return( NULL ); +} + + +struct ldap_searchattr * +dsgw_label2searchattr( struct ldap_searchobj *sop, char *label ) +{ + struct ldap_searchattr *sap; + + for ( sap = sop->so_salist; sap != NULL; sap = sap->sa_next ) { + if ( strcasecmp( label, sap->sa_attrlabel ) == 0 ) { + return( sap ); + } + } + + return( NULL ); +} + + +struct ldap_searchmatch * +dsgw_prompt2searchmatch( struct ldap_searchobj *sop, char *prompt ) +{ + struct ldap_searchmatch *smp; + + for ( smp = sop->so_smlist; smp != NULL; smp = smp->sm_next ) { + if ( strcasecmp( prompt, smp->sm_matchprompt ) == 0 ) { + return( smp ); + } + } + + return( NULL ); +} + + +static dsgwtmplinfo * +init_listdisplay( char *tmplname, unsigned long options ) +{ + char *s; + + if (( s = dsgw_get_cgi_var( "listtemplate", DSGW_CGIVAR_OPTIONAL )) + != NULL ) { + tmplname = s; + } + + return( dsgw_display_init( DSGW_TMPLTYPE_LIST, tmplname, options )); +} + + +void +dsgw_smart_search( LDAP *ld, struct ldap_searchobj *sop, LDAPFiltDesc *lfdp, + char *base, char *value, unsigned long options ) +{ + int rc; + LDAPFiltInfo *lfip; + dsgwtmplinfo *tip; + LDAPMessage *msgp; + + ldap_setfilteraffixes( lfdp, sop->so_filterprefix, NULL ); + tip = init_listdisplay( sop->so_objtypeprompt, options ); + + if (( lfip = ldap_getfirstfilter( lfdp, sop->so_filtertag, value )) + == NULL ) { + dsgw_error( DSGW_ERR_NOFILTERS, sop->so_objtypeprompt, + DSGW_ERROPT_EXIT, 0, NULL ); + } + + for ( ; lfip != NULL; lfip = ldap_getnextfilter( lfdp )) { + dsgw_set_searchdesc( tip, NULL, lfip->lfi_desc, value ); + + rc = do_search( tip, ld, base, sop->so_defaultscope, lfip->lfi_filter, + &msgp ); + + if ( rc != LDAP_SUCCESS || + ( msgp != NULL && ldap_count_entries( ld, msgp ) > 0 )) { + if ( strstr( lfip->lfi_filter, "~=" ) != NULL ) { + /* always list if approximate filter used to find entry */ + options |= DSGW_DISPLAY_OPT_LIST_IF_ONE; + } + break; /* error or got some entries: stop searching */ + } + } + + handle_search_results( tip, ld, rc, msgp, options ); +} + + +void +dsgw_pattern_search( LDAP *ld, char *listtmpl, + char *searchdesc2, char *searchdesc3, char *searchdesc4, + char *filtpattern, char *filtprefix, char *filtsuffix, char *attr, + char *base, int scope, char *value, unsigned long options ) +{ + char buf[ 4096 ]; + int rc; + dsgwtmplinfo *tip; + LDAPMessage *msgp; + + tip = init_listdisplay( listtmpl, options ); + + ldap_build_filter( buf, sizeof( buf ), filtpattern, + filtprefix, filtsuffix, attr, value, NULL ); + + dsgw_set_searchdesc( tip, searchdesc2, searchdesc3, searchdesc4 ); + + rc = do_search( tip, ld, base, scope, buf, &msgp ); + handle_search_results( tip, ld, rc, msgp, options ); +} + + +/* + * Perform URL-based search. + * Note that if "ld" is NULL, this routine sets gc->gc_ldapserver and + * gc->gc_ldapport globals itself, calls dsgw_init_ldap(), and then does + * the URL-based search. If "ld" is not NULL, no initialization is done + * here. + */ +void +dsgw_ldapurl_search( LDAP *ld, char *ldapurl ) +{ + int rc, ec, saveport, did_init_ldap; + LDAPMessage *msgp; + LDAPURLDesc *ludp; + char *saveserver; + unsigned long no_options = 0; + int one_attr = 0; + + if (( rc = ldap_url_parse( ldapurl, &ludp )) != 0 ) { + switch ( rc ) { + case LDAP_URL_ERR_NODN: + ec = DSGW_ERR_LDAPURL_NODN; + break; + case LDAP_URL_ERR_BADSCOPE: + ec = DSGW_ERR_LDAPURL_BADSCOPE; + break; + case LDAP_URL_ERR_MEM: + ec = DSGW_ERR_NOMEMORY; + break; + case LDAP_URL_ERR_NOTLDAP: + default: + ec = DSGW_ERR_LDAPURL_NOTLDAP; + break; + } + dsgw_error( ec, ldapurl, DSGW_ERROPT_EXIT, 0, NULL ); + } + + if ( ld == NULL ) { + saveserver = gc->gc_ldapserver; + gc->gc_ldapserver = ludp->lud_host; + saveport = gc->gc_ldapport; + gc->gc_ldapport = ludp->lud_port; + one_attr = ( ludp->lud_attrs != NULL && ludp->lud_attrs[ 0 ] != NULL && ludp->lud_attrs[ 1 ] == NULL ); + (void)dsgw_init_ldap( &ld, NULL, 0, one_attr ); + did_init_ldap = 1; + } else { + did_init_ldap = 0; + } + + /* XXX a bit of a hack: if it looks like only a DN was included, we + * assume that a read of the entry is desired. + */ + if ( ludp->lud_scope == LDAP_SCOPE_BASE && strcasecmp( ludp->lud_filter, + "(objectClass=*)" ) == 0 ) { + dsgw_read_entry( ld, ludp->lud_dn, NULL, NULL, ludp->lud_attrs, + no_options ); + } else { + dsgwtmplinfo *tip; + + dsgw_send_header(); + tip = init_listdisplay( "urlsearch", no_options ); + dsgw_set_searchdesc( tip, NULL, XP_GetClientStr(DBT_theLDAPFilterIs_), ldapurl ); + rc = do_search( tip, ld, ludp->lud_dn, ludp->lud_scope, + ludp->lud_filter, &msgp ); + handle_search_results( tip, ld, rc, msgp, no_options ); + } + + if ( did_init_ldap ) { + ldap_unbind( ld ); + gc->gc_ldapserver = saveserver; + gc->gc_ldapport = saveport; + } +} + + +/* + * do the actual search over LDAP. Return an LDAP error code. + */ +static int +do_search( dsgwtmplinfo *tip, LDAP *ld, char *base, int scope, char *filter, + LDAPMessage **msgpp ) +{ + char **attrlist, *attrs[ 3 ]; + + *msgpp = NULL; + + if ( tip == NULL || tip->dsti_attrs == NULL ) { + attrs[ 0 ] = DSGW_ATTRTYPE_OBJECTCLASS; + if ( tip != NULL && tip->dsti_sortbyattr != NULL ) { + attrs[ 1 ] = tip->dsti_sortbyattr; + attrs[ 2 ] = NULL; + } else { + attrs[ 1 ] = NULL; + } + attrlist = attrs; + } else { + attrlist = tip->dsti_attrs; + } +#ifdef DSGW_DEBUG + dsgw_log ("ldap_search_s(ld,\"%s\",%i,\"%s\")\n", base, scope, filter); +#endif + return( ldap_search_s( ld, base, scope, filter, attrlist, 0, msgpp )); +} + + +static int +is_subtype( const char *sub, const char *sup ) +{ + auto const size_t subLen = strlen( sub ); + auto const size_t supLen = strlen( sup ); + if ( subLen < supLen ) return 0; + if ( subLen == supLen ) return !strcasecmp( sub, sup ); + if ( sub[supLen] != ';' ) return 0; + return !strncasecmp( sub, sup, strlen( sup )); +} + +static const struct berval* LDAP_C LDAP_CALLBACK +dsgw_keygen( void *arg, LDAP *ld, LDAPMessage *entry ) +{ + auto const char* sortbyattr = (char*)arg; + auto struct berval* result = NULL; + + if (sortbyattr == NULL) { /* sort by DN */ + auto char* DN = ldap_get_dn( ld, entry ); + if (DN) { + result = dsgw_strkeygen( CASE_INSENSITIVE, DN ); + ldap_memfree( DN ); + } + } else { + auto char* attr; + auto BerElement *ber; + for (attr = ldap_first_attribute( ld, entry, &ber ); attr != NULL; + attr = ldap_next_attribute ( ld, entry, ber ) ) { + auto char **vals; + if ( is_subtype( attr, sortbyattr ) && + NULL != ( vals = ldap_get_values( ld, entry, attr ))) { + auto size_t i; + for ( i = 0; vals[i] != NULL; ++i ) { + auto struct berval* key = dsgw_strkeygen( CASE_INSENSITIVE, vals[i] ); + if ( result == NULL || dsgw_keycmp( NULL, key, result ) < 0 ) { + auto struct berval* tmp = result; + result = key; + key = tmp; +#ifdef DSGW_DEBUG + { + auto char* ev = dsgw_strdup_escaped( vals[i] ); + auto char* DN = ldap_get_dn( ld, entry ); + dsgw_log( "dsgw_keygen(%s,%s) %p %s\n", sortbyattr, DN, (void*)result, ev ); + ldap_memfree( DN ); + free( ev ); + } +#endif + } + if ( key != NULL ) { + dsgw_keyfree( arg, key ); + } + } + ldap_value_free( vals ); + } + ldap_memfree( attr ); + } + if ( ber != NULL ) { + ldap_ber_free( ber, 0 ); + } + } + return result ? result : /* no such attribute */ dsgw_key_last; +} + +static void +handle_search_results( dsgwtmplinfo *tip, LDAP *ld, int rc, LDAPMessage *msgp, + unsigned long options ) +{ + int count; + LDAPMessage *entry; + char *dn, *errortext, *lderrtxt, **ocvals; + + count = ( msgp == NULL ) ? 0 : ldap_count_entries( ld, msgp ); + if ( rc == LDAP_SUCCESS ) { + errortext = NULL; + lderrtxt = NULL; + } else { + errortext = dsgw_ldaperr2string( rc ); + (void)ldap_get_lderrno( ld, NULL, &lderrtxt ); + } + dsgw_set_search_result( tip, count, errortext, lderrtxt ); + + if ( count > 0 ) { + entry = ldap_first_entry( ld, msgp ); + + if ( count == 1 && ( options & DSGW_DISPLAY_OPT_LIST_IF_ONE ) == 0 ) { + /* found exactly one entry: read and display it */ + dn = ldap_get_dn( ld, entry ); + ocvals = ldap_get_values( ld, entry, DSGW_ATTRTYPE_OBJECTCLASS ); + ldap_msgfree( msgp ); + + dsgw_read_entry( ld, dn, ocvals, NULL, NULL, options ); + + if ( ocvals != NULL ) { + ldap_value_free( ocvals ); + } + return; + } + + /* list entries */ +#ifdef DSGW_DEBUG + dsgw_log( "handle_search_results: sort entries by %s\n", + tip->dsti_sortbyattr ? tip->dsti_sortbyattr : "DN" ); +#endif + ldap_keysort_entries( ld, &msgp, tip->dsti_sortbyattr, + dsgw_keygen, dsgw_keycmp, dsgw_keyfree ); + for ( entry = ldap_first_entry( ld, msgp ); entry != NULL; + entry = ldap_next_entry( ld, entry )) { + dsgw_display_entry( tip, ld, entry, NULL, NULL ); + } + if ( options & DSGW_DISPLAY_OPT_DNLIST_JS ) { + int i; + char *edn, *js0, *js1; + char **xdn; + char **sn; + + dsgw_emits( "<SCRIPT LANGUAGE=\"JavaScript\">\n" ); + dsgw_emits( "var dnlist = new Array;\n" ); + for ( i = 0, entry = ldap_first_entry( ld, msgp ); entry != NULL; + i++, entry = ldap_next_entry( ld, entry )) { + dn = ldap_get_dn( ld, entry ); + edn = dsgw_strdup_escaped( dn ); + xdn = ldap_explode_dn( dn, 1 ); + dsgw_emitf( "dnlist[%d] = new Object\n", i ); + dsgw_emitf( "dnlist[%d].edn = '%s';\n", i, edn ); + js0 = dsgw_escape_quotes( xdn[ 0 ] ); + if ( xdn[1] != NULL ) { + js1 = dsgw_escape_quotes( xdn[ 1 ] ); + dsgw_emitf( "dnlist[%d].rdn = '%s, %s';\n", i, js0, js1 ); + free( js1 ); + } else { + dsgw_emitf( "dnlist[%d].rdn = '%s';\n", i, js0 ); + } + free( js0 ); + if (( sn = ldap_get_values( ld, entry, "sn" )) == NULL ) { + js0 = NULL; + } else { + js0 = dsgw_escape_quotes( sn[ 0 ] ); + ldap_value_free( sn ); + } + dsgw_emitf( "dnlist[%d].sn = '%s';\n", i, ( js0 == NULL ) ? + " " : js0 ); + if ( js0 != NULL ) { + free( js0 ); + } + + dsgw_emitf( "dnlist[%d].selected = false;\n", i ); + free( edn ); + ldap_value_free( xdn ); + ldap_memfree( dn ); + } + dsgw_emitf( "dnlist.count = %d;\n", i ); + dsgw_emitf( "</SCRIPT>\n" ); + } + ldap_msgfree( msgp ); + } else { + /* Count <= 0 */ + if ( options & DSGW_DISPLAY_OPT_DNLIST_JS ) { + dsgw_emitf( "<SCRIPT LANGUAGE=\"JavaScript\">\n" ); + dsgw_emitf( "var dnlist = new Array;\n" ); + dsgw_emitf( "dnlist.count = 0;\n" ); + dsgw_emitf( "</SCRIPT>\n" ); + } + } + + dsgw_display_done( tip ); +} + + +/* + * read and display a single entry. If ocvals is non-NULL, it should + * contain the list of objectClass values for this entry. + */ +void +dsgw_read_entry( LDAP *ld, char *dn, char **ocvals, char *tmplname, + char **attrs, unsigned long options ) +{ + int rc, one_attr, freeocvals, valindex; + char *tmpattr, *attr0, *mimetype; + LDAPMessage *msgp, *entry, *aomsgp, *aoentry; + dsgwtmpl *tmpl; + dsgwtmplinfo *tip; + + if (( options & DSGW_DISPLAY_OPT_AUTH ) != 0 ) { + /* + * XXX hack -- if we are trying to authenticate, we don't generate an + * entry display at all. Instead, we generate an authenticate form. + */ + dsgw_send_header(); + dsgw_emit_auth_form( dn ); + return; + } + + one_attr = ( attrs != NULL && attrs[ 0 ] != NULL && attrs[ 1 ] == NULL ); + if ( one_attr ) { + break_up_one_attr( attrs[ 0 ], &tmpattr, &mimetype, &valindex ); + if ( strcasecmp( tmpattr, "_vcard" ) == 0 ) { /* VCards are special */ + dsgw_vcard_from_entry( ld, dn, mimetype ); + return; + } + attr0 = attrs[ 0 ]; /* replace first & only attr. */ + attrs[ 0 ] = tmpattr; + } else { + attr0 = NULL; + } + + if ( tmplname == NULL && ( tmplname = dsgw_get_cgi_var( "displaytemplate", + DSGW_CGIVAR_OPTIONAL )) == NULL && attrs == NULL ) { + /* determine what display template to use based on objectClass values */ + freeocvals = 0; + if ( ocvals == NULL ) { /* read entry to get objectClasses */ + char *attrs[ 2 ]; + + attrs[ 0 ] = DSGW_ATTRTYPE_OBJECTCLASS; + attrs[ 1 ] = NULL; + + if (( rc = ldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", + attrs, 0, &msgp )) != LDAP_SUCCESS || + ( entry = ldap_first_entry( ld, msgp )) == NULL ) { + dsgw_ldap_error( ld, DSGW_ERROPT_EXIT ); + } + ocvals = ldap_get_values( ld, msgp, DSGW_ATTRTYPE_OBJECTCLASS ); + freeocvals = 1; + ldap_msgfree( msgp ); + } + + + if ( ocvals == NULL || ( tmpl = dsgw_oc2template( ocvals )) == NULL ) { + tmplname = NULL; + } else { + tmplname = tmpl->dstmpl_name; + } + + if ( freeocvals ) { + ldap_value_free( ocvals ); + } + } + + if ( tmplname == NULL ) { + tip = NULL; + + if ( !one_attr ) { + char *title; + + if (( title = ldap_dn2ufn( dn )) == NULL ) { + title = dn; + } + dsgw_send_header(); + dsgw_html_begin( title, 1 ); + dsgw_emitf( "<FONT SIZE=\"+1\">\n%s\n</FONT>\n", + XP_GetClientStr(DBT_noteThereIsNoDisplayTemplateForT_) ); + } + + } else if (( tip = dsgw_display_init( DSGW_TMPLTYPE_DISPLAY, tmplname, + options )) != NULL ) { + dsgw_send_header(); + attrs = tip->dsti_attrs; + } + + /* now read the attributes needed for the template */ + if (( rc = ldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", + attrs, 0, &msgp )) != LDAP_SUCCESS ) { + dsgw_ldap_error( ld, DSGW_ERROPT_EXIT ); + } + + if (( entry = ldap_first_entry( ld, msgp )) == NULL ) { + ldap_msgfree( msgp ); + dsgw_ldap_error( ld, DSGW_ERROPT_EXIT ); + } + + /* and retrieve attribute types only if we need any of them */ + if ( one_attr || tip == NULL || tip->dsti_attrsonly_attrs == NULL ) { + aomsgp = NULL; + } else { + if (( rc = ldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*", + tip->dsti_attrsonly_attrs, 1, &aomsgp )) != LDAP_SUCCESS ) { + dsgw_ldap_error( ld, DSGW_ERROPT_EXIT ); + } + + /* + * if no entries were returned, "aoentry" will be set to NULL by the + * next statement. We don't treat that as an error since we know the + * entry exists. It probably just means none of the "attrsonly" types + * were present in the entry. + */ + aoentry = ldap_first_entry( ld, aomsgp ); + } + + /* display it (finally!) */ + if ( one_attr ) { + return_one_attr( ld, entry, attrs[ 0 ], mimetype, valindex ); + } else if ( tip == NULL ) { + /* no template available -- display in an ugly but complete manner */ + if (( rc = ldap_entry2html( ld, NULL, entry, NULL, NULL, NULL, + entry2htmlwrite, stdout, "\n", 0, LDAP_DISP_OPT_HTMLBODYONLY, + NULL, NULL )) != LDAP_SUCCESS ) { + dsgw_ldap_error( ld, DSGW_ERROPT_EXIT ); + } + dsgw_html_end(); + } else { + /* use template to create a nicely formatted display */ + dsgw_display_entry( tip, ld, entry, aoentry, NULL ); + dsgw_display_done( tip ); + } + + if ( attr0 != NULL ) { + attrs[ 0 ] = attr0; /* if we replaced this, put original back */ + } + + if ( msgp != NULL ) { + ldap_msgfree( msgp ); + } + if ( aomsgp != NULL ) { + ldap_msgfree( aomsgp ); + } +} + + +/* + * return 1 if the entry already exists, 0 if not, -1 if some error occurs + */ +int +dsgw_ldap_entry_exists( LDAP *ld, char *dn, char **matchedp, + unsigned long erropts ) +{ + LDAPMessage *msgp; + int rc; + + msgp = NULL; + if ( matchedp != NULL ) { + *matchedp = NULL; + } + + if (( rc = do_search( NULL, ld, dn, LDAP_SCOPE_BASE, "(objectClass=*)", + &msgp )) != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT ) { + dsgw_ldap_error( ld, erropts ); + } + + if ( msgp == NULL || rc == LDAP_NO_SUCH_OBJECT ) { + rc = 0; + if ( matchedp != NULL ) { + (void)ldap_get_lderrno( ld, matchedp, NULL ); + } + } else { + rc = ( ldap_count_entries( ld, msgp ) > 0 ? 1 : 0 ); + ldap_msgfree( msgp ); + } + + return( rc ); +} + + +static int +entry2htmlwrite( void *fp, char *buf, int len ) +{ + return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len ); +} + + +/* + * return 1 if the entry's parent exists, 0 if not, -1 if some error occurs. + * If the entry is the same as gc->gc_ldapsearchbase, then we return 1, + * so we don't prevent people from adding their organizational entry. + */ +int +dsgw_ldap_parent_exists( LDAP *ld, char *dn, unsigned long erropts ) +{ + LDAPMessage *msgp; + int rc; + + /* Is "dn" == gc->gc_ldapsearchbase? */ + msgp = NULL; + if (( rc = do_search( NULL, ld, dn, LDAP_SCOPE_BASE, "(objectClass=*)", + &msgp )) != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT ) { + dsgw_ldap_error( ld, erropts ); + } + + if ( msgp == NULL ) { + rc = 0; + } else { + rc = ( ldap_count_entries( ld, msgp ) > 0 ? 1 : 0 ); + ldap_msgfree( msgp ); + } + + return( rc ); +} + + + +/* + * this function is called back by LIBLDAP when chasing referrals + */ +static int LDAP_CALL LDAP_CALLBACK +get_rebind_credentials( LDAP *ld, char **whop, char **credp, + int *methodp, int freeit, void *arg ) +{ + if ( !freeit ) { + *whop = binddn; + *credp = bindpasswd; + *methodp = LDAP_AUTH_SIMPLE; + } + + return( LDAP_SUCCESS ); +} + + +char * +dsgw_get_binddn() +{ + return( binddn ); +} + +/* + * return 1 if bound using "dn" + * return 0 if definitely bound as someone else + * return "def_answer" is we can't tell for sure + */ +int +dsgw_bound_as_dn( char *dn, int def_answer ) +{ + int i, rc; + char **rdns1, **rdns2; + + if ( binddn == NULL ) { + /* + * not authenticated: if not using local db or using it as an + * end-user, return the default + */ + if ( gc->gc_localdbconf == NULL || gc->gc_enduser ) { + return( def_answer ); + } + + /* + * if using local db as an admin, return "bound as someone else" + * since there is no access control enforced anyways. + */ + return( 0 ); + } + + /* first try a simple case-insensitive comparison */ + if ( strcasecmp( binddn, dn ) == 0 ) { + return( 1 ); /* DNs are the same */ + } + + /* + * These DNs may not have the same spacing or punctuation. Compare RDN + * components to eliminate any differences. + */ + if (( rdns1 = ldap_explode_dn( binddn, 0 )) == NULL ) { + return( def_answer ); /* we don't know: return the default */ + } + + if (( rdns2 = ldap_explode_dn( dn, 0 )) == NULL ) { + ldap_value_free( rdns1 ); + return( def_answer ); /* we don't know: return the default */ + } + + for ( i = 0; rdns1[ i ] != NULL && rdns2[ i ] != NULL; ++i ) { + if ( strcasecmp( rdns1[ i ], rdns2[ i ] ) != 0 ) { + break; /* DNs are not the same */ + } + } + + rc = ( rdns1[ i ] == NULL && rdns2[ i ] == NULL ); + + ldap_value_free( rdns1 ); + ldap_value_free( rdns2 ); + + return( rc ); +} + + + +/* + * Compare 2 DNs. Return 1 if they are equivalent, 0 if not. + */ +int +dsgw_dn_cmp( char *dn1, char *dn2 ) +{ + int i, rc; + char **rdns1, **rdns2; + + /* first try a simple case-insensitive comparison */ + if ( dsgw_utf8casecmp( (unsigned char *)dn1, (unsigned char *)dn2 ) == 0 ) { + return( 1 ); /* DNs are the same */ + } + + /* + * These DNs may not have the same spacing or punctuation. Compare RDN + * components to eliminate any differences. + */ + if (( rdns1 = ldap_explode_dn( dn1, 0 )) == NULL ) { + return( 0 ); /* we don't know: return 0 */ + } + + if (( rdns2 = ldap_explode_dn( dn2, 0 )) == NULL ) { + ldap_value_free( rdns1 ); + return( 0 ); /* we don't know: return 0 */ + } + + for ( i = 0; rdns1[ i ] != NULL && rdns2[ i ] != NULL; ++i ) { + if ( dsgw_utf8casecmp( (unsigned char *)rdns1[ i ], (unsigned char *)rdns2[ i ] ) != 0 ) { + break; /* DNs are not the same */ + } + } + + rc = ( rdns1[ i ] == NULL && rdns2[ i ] == NULL ); + + ldap_value_free( rdns1 ); + ldap_value_free( rdns2 ); + + return( rc ); +} + + +/* + * Return the parent of dn. The caller is responsible for freeing the + * returned value. Returns NULL on error. + */ +char * +dsgw_dn_parent( char *dn ) +{ + char *dnp; + int i; + char **rdns; + + if ( dn == NULL ) { + return( NULL ); + } + + dnp = dsgw_ch_malloc( strlen( dn )); + dnp[ 0 ] = '\0'; + if (( rdns = ldap_explode_dn( dn, 0 )) == NULL ) { + return NULL; + } + for ( i = 1; rdns[ i ] != NULL; i++ ) { + strcat( dnp, rdns[ i ] ); + strcat( dnp, "," ); + } + /* Get rid of the trailing "," we just appended */ + dnp[ strlen( dnp ) - 1 ] = '\0'; + ldap_value_free( rdns ); + return( dnp ); +} + + +/* + * Return 1 if dn1 is the immediate ancestor of dn2, 0 otherwise. + */ +int +dsgw_is_dnparent( char *dn1, char *dn2 ) +{ + char *dnp; + int rc; + + /* A null or zero-length DN cannot have a parent */ + if ( dn2 == NULL || strlen( dn2 ) == 0 ) { + return 0; + } + + dnp = dsgw_dn_parent( dn2 ); + rc = dsgw_dn_cmp( dn1, dnp ); + free( dnp ); + + return rc; +} + + +/* + * return malloc'd array of RDN attribute value pairs + * each element of the array is a string that looks like: TAG=VALUE + * this is used to extract values from the RDN when a new entry is added + */ +char ** +dsgw_rdn_values( char *dn ) +{ + char **rdns, **rdncomps, *val; + int i; + + if (( rdns = ldap_explode_dn( dn, 0 )) == NULL ) { + return( NULL ); + } + + rdncomps = ldap_explode_rdn( rdns[0], 0 ); + ldap_value_free( rdns ); + if ( rdncomps == NULL ) { + return( NULL ); + } + + for ( i = 0; rdncomps[ i ] != NULL; ++i ) { + if (( val = strchr( rdncomps[ i ], '=' )) == NULL ) { + ldap_value_free( rdncomps ); + return( NULL ); + } + ++val; + strcpy_special_undo( val, val ); /* undo in place */ + } + + return( rdncomps ); +} + + +/* + * the following routine was lifted from servers/slapd/ava.c + * it removes special quoting, etc. from values that appear in an LDAP DN + */ +static void +strcpy_special_undo( char *d, char *s ) +{ + int quote; + + quote = 0; + if ( *s == '"' ) { + s++; + quote = 1; + } + for ( ; *s; LDAP_UTF8INC(s)) { + switch ( *s ) { + case '"': + break; + case '\\': + s++; + /* FALL */ + default: + d += LDAP_UTF8COPY (d, s); + break; + } + } + *d = '\0'; LDAP_UTF8DEC(d); + if ( quote && *d == '"' ) { + *d = '\0'; + } +} + + +static char * +uid2dn( LDAP *ld, char *uid, char *base, int *ldaprc, char **lderrtxtp, + char **errsp ) +{ + char *attrs[] = { "objectclass", NULL }; + char filtbuf[ 85 ]; /* max of 80 char. uid + "uid=" + zero terminator */ + int rc, count; + LDAPMessage *result; + LDAPMessage *e; + char *dn; + + *ldaprc = LDAP_SUCCESS; /* optimistic */ + *errsp = *lderrtxtp = NULL; + + if ( ld == NULL || uid == NULL || strlen( uid ) > 80 ) { + *errsp = XP_GetClientStr(DBT_invalidUserIdOrNullLdapHandle_); + return NULL; + } + PR_snprintf( filtbuf, 85, "uid=%s", uid ); + + if (( rc = ldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filtbuf, + attrs, 1, &result )) != LDAP_SUCCESS ) { + *ldaprc = rc; + (void)ldap_get_lderrno( ld, NULL, lderrtxtp ); + return NULL; + } + if (( count = ldap_count_entries( ld, result )) != 1 ) { + /* Search either returned no entries, or more than one entry */ + ldap_msgfree( result ); + if ( count == 0 ) { + *errsp = XP_GetClientStr(DBT_noMatchForUserId_); + } else { + *errsp = XP_GetClientStr(DBT_moreThanOneMatchForUserId_); + } + return NULL; + } + + dn = NULL; + if (( e = ldap_first_entry( ld, result )) == NULL || + ( dn = ldap_get_dn( ld, e )) == NULL ) { + *ldaprc = ldap_get_lderrno( ld, NULL, NULL ); + } + ldap_msgfree( result ); + return( dn ); +} + + +/* + * Emit an HTML "SELECT" object that contains all the o's and ou's that + * are underneath our default searchbase. If there are none other than + * the searchbase, we emit a hidden HTML TEXT object that contains the + * searchbase and the "prefix" and "suffix" are not used. The values for + * the SELECT options and for the TEXT object are all escaped DNs. + * + * Location popup directives look like this: + * <-- DS_LOCATIONPOPUP "name=VARNAME" "prefix=PREFIX" "suffix=SUFFIX" --> + * + * If "prefix" and/or "suffix" are omitted, they default to "". + * If "name" is omitted it defaults to "base". + * + * If there are "location" directives in the dsgw.conf file, we use those + * instead of actually searching the directory. + */ +void +dsgw_emit_location_popup( LDAP *ld, int argc, char **argv, int erropts ) +{ + char line[BIG_LINE]; + char *varname, *prefix, *suffix, *rootname, *dn; + int i, count, did_init_ldap; + LDAPMessage *res, *e; + + if (( varname = get_arg_by_name( "name", argc, argv )) == NULL ) { + varname = "base"; + } + if (( prefix = get_arg_by_name( "prefix", argc, argv )) == NULL ) { + prefix = ""; + } + if (( suffix = get_arg_by_name( "suffix", argc, argv )) == NULL ) { + suffix = ""; + } + rootname = get_arg_by_name( "rootname", argc, argv ); + + did_init_ldap = 0; + res = NULL; + + if ( gc->gc_newentryloccount > 0 ) { + count = gc->gc_newentryloccount; + } else { + char *attrs[ 3 ]; + int rc; + + if ( ld == NULL ) { + (void)dsgw_init_ldap( &ld, NULL, 0, 0 ); + did_init_ldap = 1; + } + attrs[ 0 ] = "o"; + attrs[ 1 ] = "ou"; + attrs[ 2 ] = NULL; + + rc = ldap_search_s( ld, gc->gc_ldapsearchbase, LDAP_SCOPE_SUBTREE, + "(|(objectclass=organization)(objectclass=organizationalunit))", + attrs, 1, &res ); + if ( rc != LDAP_SUCCESS || res == NULL ) { + dsgw_ldap_error( ld, erropts ); + return; + } + + count = ldap_count_entries( ld, res ); + if ( gc->gc_ldapsearchbase == NULL || *gc->gc_ldapsearchbase == '\0' ) { + ++count; /* include base DN even if it is "" */ + } else { + /* + * check to see if search base was one of the entries returned + * we want to always list the base entry, so we need to check + */ + for ( e = ldap_first_entry( ld, res ); e != NULL; + e = ldap_next_entry( ld, e )) { + if (( dn = ldap_get_dn( ld, e )) == NULL ) { + dsgw_ldap_error( ld, erropts ); + ldap_msgfree( res ); + return; + } + + rc = dsgw_dn_cmp( dn, gc->gc_ldapsearchbase ); + free( dn ); + if ( rc ) { /* base DN was returned */ + break; + } + } + if ( e == NULL ) { + ++count; /* include base DN even if was not returned */ + } + } + } + + if ( count > 1 ) { + util_snprintf( line, BIG_LINE, "%s\n<SELECT NAME=\"%s\">\n", + prefix, varname ); + } else { + util_snprintf( line, BIG_LINE, "<INPUT TYPE=\"hidden\" NAME=\"%s\" ", + varname ); + } + dsgw_emits( line ); + + if ( gc->gc_newentryloccount > 0 ) { + for ( i = 0; i < gc->gc_newentryloccount; ++i ) { + emit_one_loc_dn( gc->gc_newentrylocs[ i ].dsloc_dnsuffix, + gc->gc_newentrylocs[i].dsloc_fullname, rootname, + ( count < 2 )); + } + } else { + /* always include the base dn first */ + emit_one_loc_dn( gc->gc_ldapsearchbase, NULL, rootname, ( count < 2 )); + + /* XXXmcs it would be nice to do a more intelligent sort here */ +#ifdef DSGW_DEBUG + dsgw_log( "dsgw_emit_location_popup: ldap_sort_entries(NULL)\n" ); +#endif + ldap_sort_entries( ld, &res, NULL, dsgw_strcmp (CASE_INSENSITIVE)); + + for ( e = ldap_first_entry( ld, res ); e != NULL; + e = ldap_next_entry( ld, e )) { + if (( dn = ldap_get_dn( ld, e )) == NULL ) { + dsgw_ldap_error( ld, erropts ); + ldap_msgfree( res ); + return; + } + + if ( !dsgw_dn_cmp( dn, gc->gc_ldapsearchbase )) { + emit_one_loc_dn( dn, NULL, rootname, ( count < 2 )); + } + free( dn ); + } + } + + if ( count > 1 ) { + util_snprintf( line, BIG_LINE, "</SELECT>\n%s\n", suffix ); + dsgw_emits( line ); + } + + if ( res != NULL ) { + ldap_msgfree( res ); + } + if ( did_init_ldap ) { + ldap_unbind( ld ); + } +} + + +static void +emit_one_loc_dn( char *dn, char *friendlyname, char *rootname, int only_one ) +{ + char *escapeddn, **rdns, line[ BIG_LINE ]; + + rdns = NULL; + escapeddn = dsgw_strdup_escaped( dn ); + + if ( !only_one ) { + dsgw_emits( "<OPTION" ); + } + + if ( friendlyname == NULL ) { /* use first component of DN */ + if ( *dn == '\0' ) { + friendlyname = ( rootname == NULL ? XP_GetClientStr(DBT_theEntireDirectory_) + : rootname ); + } else if (( rdns = ldap_explode_dn( dn, 1 )) == NULL + || rdns[ 0 ] == NULL ) { + friendlyname = dn; + } else { + friendlyname = rdns[ 0 ]; + } + } + + util_snprintf( line, BIG_LINE, " VALUE=\"%s\">%s\n", escapeddn, + only_one ? "" : friendlyname ); + free( escapeddn ); + if ( rdns != NULL ) { + ldap_value_free( rdns ); + } + dsgw_emits( line ); +} + + +/* + * Return a MIME document that contains a single value. + * XXX: does this really belong in ldaputil.c? + */ +static void +return_one_attr( LDAP *ld, LDAPMessage *entry, char *attrtype, char *mimetype, + int valindex ) +{ + char *val; + struct berval **bvals; + unsigned long vlen; + + if (( bvals = ldap_get_values_len( ld, entry, attrtype )) == NULL ) { + dsgw_error( DSGW_ERR_NOATTRVALUE, attrtype, DSGW_ERROPT_EXIT, 0, NULL ); + } + + if ( valindex > ldap_count_values_len( bvals )) { + dsgw_error( DSGW_ERR_NOATTRVALUE, attrtype, DSGW_ERROPT_EXIT, 0, NULL ); + } + + val = bvals[ valindex ]->bv_val; + vlen = bvals[ valindex ]->bv_len; + + fprintf( stdout, "Content-Type: %s\n", mimetype ); + fprintf( stdout, "Content-Length: %ld\n\n", vlen ); + +#ifdef XP_WIN32 + /* flush any data on stdout before changing the mode */ + fflush( stdout ); + + /* set the mode to binary + so windows doesn't replace with carriage + return line feed and mess everything up + */ + _setmode( _fileno( stdout ), _O_BINARY ); +#endif + + fwrite( val, vlen, 1, stdout ); + +#ifdef XP_WIN32 + /* flush any remaining binary data */ + fflush( stdout ); + + /* set the mode back to text */ + _setmode( _fileno( stdout ), _O_TEXT ); +#endif + + ldap_value_free_len( bvals ); + free( attrtype ); +} + + +/* + * The general format of attrtype is: + * <attrtype> [ &<mimetype> ] [ &<valindex> ] + * This routine breaks it up. Callers should free( *attrtypep ) after they + * are done using attrtypep and mimetypep. + */ +static void +break_up_one_attr( char *attr, char **attrtypep, char **mimetypep, + int *valindexp ) +{ + char *p; + + *attrtypep = dsgw_ch_strdup( attr ); + + *mimetypep = "text/plain"; /* default */ + *valindexp = 0; /* default: retrieve first value */ + + if (( p = strchr( *attrtypep, '&' )) != NULL ) { + *p++ = '\0'; + if ( *p != '\0' ) { + *mimetypep = p; + if (( p = strchr( *mimetypep, '&' )) != NULL ) { + *p++ = '\0'; + *valindexp = atoi( p ); + } + } + } +} |