/* -*- C -*-
 * $Id: misc.c,v 1.3 2002/12/21 22:05:45 ttate Exp $
 */

#include "ruby.h"
#include "rbldap.h"

VALUE rb_sLDAP_APIInfo;
VALUE rb_cLDAP_Control;

#ifdef LDAP_OPT_API_INFO
VALUE
rb_ldap_apiinfo_new(LDAPAPIInfo *info)
{
  VALUE info_version, api_version, protocol_version;
  VALUE extensions, vendor_name, vendor_version;
  int i;

  info_version     = INT2NUM(info->ldapai_info_version);
  api_version      = INT2NUM(info->ldapai_api_version);
  protocol_version = INT2NUM(info->ldapai_protocol_version);
  vendor_version   = INT2NUM(info->ldapai_vendor_version);
  vendor_name      = rb_tainted_str_new2(info->ldapai_vendor_name);
  extensions       = rb_ary_new();

  for( i=0; info->ldapai_extensions[i]; i++ ){
    rb_ary_push(extensions, rb_tainted_str_new2(info->ldapai_extensions[i]));
  }

  return rb_struct_new(rb_sLDAP_APIInfo,
		       info_version, api_version, protocol_version,
		       extensions, vendor_name, vendor_version, 0);
}

LDAPAPIInfo *
rb_ldap_get_apiinfo(VALUE data)
{
  LDAPAPIInfo *info;
  VALUE r_extensions;
  int len, i;
  char **c_extensions;

  if( data == Qnil )
    return NULL;
  
  info = ALLOC_N(LDAPAPIInfo, 1);
  info->ldapai_info_version =
    FIX2INT(rb_struct_getmember(data, rb_intern("info_version")));
  info->ldapai_api_version =
    FIX2INT(rb_struct_getmember(data, rb_intern("api_version")));
  info->ldapai_protocol_version =
    FIX2INT(rb_struct_getmember(data, rb_intern("protocol_version")));
  r_extensions = rb_struct_getmember(data, rb_intern("extensions"));
  len = RARRAY(r_extensions)->len;
  c_extensions = ALLOCA_N(char*, len);
  for( i=0; i<=len-1; i++ ){
    VALUE str = RARRAY(r_extensions)->ptr[i];
    RB_LDAP_SET_STR(c_extensions[i], str);
  }
  info->ldapai_extensions = c_extensions;
  RB_LDAP_SET_STR(info->ldapai_vendor_name,
		  rb_struct_getmember(data, rb_intern("vendor_name")));
  info->ldapai_vendor_version =
    FIX2INT(rb_struct_getmember(data, rb_intern("vendor_version")));

  return info;
}
#endif /* LDAP_OPT_API_INFO */

#ifdef HAVE_LDAPCONTROL
static void
rb_ldap_control_free(LDAPControl *ctl)
{
  if( ctl ){
    if( ctl->ldctl_value.bv_val )
      xfree(ctl->ldctl_value.bv_val);
    if( ctl->ldctl_oid )
      xfree(ctl->ldctl_oid);
    xfree(ctl);
  }
}

VALUE
rb_ldap_control_new(LDAPControl *ctl)
{
  int len;
  VALUE val, oid, critical;

  if( !ctl ){
    return Qnil;
  }
  else{
    return Data_Wrap_Struct(rb_cLDAP_Control, 0, rb_ldap_control_free, ctl);
  }
}

static VALUE
rb_ldap_control_s_allocate(VALUE klass)
{
  LDAPControl *ctl;

  ctl = ALLOC_N(LDAPControl, 1);
  ctl->ldctl_value.bv_val = NULL;
  ctl->ldctl_value.bv_len = 0;
  ctl->ldctl_oid = NULL;
  ctl->ldctl_iscritical = 0;
  return Data_Wrap_Struct(klass, 0, rb_ldap_control_free, ctl);
}

static VALUE
rb_ldap_control_s_new(int argc, VALUE argv[], VALUE klass)
{
  VALUE obj;

  obj = rb_ldap_control_s_allocate(klass);
  rb_obj_call_init(obj, argc, argv);

  return obj;
}

