/**
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved
 * Use of this product is subject to license terms.
 */
/************************************************************

 saslclient.c

 This example LDAP client binds using a custom SASL mechanism.
 Use it with the testsaslbind.c server plug-in, which implements
 a sample SASL mechanism.

*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ldap.h"

/* Use our fake SASL mechanism.                                      */
#define MECH "my_sasl_mechanism"

/* Global variables for client connection information.
 * You may set these here or on the command line.                    */
static char * host = "localhost";      /* Server hostname            */
static int    port = 389;              /* Server port                */
/* Load <install-path>/ldif/Example.ldif
 * before trying the plug-in with this user.                         */
static char * user = "uid=tmorris,ou=people,dc=example,dc=com";
/* New value for userPassword                                        */
static char * npwd = "23skidoo";

/* Check for host, port, user and new password as arguments.         */
int get_user_args(int argc, char ** argv);

int
main(int argc, char ** argv)
{
    LDAP          * ld;                /* Handle to LDAP connection  */
    LDAPMod         modPW, * mods[2];  /* For modifying the password */
    char          * vals[2];           /* Value of modified password */
    struct berval   cred;              /* Client bind credentials    */
    struct berval * srvCred;           /* Server bind credentials    */
    int             ldapVersion;

    /* Use default hostname, server port, user, and new password
     * unless they are provided as arguments on the command line.    */
    if (get_user_args(argc, argv) != 0) return 1; /* Usage error     */
    
    /* Get a handle to an LDAP connection.                           */
    printf("Getting the handle to the LDAP connection...\n");
    if ((ld = ldap_init(host, port)) == NULL) {
        perror("ldap_init");
        return 1;
    }

    /* By default, the LDAP version is set to 2.                     */
    printf("Setting the version to LDAP v3...\n");
    ldapVersion = LDAP_VERSION3;
    ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);

    /* Authenticate using the example SASL mechanism.                */
    printf("Bind DN is %s...\n", user);
    printf("Binding to the server using %s...\n", MECH);
    cred.bv_val = "magic";
    cred.bv_len = sizeof("magic") - 1;
    if (ldap_sasl_bind_s(ld, user, MECH, &cred, NULL, NULL, &srvCred)) {
        ldap_perror(ld, "ldap_sasl_bind_s");
        return 1;
    }

    /* Display the credentials returned by the server.               */
    printf("Server credentials: %s\n", srvCred->bv_val);

    /* Modify the user's password.                                   */
    printf("Modifying the password...\n");
    modPW.mod_op     = LDAP_MOD_REPLACE;
    modPW.mod_type   = "userpassword";
    vals[0]          = npwd;
    vals[1]          = NULL;
    modPW.mod_values = vals; 

    mods[0] = &modPW; mods[1] = NULL;

    if (ldap_modify_ext_s(ld, user, mods, NULL, NULL)) {
        ldap_perror(ld, "ldap_modify_ext_s");
        return 1;
    }

    /* Finish up.                                                    */
    ldap_unbind(ld);
    printf("Modification was successful.\n");
    return 0;
}

/* Check for host, port, user and new password as arguments.         */
int
get_user_args(int argc, char ** argv)
{
    int    errflag   = 0;              /* Any unrecognized args?     */   
    int    i;
    char * option_id = NULL;           /* Which arg is this?         */
    
    /* Try to use the any arguments provided by the user. If some
     * of the defaults can be reused, so much the better. So keep
     * interpreting arguments until it is no longer possible,
     * displaying a usage message when not all arguments are used,
     * and displaying the list of the arguments recorded.            */
    for (i = 1; i+1 <= argc; i += 2) {
        option_id = argv[i];
        if (argv[i][0] == '-') {
            if ((strcmp(option_id, "-h") == 0) && (i+1 < argc)) {
                host = argv[i+1];
            } else if ((strcmp(option_id, "-p") == 0) && (i+1 < argc)) {
                port = atoi(argv[i+1]);
            } else if ((strcmp(option_id, "-D") == 0) && (i+1 < argc)) {
                user = argv[i+1];
            } else if ((strcmp(option_id, "-n") == 0) && (i+1 < argc)) {
                npwd = argv[i+1];
            } else {
                errflag = 1;
                fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
            }
        }
    }


    if (errflag) {
        fprintf(stderr, "USAGE:\n");
        fprintf(stderr, "\t%s -h host -p port -D \"bind DN\" -n new_pwd\n",
                argv[0]);
        return 1;
    }

    fprintf(stderr, "Using the following connection info:\n");
    fprintf(stderr, "\thost:    %s\n\tport:    %d\n"
                    "\tbind DN: %s\n\tnew pwd: %s\n",
            host, port, user, npwd);
    return 0;
}
