/**
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 * Portions Copyright 2003 Sun Microsystems, Inc. All Rights Reserved
 * Use of this product is subject to license terms.
 * Some preexisting portions Copyright 1997-2001 Netscape Communications Corp.
 * All rights reserved.
 */
/************************************************************

 pwreset.c

resets user password via RFC 3062 extended op
OID: 1.3.6.1.4.1.4204.1.11.1
stolen from reqextop.c

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

/* 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              */
static char * bind_DN  = "";   /* DN to bind as  */
static char * bind_pwd = "";   /* Password for bind DN     */

int saw_reqval_field=0;
struct berval old_pwd={0};
struct berval new_pwd={0};
struct berval user={0};

/* Check for connection info as command line arguments.            */
int get_user_args(int argc, char ** argv);

#define EXTOP_MOD_PWD "1.3.6.1.4.1.4203.1.11.1"
#define LDAP_TAG_MOD_PWD_ID	0x80	/* user id */
#define LDAP_TAG_MOD_PWD_OLD	0x81	/* old pwd */
#define LDAP_TAG_MOD_PWD_NEW	0x82	/* new pwd */
#define LDAP_TAG_MOD_PWD_GEN	0x80	/* server gen */

int
main(int argc, char ** argv)
{
  char          * oidresult;         /* OID in reply from server */
  struct berval   *modrequest;
  struct berval   *valresult;         /* Reply received           */
  BerElement *requestval;
  LDAP          * ld;                /* Handle to connection     */
  int             version;           /* LDAP version             */

  if (get_user_args(argc, argv) != 0) return 1; /* Usage error   */
  printf("Getting the handle to the LDAP connection...\n");
  if ((ld = ldap_init(host, port)) == NULL) {
    perror("ldap_init");
    ldap_unbind(ld);
    return 1;
  }
  /* Set the LDAP protocol version supported by the client
     to 3. (By default, this is set to 2. Extended operations
     are part of version 3 of the LDAP protocol.)                */
  printf("Resetting version to 3.0...\n");
  version = LDAP_VERSION3;
  ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
  
  /* Authenticate to the directory     */
  printf("Binding to the directory...\n");

  if (ldap_simple_bind_s(ld, bind_DN, bind_pwd) != LDAP_SUCCESS) {
    ldap_perror(ld, "ldap_simple_bind_s");
    ldap_unbind(ld);
    return 1;
  }

  if((requestval=der_alloc()) == NULL) {
    ldap_perror(ld, "der_alloc");
    ldap_unbind(ld);
    return 1;
  }
  /* rfc 3062 states user, old_pwd and new_pwd are optional */
  if(saw_reqval_field) {
    ber_printf(requestval, "{");
    if(user.bv_len != 0)
      ber_printf(requestval,"to",LDAP_TAG_MOD_PWD_ID,user.bv_val, user.bv_len);
    if(old_pwd.bv_len != 0)
      ber_printf(requestval,"to",LDAP_TAG_MOD_PWD_OLD,old_pwd.bv_val, old_pwd.bv_len);
    if(new_pwd.bv_len != 0)
      ber_printf(requestval,"to",LDAP_TAG_MOD_PWD_NEW,new_pwd.bv_val, new_pwd.bv_len);
    ber_printf(requestval,"}");
  } else { /* CR 6313807 */
    modrequest->bv_val=NULL;
    modrequest->bv_len=0;
  }
  if(ber_flatten(requestval, &modrequest) != 0) {
    ber_free(requestval, 1);
    ldap_perror(ld, "ber_flatten");
    ldap_unbind(ld);
    return 1;
  };
  printf( "Initiating the extended operation...\n" );
  if (ldap_extended_operation_s(ld, EXTOP_MOD_PWD ,modrequest,
				NULL,NULL, &oidresult, &valresult) != LDAP_SUCCESS) {
    ldap_perror(ld, "ldap_extended_operation_s failed: ");
    ldap_unbind(ld);
    return 1;
  } 
  if((valresult != NULL) && (valresult->bv_len > 0)) {
    BerElement *berEle;
    char *pw;
    unsigned long tag;
    berEle = ber_init(valresult);
    tag = ber_scanf( berEle, "{a}", &pw);
    printf("newpw: %s\n", pw);
    ber_free( berEle, 1 );
  }
  ber_bvfree(modrequest);
  ber_free(requestval, 1);

  /* Disconnect from the server. */
  ldap_unbind(ld);
  return 0;
}

/* Check for connection info as command line 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)) {
                bind_DN = argv[i+1];
            } else if ((strcmp(option_id, "-w") == 0) && (i+1 < argc)) {
                bind_pwd = argv[i+1];
            }  else if ((strcmp(option_id, "-o") == 0) && (i+1 < argc)) {
	      saw_reqval_field=1; 
	      old_pwd.bv_len=strlen(argv[i+1]);
	      old_pwd.bv_val = (void *)argv[i+1];
            }  else if ((strcmp(option_id, "-n") == 0) && (i+1 < argc)) {
	      saw_reqval_field=1;
	      new_pwd.bv_len=strlen(argv[i+1]);
	      new_pwd.bv_val = (void *)argv[i+1];
            }  else if ((strcmp(option_id, "-u") == 0) && (i+1 < argc)) {
	      saw_reqval_field=1;
	      user.bv_len=strlen(argv[i+1]);
	      user.bv_val = (void *)argv[i+1];
            }   
	    else {
	      errflag = 1;
	      fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
            }
        }
    }

    if (errflag) {
      fprintf(stderr, "\nPassword reset based on RFC 3062. Usage:\n");
      fprintf(stderr,
	      "%s -h [host] -p [port] -D [\"DN\"] -w [pw] -o [oldpw] -n [newpw] [-u user]\n",
	      argv[0]);
      fprintf(stderr, "\t -h host (default localhost) \n");
      fprintf(stderr, "\t -p port (default port 389) \n");
      fprintf(stderr, "\t -D DN (default anonymous bind) \n");
      fprintf(stderr, "\t -w password (required with -D option) \n");
      fprintf(stderr, "\t -u user (optional) \n");
      fprintf(stderr, "\t -o old password (optional)  \n");
      fprintf(stderr, "\t -n new password (optional)  \n");

      return 1;
    } 

    fprintf(stderr, "Using the following connection info:\n");

        fprintf(stderr, "\thost:    %s\n\tport:    %d\n"
		"\tbind DN: %s\n\tpwd:    %s\n"
		"\toldpwd: %s\n\tnewpwd: %s\n\tuser: %s\n",
		host, port, bind_DN, bind_pwd,
		old_pwd.bv_len ? old_pwd.bv_val : "NULL",
		new_pwd.bv_len ? new_pwd.bv_val : "NULL",
		user.bv_len ? user.bv_val : "NULL");

    return 0;
}