static VALUE
rb_ldap_control_set_value(VALUE self, VALUE val)
{
  LDAPControl *ctl;

  Data_Get_Struct(self, LDAPControl, ctl);

  if( ctl->ldctl_value.bv_val )
    free(ctl->ldctl_value.bv_val);

  if( val == Qnil ){
    ctl->ldctl_value.bv_val = NULL;
    ctl->ldctl_value.bv_len = 0;
  }
  else{
    RB_LDAP_SET_STR(ctl->ldctl_value.bv_val, val);
    ctl->ldctl_value.bv_len = RSTRING(val)->len;
  }

  return val;
}

static VALUE
rb_ldap_control_get_value(VALUE self)
{
  LDAPControl *ctl;
  VALUE val;

  Data_Get_Struct(self, LDAPControl, ctl);

  if( ctl->ldctl_value.bv_len == 0 || ctl->ldctl_value.bv_val == NULL ){
    val = Qnil;
  }
  else{
    val = rb_tainted_str_new(ctl->ldctl_value.bv_val, ctl->ldctl_value.bv_len);
  }

  return val;
}

static VALUE
rb_ldap_control_value(int argc, VALUE argv[], VALUE self)
{
  VALUE val;
  LDAPControl *ctl;


  if( rb_scan_args(argc, argv, "01", &val) == 1 ){
    val = rb_ldap_control_set_value(self, val);
  }
  else{
    val = rb_ldap_control_get_value(self);
  }
  return val;
}

static VALUE
rb_ldap_control_set_oid(VALUE self, VALUE val)
{
  LDAPControl *ctl;

  Data_Get_Struct(self, LDAPControl, ctl);

  if( ctl->ldctl_oid )
    free(ctl->ldctl_oid);

  if( val == Qnil ){
    ctl->ldctl_oid = NULL;
  }
  else{
    RB_LDAP_SET_STR(ctl->ldctl_oid, val);
  }

  return val;
}

static VALUE
rb_ldap_control_get_oid(VALUE self)
{
  LDAPControl *ctl;
  VALUE val;

  Data_Get_Struct(self, LDAPControl, ctl);

  if( ctl->ldctl_oid == NULL ){
    val = Qnil;
  }
  else{
    val = rb_tainted_str_new2(ctl->ldctl_oid);
  }

  return val;
}

static VALUE
rb_ldap_control_oid(int argc, VALUE argv[], VALUE self)
{
  VALUE val;
  LDAPControl *ctl;

  Data_Get_Struct(self, LDAPControl, ctl);
  if( rb_scan_args(argc, argv, "01", &val) == 1 ){
    val = rb_ldap_control_set_oid(self, val);
  }
  else{
    val = rb_ldap_control_get_oid(self);
  }
  return val;
}

static VALUE
rb_ldap_control_set_critical(VALUE self, VALUE val)
{
  LDAPControl *ctl;

  Data_Get_Struct(self, LDAPControl, ctl);
  ctl->ldctl_iscritical = (val == Qtrue) ? 1 : 0;
  return val;
}

static VALUE
rb_ldap_control_get_critical(VALUE self)
{
  LDAPControl *ctl;
  VALUE val;

  Data_Get_Struct(self, LDAPControl, ctl);
  val = ctl->ldctl_iscritical ? Qtrue : Qfalse;

  return val;
}

static VALUE
rb_ldap_control_critical(int argc, VALUE argv[], VALUE self)
{
  VALUE val;
  LDAPControl *ctl;

  Data_Get_Struct(self, LDAPControl, ctl);
  if( rb_scan_args(argc, argv, "01", &val) == 1 ){
    val = rb_ldap_control_set_critical(self, val);
  }
  else{
    val = rb_ldap_control_get_critical(self);
  }
  return val;
}

static VALUE
rb_ldap_control_initialize(int argc, VALUE argv[], VALUE self)
{
  VALUE oid, value, critical;

  switch( rb_scan_args(argc, argv, "03", &oid, &value, &critical) ){
  case 3:
    rb_ldap_control_set_critical(self, critical);
  case 2:
    rb_ldap_control_set_value(self, value);
  case 1:
    rb_ldap_control_set_critical(self, critical);
  default:
    break;
  }

  return Qnil;
}

