/* XDS_SEARCH_EXAMPLE_1.C V5.6-3 *%COPYRIGHT_START% * * Copyright Digital Equipment Corporation 1993, 1995. All rights reserved * * Restricted Rights: Use, duplication, or disclosure by the U.S Government is * subject to restrictions as set forth in subparagraph (C) (1) (ii) of DFARS * 252.227-7013, or in FAR 52.227-19, or in FAR 52.227-14 Alt, III, as * applicable. Unpublished rights reserved under applicable copyright laws. * * This software is proprietary to and embodies the confidential technology of * Digital Equipment Corporation. Possession, use, or copying of this software * and media is authorized only pursuant to a valid written license from * Digital or an authorized sublicensor. * *%COPYRIGHT_END% */ /* **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ** ** The following are trademarks of Digital Equipment Corporation: ** DEC, OpenVMS, ULTRIX. ** **------------------------------------------------------------------------ */ /* **++ ** FACILITY: X.500 XDSHLI Example Program ** ** MODULE DESCRIPTION: ** ** ** This module is part of an example program, and a simple X.500 Directory ** lookup utility. ** This module demonstrates how to write a simple XDS/OM application ** using the Digital X.500 XDS High Level Interface (XDSHLI) API. ** This module, with the XDSHLI modules, is also an example of ** how to write an XDS/OM application. ** ** ** See XDSHLI.H for more information about the XDSHLI API. ** ** Functional Overview: ** The program searches for a Person entry which matches a user-provided ** name, and returns the names found, and the telephone and location ** information about that person. If the search cannot be performed, ** or no information is returned, an appropriate error message is ** displayed. ** ** Pre-requisites: ** Before building and running the the program, the following conditions ** must be satisfied: ** - The Digital X.500 Directory Service API component must be installed ** and the post-installation tasks must be completed. See the ** installation documentation for details. ** - The application defaults must be configured to access the Directory. ** - The literal SEARCH_BASE defined in this module must be configured ** to name the entry in the Directory Information Tree where the ** search operations commence from. ** - A compiler which is ANSI 'c' compliant must be installed. ** ** Building: ** Building on an ULTRIX system: ** - Copy the sources from /usr/examples/dxd to a working directory ** - Build the image with the command: ** c89 -o search4 xds_search_example_1.c xdshli_build.c xdshli_name.c \ ** xdshli_filter.c xdshli_process.c xdshli_utils.c \ ** xdshli_export.c -lxds ** #(note:cc may not compile the program) ** ** Building on a Digital UNIX (formerly DEC OSF/1) system: ** - Copy the sources from /usr/examples/dxd to a working directory ** - Build the image with the command: ** cc -o search4 xds_search_example_1.c xdshli_build.c xdshli_name.c \ ** xdshli_filter.c xdshli_process.c xdshli_utils.c \ ** xdshli_export.c -lxds ** ** Building on an OpenVMS system: ** - Copy the sources from sys$examples:[dxd] to a working directory ** - Compile the sources with the command: ** $ cc xds_search_example_1.c, xdshli_build.c, xdshli_name.c, - ** xdshli_filter.c, xdshli_process.c, xdshli_utils.c, - ** xdshli_export.c ** ** - Link the image with one of the following commands: ** ** If the VAX C compiler was used, link using the following command: ** $ link/exe=search4 xds_search_example_1, xdshli_build, - ** xdshli_name, xdshli_filter, xdshli_process, xdshli_utils, - ** xdshli_export, sys$input:/opt ** sys$share:dxd$xds_shr.exe/share ** sys$share:vaxcrtl.exe/share ** ** ** If the DEC C compiler was used, link using the following command: ** $ link/exe=search4 xds_search_example_1, xdshli_build, - ** xdshli_name, xdshli_filter, xdshli_process, xdshli_utils, - ** xdshli_export, sys$input:/opt ** sys$share:dxd$xds_shr.exe/share ** sys$share:decc$shr.exe/share ** ** ** - Define a symbol to invoke the program: ** search4 :== $:[]search4.exe ** ** Invocation: ** The program is invoked from the command line by typing: ** ** search4 ** ** where is either a common name or a surname, and may contain ** wildcard characters ('*'). For example: ** prompt> search4 Francis Black ** or ** prompt> search4 F*Black ** or ** prompt> search4 Black ** ** Note: on non-OpenVMS platforms, values containing wildcards may have ** to be enclosed in quotes (eg "F*Black"). ** ** Output: ** For each entry that matches the user provided name, the following ** output is displayed... ** ** Matching entries are : ** Name : Francis BLack ** Telephone : 345-4454 ** Locality : New York ** ** ** AUTHORS: ** ** Ivor Davies ** ** CREATION DATE: 27-Jul-1993 ** ** MODIFICATION HISTORY: ** ** 000 27-Jul-1993 IRD Module Creation. ** 001 24-Aug-1993 IRD Fix some problems, and interface changes ** 002 26-Aug-1993 IRD Minor misc changes ** 003 27-Sep-1993 IRD Change call to dsX_add_filter_item() ** 004 13-Oct-1993 CMB Minor changes to comments ** 005 12-Nov-1993 IRD Change include statements and ammend comments **-- */ /* ** ** INCLUDE FILES ** */ #include #include #include #include "xdshli.h" /* Includes XDS/OM header files, defines prototypes, * and imports various required symbols. */ /* **++ ** DEFINITIONS **-- */ /* * Define the search base to be used in the searches. This should be * customised to reflect the Directory which is being searched. * Note that the string is specified in RFC1485 format (see XDSHLI.H). */ #ifndef SEARCH_BASE #define SEARCH_BASE "O=Abacus, C=US" #endif /* * Define the format string used to output the attribute names. */ #define ATTRIBUTE_FORMAT "\t%-10.10s:\t\t" /* * Define the labels for attribute types. */ #define NAME_LABEL "Name" #define TELEPHONE_LABEL "Telephone" #define LOCALITY_LABEL "Locality" #define VALUE_SEPARATOR ", " /* * Define other messages which are displayed. */ #define RESULTS_LABEL "Matching entries are :" #define INCOMPLETE_RESULTS "Only part of the Directory was searched." #define NO_MATCHING_ENTRIES "No matching entries were found." #define IN_ENTRY "Entry returned is" /* * Define Error messages. */ #define DEFAULT_ERROR "Unkown Error." #define NO_NAME_ERROR "Error: No name to search for." #define SEARCH_BASE_ERROR "Error: Search base is invalid." #define FILTER_ERROR "Error: Unable to create Search Filter." #define BIND_ERROR "Error: Unable to bind to Directory." #define SEARCH_ERROR "Error: Search Failed." #define REFERRAL_ERROR "Referral received from Directory." /* * Define the wildcard character. */ #define WILDCARD '*' /* * Prototpes of locally defined routines. */ static char *get_name( int argc, /* IN - no of arguments */ char * argv[] ) ; /* IN - array of arguments */ static OM_private_object perform_search( OM_workspace workspace, /* IN - OM workspace */ char * name ) ; /* IN - name to search for */ static OM_private_object make_filter( OM_workspace workspace, /* IN - OM workspace */ char * name ) ; /* IN - name to go in filter */ static void display_search_results( OM_private_object search_results ); /* IN - result of search*/ static void print_attribute( char * attr_label, /* IN - label name */ unsigned int num_values, /* IN - number of values*/ OM_descriptor * attr_value);/* IN - values */ /* main() **++ ** The main entry point gets the name from the command line, performs ** the search, and displays the results. **-- */ main( int argc, char *argv[] ) { OM_workspace workspace ; OM_private_object search_results ; char *name ; /* * Get name to search for from the command-line arguments. */ if ( name = get_name(argc, argv) ) { /* * Create the OM workspace. */ if ( workspace = ds_initialize() ) { /* * Perform the search. */ if ( search_results = perform_search(workspace, name) ) { /* * The search has successfully completed. Display the results, * and then delete them. */ display_search_results( search_results ) ; om_delete( search_results ); } /* * Shutdown the OM workspace. */ ds_shutdown( workspace ); } else { /* * Report error and exit. */ printf( "%s\n", DEFAULT_ERROR ); } /* * Free up memory containing the name. */ free( name ) ; } else { /* * Report error and exit. */ printf( "%s\n", NO_NAME_ERROR ); } /* * Use exit() to return to operating system. This overcomes the problem of * displaying the OpenVMS message "%NONAME-W-NOMSG, Message number 00000000". */ exit(EXIT_SUCCESS); } /* get_name() **++ ** This routine take the significant command line arguments, ** and appends them into a single string, which is returned. **-- */ static char *get_name( int argc, /* IN - no. of arguments */ char *argv[] ) /* IN - array of arguments */ { unsigned int i ; char* name = NULL ; if ( argc > 1 ) { /* * There is at least one argument to be used in the search. * Allocate a working buffer. */ name = (char*)malloc( sizeof(char) ); if ( name != NULL ) { *name = '\0' ; /* * Get the command line arguments and append them (with spaces) into * a buffer. The first argument (the image name) is of no interest. * All but the last argument have the space appended. */ for ( i = 1 ; i < (argc -1) ; i++ ) { /* * Reallocate the buffer to hold the existing string, this * argument, and a space. */ name = (char*)realloc(name, (strlen(name)+1+ strlen(argv[i])+1)); strcat( name, argv[i] ) ; strcat( name, " " ) ; } /* * Allocate space for the final argument, and do not append a space. */ name = (char*)realloc(name, (strlen(name)+1 + strlen(argv[i])) ) ; strcat( name, argv[argc-1] ) ; } } return name ; } /* perform_search() **++ ** This function performs a search, and returns the result from the ** Directory operation. If the search fails for any reason, NULL is returned. ** Implicit inputs are the SEARCH_BASE, and user-provided name to search ** for, and any error messages. **-- */ static OM_private_object perform_search( OM_workspace workspace, /* IN - workspace */ char * name ) /* IN - name */ { OM_private_object search_base, filter, selection, session, results = NULL; DS_status dsts = DS_SUCCESS ; char *error_message, *entry_name ; enum dsX_Error_Class class ; OM_enumeration problem ; /* * Declare and initialise an array with the attributes to be returned from * the search. */ OM_string *attr_selected[] = { &(DS_A_COMMON_NAME), &(DS_A_PHONE_NBR), &(DS_A_LOCALITY_NAME), OM_NO_MORE_TYPES } ; /* * Get the name of the entry that is the base of the search operation. * Call 'dsX_dn_string_to_object()' to convert the string to a * DS-DN OM object required for the search. */ if ( search_base = dsX_dn_string_to_object(workspace, SEARCH_BASE, &error_message, NULL) ) { /* * Create the filter expression which specifies the entries to * be returned in the results of the search. */ if ( filter = make_filter(workspace, name) ) { /* * Call 'dsX_make_selection()' to create the * Entry-Information-Selection OM object, to * return commonName, telephoneNumber, and localityName. */ if ( selection = dsX_make_selection(workspace, attr_selected) ) { /* * Establish a session to the Directory by calling ds_bind(). * DS_DEFAULT_SESSION instructs XDS/OM to use default information * in setting up the session. */ if ( (dsts = ds_bind(DS_DEFAULT_SESSION, workspace, &session)) != DS_SUCCESS) { /* * The return status from 'ds_bind()' is not DS_SUCCESS, * so is an error. This program is unable to bind to * the Directory. * * Display an error message. */ printf( "%s\n", BIND_ERROR ) ; /* * The status returned by 'ds_bind()' is an Error OM Object. * This contains information that will help diagnose the * problem. * Call 'dsX_process_error()' to get such an explanation. */ dsX_process_error(dsts, &class, &problem, &error_message, NULL); /* * Display the error message. */ if ( error_message != NULL ) { printf( "%s\n", error_message ); free(error_message) ; } /* * Delete the Error OM object. It is no longer required. */ om_delete( dsts ) ; } else { /* * Search the directory for the user-provided name. * Call 'ds_search()' to perform this operation, * using the session setup above, searching the * whole subtree below the entry specified in 'search_base', * for the entries which match 'filter', and * return the required attributes specified in 'selection'. */ if ( (dsts = ds_search( session, DS_DEFAULT_CONTEXT, search_base, DS_WHOLE_SUBTREE, filter, OM_TRUE, selection, &results, NULL )) != DS_SUCCESS ) { /* * There was an error in the search operation - * the status returned by 'ds_search()' was not * DS_SUCCESS. * Display an error message. */ printf( "%s\n", SEARCH_ERROR ) ; /* * The status returned by 'ds_search()' is an Error OM * Object. This contains information that will help * diagnose the problem. * Call 'dsX_process_error()' to get such an explanation. */ dsX_process_error( dsts, &class, &problem, &error_message, &entry_name); /* * If the error is a Referral, print out a message. * This example program will not attempt to follow * the Referral. */ if ( class == DSX_REFERRAL ) { printf( "%s\n", REFERRAL_ERROR ) ; } /* * Display the error message if one was returned. */ if ( error_message != NULL ) { printf( "%s\n", error_message ); free(error_message) ; } /* * Some errors return the name of the entry related * to the error. If an entry name was returned, display * this as well. */ if ( entry_name != NULL ) { printf( " %s %s\n", IN_ENTRY, entry_name ); free(entry_name) ; } /* * Delete the Error OM object. It is no longer required. */ om_delete( dsts ) ; } /* * Unbind the session, and delete the Session OM object. */ ds_unbind(session); om_delete( session ) ; } /* * Delete the Entry-Information-Selection OM object. */ om_delete( selection ) ; } /* * Delete the Filter OM object used in the search. */ om_delete( filter ) ; } else { /* * Unable to create filter; report error. */ printf( "%s\n", FILTER_ERROR ) ; } /* * Delete the DS-DN OM object which represent the search base. */ om_delete( search_base ) ; } else { /* * Unable to create search base. Report error and return. */ printf( "%s\n%s\n", SEARCH_BASE_ERROR, error_message ); free( error_message ) ; } return( results ) ; } /* make_filter() **++ ** This routine builds and returns the following filter, inserting ** the given name in the appropriate place. ** The filter expression is used to match entries for Persons whose ** name or surname matches the given name. ** ** ((commonName= OR surname=) AND objectClass=Person) ** ** This filter is created 'top-down', that is, the AND filter is created ** first, and then the OR filter is created. See XDSHLI.H for more ** information. **-- */ static OM_private_object make_filter( OM_workspace workspace, /* IN - OM workspace */ char * name ) /* IN - name to go in filter */ { OM_private_object AND_Filter, OR_Filter ; OM_value value ; /* * Create the AND filter by calling 'dsX_make_filter()'. */ if ( AND_Filter = dsX_make_filter(workspace, DS_AND) ) { /* * Create the Filter-Item OM object to represent the * 'objectClass=Person' expression, and insert it into the AND filter. * 'dsX_add_filter_item()' performs this operation. The syntax parameter * indicates the syntax of the value. */ value.string = DS_O_PERSON ; dsX_add_filter_item(AND_Filter, &(DS_A_OBJECT_CLASS), DS_EQUALITY, &value, OM_S_OBJECT_IDENTIFIER_STRING) ; /* * Create the OR filter and insert it into AND filter, by calling * 'dsX_add_filter()'. */ if ( OR_Filter = dsX_add_filter(AND_Filter, DS_OR) ) { /* * Create the Filter-Item OM objects to represent * 'surname=' and 'commonname= filter' expressions, * and insert these into the OR filter. * * NOTE: To demonstrate the use of dsX_add_substring_filter_item() * and dsX_add_filter_item(), this function will use the * former if the name contains substrings, and the latter if * they do not. Infact, dsX_add_substring_filter_item() * can be used for both. */ if ( strchr(name, WILDCARD) == 0 ) { /* * The name does not contain substrings. * * Call 'dsX_add_filter_item()' to create the required * Filter-Item OM objects, and insert them into the OR * filter. * * Set up the value input parameter to contain the name. * In this program the representation is ISO-Latin-1. * The syntax in the Directory of commonName is a choice of * TeletexString or PrintableString. * So, the call instructs XDS to convert the value from the * local string representation to TeletexString. */ value.string.length = strlen(name); value.string.elements = name ; dsX_add_filter_item(OR_Filter, &(DS_A_COMMON_NAME), DS_EQUALITY, &value, OM_S_TELETEX_STRING | OM_S_LOCAL_STRING) ; dsX_add_filter_item(OR_Filter, &(DS_A_SURNAME), DS_EQUALITY, &value, OM_S_TELETEX_STRING | OM_S_LOCAL_STRING) ; } else { /* * The name does contain substrings. * * Call 'dsX_add_substring_filter_item()' to create the required * Filter-Item OM objects, and insert them into the OR * filter. */ dsX_add_substring_filter_item(OR_Filter, &(DS_A_COMMON_NAME), name) ; dsX_add_substring_filter_item(OR_Filter, &(DS_A_SURNAME), name); } return( AND_Filter ) ; } /* * If control reaches here, the filter generation has failed. * Delete the AND_Filter, which will delete all the sub-objects * which have been created and added to it. */ om_delete( AND_Filter ) ; } return( NULL ) ; } /* display_search_results() **++ ** ** This function displays the Name, Telephone and LocalityName Attributes ** of the entries in the given search_results(). ** **-- */ static void display_search_results( OM_private_object search_results ) /* IN - search results */ { OM_public_object entry ; OM_value_position pos = 0; unsigned int num, entry_count, is_complete ; OM_descriptor *value ; /* * Call 'dsX_count_result_entries()' to determine whether any results were * returned, and whether the search was complete. */ dsX_count_result_entries( search_results, &entry_count, &is_complete ) ; if ( !is_complete ) { /* * Only part of the Directory was searched, so display a warning message. */ printf( "%s\n", INCOMPLETE_RESULTS ) ; } if ( entry_count == 0 ) { /* * No results were returned. Display a message. */ printf( "%s\n", NO_MATCHING_ENTRIES ) ; } else { printf( "%s\n", RESULTS_LABEL ) ; /* * For each entry in the the search results, display the appropriate * information. * Call 'dsX_get_result_entry()' to return each entry as an * Entry-Information OM public object. When there are no further * entries in the results, 'dsX_get_result_entry()' returns NULL. */ while ( (entry = dsX_get_result_entry(search_results, &pos)) != NULL) { /* * For each entry, display Common Name, Telephone Number, and Locality * information. Call 'dsX_find_attribute()' to find the required * attribute information in the given Entry-Information OM object. * If there are no values, nothing is displayed. */ /* * Find the commonName attribute in the entry. */ dsX_find_attribute(entry,&(DS_A_COMMON_NAME), &num, &value) ; /* * If there is one or more attribute values in the attribute, * display them. */ if ( num > 0 ) { print_attribute( NAME_LABEL, num, value ) ; } dsX_find_attribute(entry, &(DS_A_PHONE_NBR), &num, &value ) ; if ( num > 0 ) { print_attribute( TELEPHONE_LABEL, num, value ) ; } dsX_find_attribute(entry, &(DS_A_LOCALITY_NAME), &num, &value ) ; if ( num > 0 ) { print_attribute( LOCALITY_LABEL, num, value ) ; } /* * Delete the Entry-Information OM object. */ om_delete( entry ) ; /* * Separate this entry from the next. */ printf( "\n" ) ; } } } /* print_attribute() **++ ** This routine displays the given attribute name, and its values ** (separated by commas) on a single line to 'stdout'. ** Note that this routine only supports values which are of string syntax. **-- */ static void print_attribute( char * attr_label, /* IN - Attr label */ unsigned int num_values, /* IN - no of attr values */ OM_descriptor * attr_value ) /* IN - ptr to first value */ { unsigned int i, j ; /* * If there is one or more values, print out something. */ if ( num_values > 0 ) { /* * Print out attribute name. */ printf ( ATTRIBUTE_FORMAT, attr_label ) ; /* * Print out each value. If there are multiple values, separate * them with a comma and space. * (OM_string values are not null-terminated, * so the 'putchar' has to be used to output each character.) */ for( i = 0 ; i < (num_values - 1) ; i++, attr_value++ ) { for ( j = 0 ; j < attr_value->value.string.length; j++ ) { putchar( ((char*)attr_value->value.string.elements)[j] ) ; } printf( VALUE_SEPARATOR ) ; } /* * Print out final value, without a separator, but followed * with a . */ for ( j = 0 ; j < attr_value->value.string.length; j++ ) { putchar( ((char*)attr_value->value.string.elements)[j] ) ; } printf( "\n" ) ; } }