static VALUE
rb_ldap_control_inspect(VALUE self)
{
  VALUE str;

  str = rb_tainted_str_new2("#<");
  rb_str_cat2(str, rb_class2name(CLASS_OF(self)));
  rb_str_cat2(str, " oid=");
  rb_str_concat(str, rb_inspect(rb_ldap_control_get_oid(self)));
  rb_str_cat2(str, " value=");
  rb_str_concat(str, rb_inspect(rb_ldap_control_get_value(self)));
  rb_str_cat2(str, " iscritical=");
  rb_str_concat(str, rb_inspect(rb_ldap_control_get_critical(self)));
  rb_str_cat2(str, ">");

  return str;
}

VALUE
rb_ldap_controls_new(LDAPControl **ctrls)
{
  int len, i;
  VALUE ary;

  if( !ctrls )
    return Qnil;

  ary = rb_ary_new();
  for( i=0; ctrls[i]; i++ ){
    rb_ary_push(ary, rb_ldap_control_new(ctrls[i]));
  }

  return ary;
}

LDAPControl *
rb_ldap_get_control(VALUE obj)
{
  LDAPControl *ctl;

  if( obj == Qnil ){
    return NULL;
  }
  else{
    Data_Get_Struct(obj, LDAPControl, ctl);
    return ctl;
  }
}

LDAPControl **
rb_ldap_get_controls(VALUE data)
{
  LDAPControl **ctls;
  int len, i;

  if( data == Qnil )
    return NULL;

  Check_Type(data, T_ARRAY);
  len = RARRAY(data)->len;
  ctls = ALLOC_N(LDAPControl*, len+1);
  for( i=0; i < len; i++ ){
    ctls[i] = rb_ldap_get_control(rb_ary_entry(data, i));
  }
  ctls[len] = NULL;

  return ctls;
}
#endif

void
Init_ldap_misc()
{
  rb_sLDAP_APIInfo = rb_struct_define("APIInfo",
				      "info_version", /* ldapai_xxxx */
				      "api_version",
				      "protocol_version",
				      "extensions",
				      "vendor_name",
				      "vendor_version",
				      0);
  rb_define_const(rb_mLDAP, "APIInfo", rb_sLDAP_APIInfo);

#ifdef HAVE_LDAPCONTROL
  rb_cLDAP_Control = rb_define_class_under(rb_mLDAP, "Control", rb_cObject);
#if RUBY_VERSION_CODE >= 170
#  if RUBY_VERSION_CODE >= 173
  rb_define_alloc_func(rb_cLDAP_Control, rb_ldap_control_s_allocate);
#  else
  rb_define_singleton_method(rb_cLDAP_Control, "allocate",
			     rb_ldap_control_s_allocate, 0);
#  endif
#else
  rb_define_singleton_method(rb_cLDAP_Control, "new",
			     rb_ldap_control_s_new, -1);
#endif
  rb_define_method(rb_cLDAP_Control, "initialize", rb_ldap_control_initialize, -1);
  rb_define_method(rb_cLDAP_Control, "inspect", rb_ldap_control_inspect, 0);
  rb_define_method(rb_cLDAP_Control, "oid", rb_ldap_control_oid, -1);
  rb_define_method(rb_cLDAP_Control, "oid=", rb_ldap_control_oid, -1);
  rb_define_method(rb_cLDAP_Control, "value", rb_ldap_control_value, -1);
  rb_define_method(rb_cLDAP_Control, "value=", rb_ldap_control_value, -1);
  rb_define_method(rb_cLDAP_Control, "critical?", rb_ldap_control_critical, -1);
  rb_define_method(rb_cLDAP_Control, "critical", rb_ldap_control_critical, -1);
  rb_define_method(rb_cLDAP_Control, "critical=", rb_ldap_control_critical, -1);
  rb_define_method(rb_cLDAP_Control, "iscritical", rb_ldap_control_critical, -1);
  rb_define_method(rb_cLDAP_Control, "iscritical=", rb_ldap_control_critical, -1);
#endif
}
