2003-10-28 10:27:54 -05:00
|
|
|
#include "scsh-ldap.h"
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_MAKE_ENTER_RECORD(scsh_enter_ldap, scsh_ldap_record_type,
|
|
|
|
LDAP*);
|
2003-11-16 03:35:08 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_MAKE_ENTER_RECORD(scsh_enter_ldapmessage, scsh_ldapmessage_record_type,
|
|
|
|
LDAPMessage*);
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_MAKE_ENTER_RECORD(scsh_enter_ldapmod, scsh_ldapmod_record_type,
|
|
|
|
LDAPMod*);
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_MAKE_ENTER_RECORD(scsh_enter_ldapiinfo, scsh_ldapapiinfo_record_type,
|
|
|
|
LDAPAPIInfo*);
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_STRUCT_GET_INT(scsh_ldapapiinfo_get_info_version,
|
|
|
|
scsh_ldapapiinfo_record_type, LDAPAPIInfo*,
|
|
|
|
ldapai_info_version);
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_STRUCT_GET_INT(scsh_ldapapiinfo_get_api_version,
|
|
|
|
scsh_ldapapiinfo_record_type, LDAPAPIInfo*,
|
|
|
|
ldapai_api_version);
|
|
|
|
|
|
|
|
FFIT_STRUCT_GET_INT(scsh_ldapapiinfo_get_protocol_version,
|
|
|
|
scsh_ldapapiinfo_record_type, LDAPAPIInfo*,
|
|
|
|
ldapai_protocol_version);
|
|
|
|
|
|
|
|
FFIT_STRUCT_GET_STRING(scsh_ldapapiinfo_get_vendor_name,
|
|
|
|
scsh_ldapapiinfo_record_type, LDAPAPIInfo*,
|
|
|
|
ldapai_vendor_name);
|
|
|
|
|
|
|
|
FFIT_STRUCT_GET_INT(scsh_ldapapiinfo_get_vendor_version,
|
|
|
|
scsh_ldapapiinfo_record_type, LDAPAPIInfo*,
|
|
|
|
ldapai_vendor_version);
|
|
|
|
|
|
|
|
FFIT_STRUCT_GET(scsh_ldapapiinfo_get_extensions,
|
|
|
|
scsh_ldapapiinfo_record_type, LDAPAPIInfo*,
|
|
|
|
ldapai_extensions, ffit_enter_string_array);
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
FFIT_MAKE_ENTER_RECORD(scsh_enter_berelement, scsh_berelement_record_type,
|
|
|
|
BerElement *);
|
|
|
|
|
2003-10-28 10:27:54 -05:00
|
|
|
s48_value scsh_ldap_init(s48_value host, s48_value port)
|
|
|
|
{
|
|
|
|
LDAP *ldap;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(host, port);
|
|
|
|
ldap = ldap_init(s48_extract_string(host), s48_extract_integer(port));
|
|
|
|
S48_GC_UNPROTECT();
|
2004-02-11 11:25:34 -05:00
|
|
|
|
2004-01-08 02:48:00 -05:00
|
|
|
if (ldap == NULL)
|
2004-02-11 07:46:39 -05:00
|
|
|
s48_raise_os_error(errno);
|
2004-01-08 02:48:00 -05:00
|
|
|
else
|
2004-02-11 07:46:39 -05:00
|
|
|
return scsh_enter_ldap(ldap);
|
2003-10-28 10:27:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_simple_bind_s(s48_value ldap, s48_value user, s48_value cred)
|
|
|
|
{
|
|
|
|
int r;
|
2004-02-11 11:25:34 -05:00
|
|
|
LDAP *l;
|
2003-10-28 10:27:54 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_3(ldap, user, cred);
|
2004-02-11 11:25:34 -05:00
|
|
|
r = ldap_simple_bind_s(scsh_extract_ldap(ldap),
|
|
|
|
S48_FALSE_P(user) ? NULL : s48_extract_string(user),
|
|
|
|
S48_FALSE_P(cred) ? NULL : s48_extract_string(cred));
|
2003-10-28 10:27:54 -05:00
|
|
|
S48_GC_UNPROTECT();
|
2003-11-16 03:35:08 -05:00
|
|
|
return s48_enter_integer(r);
|
|
|
|
}
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
s48_value scsh_ldap_sasl_bind_s(s48_value ldap, s48_value dn,
|
|
|
|
s48_value mechanism, s48_value cred,
|
|
|
|
s48_value server_controls,
|
|
|
|
s48_value client_controls,
|
|
|
|
s48_value server_cred_p)
|
2003-11-16 03:35:08 -05:00
|
|
|
{
|
2003-12-31 10:40:26 -05:00
|
|
|
/* need to implement bindings for berval stuff first */
|
2003-11-17 09:44:27 -05:00
|
|
|
return S48_FALSE;
|
2003-10-28 10:27:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_unbind_s(s48_value ldap)
|
|
|
|
{
|
|
|
|
return s48_enter_integer(ldap_unbind_s(scsh_extract_ldap(ldap)));
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_error_string(s48_value errcode)
|
|
|
|
{
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_CHECK_INTEGER(errcode);
|
2003-10-28 10:27:54 -05:00
|
|
|
return s48_enter_string(ldap_err2string(s48_extract_integer(errcode)));
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_result_error(s48_value ldap, s48_value res)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, res);
|
|
|
|
r = s48_enter_integer(ldap_result2error(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(res), 0));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return s48_enter_integer(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_memfree(s48_value ldap)
|
|
|
|
{
|
|
|
|
ldap_memfree(scsh_extract_ldap(ldap));
|
|
|
|
return S48_UNSPECIFIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_msgfree(s48_value ldapmsg)
|
|
|
|
{
|
|
|
|
ldap_msgfree(scsh_extract_ldapmessage(ldapmsg));
|
|
|
|
return S48_UNSPECIFIC;
|
|
|
|
}
|
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
s48_value scsh_ldap_ber_free(s48_value ber, s48_value sfbuf)
|
|
|
|
{
|
|
|
|
int fbuf;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ber, sfbuf);
|
|
|
|
fbuf = s48_extract_integer(sfbuf);
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
ber_free(scsh_extract_berelement(ber), fbuf);
|
|
|
|
}
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
s48_value scsh_ldap_search_s(s48_value ldap, s48_value base,
|
|
|
|
s48_value scope, s48_value filter,
|
|
|
|
s48_value attrs, s48_value attrsonly)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
char** a;
|
2004-02-12 07:45:29 -05:00
|
|
|
LDAPMessage *msg;
|
2003-11-16 03:35:08 -05:00
|
|
|
s48_value res;
|
|
|
|
S48_DECLARE_GC_PROTECT(7);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_7(ldap, base, scope, filter, attrs, attrsonly, res);
|
2003-12-31 10:40:26 -05:00
|
|
|
a = ffit_extract_list_of_strings(attrs);
|
2004-02-12 07:45:29 -05:00
|
|
|
r = ldap_search_s(scsh_extract_ldap(ldap),
|
|
|
|
S48_FALSE_P(base) ? NULL : s48_extract_string(base),
|
2003-11-16 03:35:08 -05:00
|
|
|
s48_extract_integer(scope),
|
2004-02-12 04:10:22 -05:00
|
|
|
S48_FALSE_P(filter) ? NULL : s48_extract_string(filter),
|
2004-02-12 07:45:29 -05:00
|
|
|
a, S48_TRUE_P(attrsonly), &msg);
|
2004-02-16 03:20:29 -05:00
|
|
|
|
|
|
|
free(a); /* FIXME: is this enough? */
|
2004-01-08 02:48:00 -05:00
|
|
|
res = s48_list_2(s48_enter_integer(r),
|
2004-02-12 07:45:29 -05:00
|
|
|
r == LDAP_SUCCESS ? scsh_enter_ldapmessage(msg) : S48_FALSE);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_search_st(s48_value ldap, s48_value base,
|
|
|
|
s48_value scope, s48_value filter,
|
|
|
|
s48_value attrs, s48_value attrsonly,
|
|
|
|
s48_value timeout_sec, s48_value timeout_usec)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
char** a;
|
2004-02-12 07:45:29 -05:00
|
|
|
LDAPMessage *msg;
|
2003-11-16 03:35:08 -05:00
|
|
|
struct timeval timeout;
|
2004-02-12 07:45:29 -05:00
|
|
|
s48_value res;
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(9);
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_GC_PROTECT_9(ldap, base, scope, filter, attrs, attrsonly, res,
|
|
|
|
timeout_sec, timeout_usec);
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
timeout.tv_sec = s48_extract_integer(timeout_sec);
|
|
|
|
timeout.tv_usec = s48_extract_integer(timeout_usec);
|
2003-12-31 10:40:26 -05:00
|
|
|
a = ffit_extract_list_of_strings(attrs);
|
2004-02-12 07:45:29 -05:00
|
|
|
r = ldap_search_st(scsh_extract_ldap(ldap),
|
|
|
|
S48_FALSE_P(base) ? NULL : s48_extract_string(base),
|
|
|
|
s48_extract_integer(scope),
|
|
|
|
S48_FALSE_P(filter) ? NULL : s48_extract_string(filter),
|
|
|
|
a, S48_TRUE_P(attrsonly), &timeout, &msg);
|
2004-02-16 03:20:29 -05:00
|
|
|
free(a); /* FIXME: is this enough? */
|
2004-02-12 07:45:29 -05:00
|
|
|
res = s48_list_2(s48_enter_integer(r),
|
|
|
|
r == LDAP_SUCCESS ? scsh_enter_ldapmessage(msg) : S48_FALSE);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
s48_value scsh_ldap_compare_s(s48_value ldap, s48_value dn,
|
|
|
|
s48_value attr, s48_value value)
|
|
|
|
{
|
|
|
|
s48_value res;
|
|
|
|
int r;
|
|
|
|
S48_DECLARE_GC_PROTECT(5);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_5(ldap, dn, attr, value, res);
|
2004-02-12 04:10:22 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
r = ldap_compare_s(scsh_extract_ldap(ldap), s48_extract_string(dn),
|
|
|
|
s48_extract_string(attr), s48_extract_string(value));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
switch (r) {
|
|
|
|
case LDAP_COMPARE_TRUE: return S48_TRUE;
|
|
|
|
case LDAP_COMPARE_FALSE: return S48_FALSE;
|
|
|
|
default: return s48_enter_integer(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
s48_value scsh_ldap_count_entries(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
r = ldap_count_entries(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
2004-01-14 04:12:07 -05:00
|
|
|
return (r == -1) ? S48_FALSE : s48_enter_integer(r);
|
2003-11-16 03:35:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_first_entry(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
LDAPMessage *lm_new;
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
2003-11-16 03:35:08 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
lm_new = ldap_first_entry(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
2003-12-31 10:40:26 -05:00
|
|
|
return (lm_new == NULL) ? S48_FALSE : scsh_enter_ldapmessage(lm_new);
|
2003-11-16 03:35:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_next_entry(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
LDAPMessage *lm_new;
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
2003-11-16 03:35:08 -05:00
|
|
|
|
|
|
|
lm_new = ldap_next_entry(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
2003-12-31 10:40:26 -05:00
|
|
|
return (lm_new == NULL) ? S48_FALSE : scsh_enter_ldapmessage(lm_new);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_first_message(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
LDAPMessage *first;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
first = ldap_first_message(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return (first == NULL) ? S48_FALSE : scsh_enter_ldapmessage(first);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_next_message(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
LDAPMessage *next;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
next = ldap_next_message(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return (next == NULL) ? S48_FALSE : scsh_enter_ldapmessage(next);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_count_messages(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
c = ldap_count_messages(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return (c == -1) ? S48_FALSE : s48_enter_integer(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_first_reference(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
LDAPMessage *new_lm;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
new_lm = ldap_first_reference(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return (new_lm == NULL) ? S48_FALSE : scsh_enter_ldapmessage(new_lm);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_next_reference(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
LDAPMessage *new_lm;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
new_lm = ldap_next_reference(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return (new_lm == NULL) ? S48_FALSE : scsh_enter_ldapmessage(new_lm);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_count_references(s48_value ldap, s48_value lm)
|
|
|
|
{
|
|
|
|
int c;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, lm);
|
|
|
|
c = ldap_count_references(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(lm));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return (c == -1) ? S48_FALSE : s48_enter_integer(c);
|
2003-11-16 03:35:08 -05:00
|
|
|
}
|
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
s48_value scsh_ldap_first_attribute(s48_value ldap, s48_value entry)
|
|
|
|
{
|
|
|
|
BerElement *ber;
|
|
|
|
char *name;
|
|
|
|
s48_value res_list = S48_FALSE;
|
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_3(ldap, entry, res_list);
|
|
|
|
if ((ber = ber_alloc_t(LBER_USE_DER)) == NULL)
|
|
|
|
raise_ldap_memory_alloc_error();
|
|
|
|
|
|
|
|
name = ldap_first_attribute(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(entry),
|
|
|
|
&ber);
|
|
|
|
if (name == NULL)
|
|
|
|
res_list = s48_list_2(S48_FALSE, S48_FALSE);
|
2004-02-13 11:51:23 -05:00
|
|
|
else {
|
2004-01-14 04:12:07 -05:00
|
|
|
res_list = s48_list_2(s48_enter_string(name),
|
|
|
|
scsh_enter_berelement(ber));
|
2004-02-13 11:51:23 -05:00
|
|
|
ldap_memfree(name);
|
|
|
|
}
|
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return res_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_next_attribute(s48_value ldap, s48_value entry, s48_value ber)
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
s48_value res;
|
|
|
|
S48_DECLARE_GC_PROTECT(4);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_4(ldap, entry, ber, res);
|
|
|
|
name = ldap_next_attribute(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(entry),
|
|
|
|
scsh_extract_berelement(ber));
|
2004-02-13 11:51:23 -05:00
|
|
|
if (name == NULL)
|
|
|
|
res = S48_FALSE;
|
|
|
|
else {
|
|
|
|
res = s48_enter_string(name);
|
|
|
|
ldap_memfree(name);
|
|
|
|
}
|
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
s48_value scsh_ldap_msgtype(s48_value lm)
|
|
|
|
{
|
|
|
|
int r;
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
r = ldap_msgtype(scsh_extract_ldapmessage(lm));
|
2004-01-14 04:12:07 -05:00
|
|
|
return (r == -1) ? S48_FALSE : s48_enter_integer(r);
|
2003-11-16 03:35:08 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_msgid(s48_value lm)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = ldap_msgid(scsh_extract_ldapmessage(lm));
|
2004-01-14 04:12:07 -05:00
|
|
|
return (r == -1) ? S48_FALSE : s48_enter_integer(r);
|
2003-11-16 03:35:08 -05:00
|
|
|
}
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
/* may raise ldap error */
|
|
|
|
s48_value scsh_ldap_get_dn(s48_value ldap, s48_value entry)
|
|
|
|
{
|
|
|
|
char *s;
|
2004-01-14 04:12:07 -05:00
|
|
|
s48_value res;
|
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
S48_GC_PROTECT_3(ldap, entry, res);
|
2003-12-31 10:40:26 -05:00
|
|
|
s = ldap_get_dn(scsh_extract_ldap(ldap), scsh_extract_ldapmessage(entry));
|
2004-01-14 04:12:07 -05:00
|
|
|
res = (s == NULL) ? S48_FALSE : s48_enter_string(s);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_GC_UNPROTECT();
|
2004-01-14 04:12:07 -05:00
|
|
|
ldap_memfree(s);
|
|
|
|
return res;
|
2003-12-31 10:40:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_explode_dn(s48_value dn, s48_value notypes)
|
|
|
|
{
|
|
|
|
char **a;
|
|
|
|
s48_value res = S48_FALSE;
|
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_3(dn, notypes, res);
|
|
|
|
a = ldap_explode_dn(s48_extract_string(dn),
|
|
|
|
S48_TRUE_P(notypes) ? 0 : 1);
|
|
|
|
res = ffit_enter_string_array(a);
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
ldap_value_free(a);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_explode_rdn(s48_value rdn, s48_value notypes)
|
|
|
|
{
|
|
|
|
char **a;
|
|
|
|
s48_value res = S48_FALSE;
|
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_3(rdn, notypes, res);
|
|
|
|
a = ldap_explode_rdn(s48_extract_string(rdn),
|
|
|
|
S48_TRUE_P(notypes) ? 0 : 1);
|
|
|
|
res = ffit_enter_string_array(a);
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
ldap_value_free(a);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_dn2ufn(s48_value dn)
|
|
|
|
{
|
|
|
|
char *a;
|
|
|
|
s48_value res = S48_FALSE;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(dn, res);
|
|
|
|
a = ldap_dn2ufn(s48_extract_string(dn));
|
|
|
|
res = s48_enter_string(a);
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
ldap_memfree(a);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
s48_value scsh_ldap_get_values(s48_value ldap, s48_value entry,
|
|
|
|
s48_value attr)
|
|
|
|
{
|
|
|
|
char **val;
|
2004-01-14 04:12:07 -05:00
|
|
|
s48_value res;
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(4);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_4(ldap, entry, attr, res);
|
|
|
|
val = ldap_get_values(scsh_extract_ldap(ldap),
|
|
|
|
scsh_extract_ldapmessage(entry),
|
|
|
|
s48_extract_string(attr));
|
2004-01-14 04:12:07 -05:00
|
|
|
res = (val == NULL) ? S48_FALSE : ffit_enter_string_array(val);
|
|
|
|
if (val != NULL)
|
|
|
|
ldap_value_free(val);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
s48_value scsh_ldap_modify(s48_value ldap, s48_value dn, s48_value mods)
|
2003-11-16 03:35:08 -05:00
|
|
|
{
|
2003-12-31 10:40:26 -05:00
|
|
|
int r;
|
2004-02-16 03:20:29 -05:00
|
|
|
LDAPMod **a;
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
2003-11-16 03:35:08 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_GC_PROTECT_3(ldap, dn, mods);
|
2004-02-16 03:20:29 -05:00
|
|
|
a = scsh_extract_ldapmod_list(mods);
|
|
|
|
r = ldap_modify_s(scsh_extract_ldap(ldap),
|
|
|
|
s48_extract_string(dn),
|
|
|
|
a);
|
|
|
|
scsh_free_ldapmod_array(a);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_GC_UNPROTECT();
|
2003-12-31 10:40:26 -05:00
|
|
|
return s48_enter_integer(r);
|
2003-11-16 03:35:08 -05:00
|
|
|
}
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
s48_value scsh_ldap_add(s48_value ldap, s48_value dn, s48_value mods)
|
2003-11-16 03:35:08 -05:00
|
|
|
{
|
2003-12-31 10:40:26 -05:00
|
|
|
int r;
|
2004-02-16 03:20:29 -05:00
|
|
|
LDAPMod **a;
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_DECLARE_GC_PROTECT(3);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_3(ldap, dn, mods);
|
2004-02-16 03:20:29 -05:00
|
|
|
a = scsh_extract_ldapmod_list(mods);
|
|
|
|
r = ldap_add_s(scsh_extract_ldap(ldap),
|
|
|
|
s48_extract_string(dn),
|
|
|
|
a);
|
|
|
|
scsh_free_ldapmod_array(a);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return s48_enter_integer(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_delete(s48_value ldap, s48_value dn)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, dn);
|
|
|
|
r = ldap_delete_s(scsh_extract_ldap(ldap), s48_extract_string(dn));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return s48_enter_integer(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
s48_value scsh_ldap_abandon(s48_value ldap, s48_value msgid)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
|
|
|
|
|
|
|
S48_GC_PROTECT_2(ldap, msgid);
|
|
|
|
r = ldap_abandon(scsh_extract_ldap(ldap), s48_extract_integer(msgid));
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return s48_enter_integer(r);
|
|
|
|
}
|
|
|
|
|
2004-02-11 11:25:34 -05:00
|
|
|
/* FIXME: Options missing here! */
|
2003-12-31 10:40:26 -05:00
|
|
|
s48_value scsh_ldap_get_set_option(s48_value ldap, s48_value option,
|
|
|
|
s48_value set, s48_value inval)
|
|
|
|
{
|
2004-01-08 02:48:00 -05:00
|
|
|
int opt, rc;
|
2003-12-31 10:40:26 -05:00
|
|
|
LDAP *ld;
|
2004-01-08 02:48:00 -05:00
|
|
|
s48_value res, res_list;
|
|
|
|
S48_DECLARE_GC_PROTECT(6);
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-01-14 04:12:07 -05:00
|
|
|
S48_GC_PROTECT_6(ldap, option, set, inval, res, res_list);
|
2003-12-31 10:40:26 -05:00
|
|
|
FFIT_CHECK_BOOLEAN(set);
|
|
|
|
ld = scsh_extract_ldap(ldap);
|
2004-02-11 11:25:34 -05:00
|
|
|
opt = s48_extract_integer(option);
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-01-08 02:48:00 -05:00
|
|
|
res = S48_UNSPECIFIC;
|
2003-12-31 10:40:26 -05:00
|
|
|
switch (opt) {
|
|
|
|
case LDAP_OPT_API_INFO:
|
|
|
|
{
|
2004-02-12 07:45:29 -05:00
|
|
|
LDAPAPIInfo *inf;
|
2003-12-31 10:40:26 -05:00
|
|
|
if (S48_TRUE_P(set))
|
|
|
|
raise_ldap_read_only_option();
|
2004-02-12 07:45:29 -05:00
|
|
|
|
|
|
|
rc = ldap_get_option(ld, opt, &inf);
|
2004-01-08 02:48:00 -05:00
|
|
|
if (rc == LDAP_OPT_SUCCESS)
|
2004-02-12 07:45:29 -05:00
|
|
|
res = scsh_enter_ldapiinfo(inf);
|
2003-12-31 10:40:26 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LDAP_OPT_DEREF:
|
|
|
|
case LDAP_OPT_SIZELIMIT:
|
|
|
|
case LDAP_OPT_TIMELIMIT:
|
2004-02-12 07:45:29 -05:00
|
|
|
case LDAP_OPT_PROTOCOL_VERSION:
|
|
|
|
case LDAP_OPT_ERROR_NUMBER:
|
2003-12-31 10:40:26 -05:00
|
|
|
{
|
|
|
|
int l;
|
2004-02-11 11:25:34 -05:00
|
|
|
int ov;
|
2003-12-31 10:40:26 -05:00
|
|
|
if (S48_TRUE_P(set)) {
|
|
|
|
FFIT_CHECK_INTEGER(inval);
|
|
|
|
l = s48_extract_integer(inval);
|
2004-01-08 02:48:00 -05:00
|
|
|
rc = ldap_set_option(ld, opt, &l);
|
2003-12-31 10:40:26 -05:00
|
|
|
}
|
|
|
|
else
|
2004-02-11 11:25:34 -05:00
|
|
|
if ((rc = ldap_get_option(ld, opt, &ov)) == LDAP_SUCCESS)
|
|
|
|
res = s48_enter_integer(ov);
|
2003-12-31 10:40:26 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LDAP_OPT_REFERRALS:
|
|
|
|
case LDAP_OPT_RESTART:
|
|
|
|
{
|
2004-02-11 11:25:34 -05:00
|
|
|
int ov;
|
2003-12-31 10:40:26 -05:00
|
|
|
if (S48_TRUE_P(set)) {
|
|
|
|
FFIT_CHECK_BOOLEAN(inval);
|
2004-01-08 02:48:00 -05:00
|
|
|
rc = ldap_set_option(ld, opt, S48_TRUE_P(inval) ? LDAP_OPT_ON : LDAP_OPT_OFF);
|
2003-12-31 10:40:26 -05:00
|
|
|
}
|
|
|
|
else
|
2004-02-11 11:25:34 -05:00
|
|
|
if ((rc = ldap_get_option(ld, opt, &ov)) == LDAP_SUCCESS)
|
|
|
|
res = ov == 0 ? S48_TRUE : S48_FALSE;
|
2003-12-31 10:40:26 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LDAP_OPT_SERVER_CONTROLS:
|
|
|
|
case LDAP_OPT_CLIENT_CONTROLS:
|
|
|
|
{
|
|
|
|
raise_ldap_read_only_option();
|
|
|
|
}
|
2004-02-12 07:45:29 -05:00
|
|
|
case LDAP_OPT_ERROR_STRING:
|
|
|
|
{
|
|
|
|
char *ov;
|
|
|
|
if (S48_TRUE_P(set))
|
|
|
|
raise_ldap_read_only_option();
|
|
|
|
|
|
|
|
if ((rc = ldap_get_option(ld, opt, &ov)) == LDAP_SUCCESS)
|
|
|
|
res = s48_enter_string(ov);
|
|
|
|
break;
|
|
|
|
}
|
2004-02-11 11:25:34 -05:00
|
|
|
default:
|
|
|
|
{
|
2004-02-12 07:45:29 -05:00
|
|
|
raise_ldap_feature_not_supported();
|
2004-02-11 11:25:34 -05:00
|
|
|
}
|
2003-12-31 10:40:26 -05:00
|
|
|
}
|
2004-01-08 02:48:00 -05:00
|
|
|
|
|
|
|
res_list =
|
|
|
|
s48_list_2(rc == LDAP_OPT_SUCCESS ? S48_TRUE : S48_FALSE,
|
|
|
|
rc == LDAP_OPT_SUCCESS ? res : S48_UNSPECIFIC);
|
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return res_list;
|
2003-12-31 10:40:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************************ */
|
|
|
|
|
2004-02-16 03:20:29 -05:00
|
|
|
/* may GC, protect ldapmod */
|
|
|
|
LDAPMod* scsh_create_ldapmod(s48_value ldapmod)
|
|
|
|
{
|
2003-12-31 10:40:26 -05:00
|
|
|
LDAPMod *m;
|
2004-02-16 03:20:29 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
if ((m = (LDAPMod*) calloc(1, sizeof(LDAPMod))) == NULL)
|
|
|
|
raise_ldap_memory_alloc_error();
|
|
|
|
|
2004-02-16 03:20:29 -05:00
|
|
|
m->mod_op
|
|
|
|
= s48_extract_integer(S48_RECORD_REF(ldapmod,
|
|
|
|
SCSH_LDAP_MOD_RECFIELD_OP));
|
|
|
|
m->mod_type
|
|
|
|
= s48_extract_string(S48_RECORD_REF(ldapmod,
|
|
|
|
SCSH_LDAP_MOD_RECFIELD_TYPE));
|
|
|
|
m->mod_vals.modv_strvals
|
|
|
|
= ffit_extract_list_of_strings(S48_RECORD_REF(ldapmod,
|
|
|
|
SCSH_LDAP_MOD_RECFIELD_MODS));
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-02-16 03:20:29 -05:00
|
|
|
return m;
|
|
|
|
}
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-02-16 03:20:29 -05:00
|
|
|
LDAPMod** scsh_extract_ldapmod_list(s48_value list)
|
2003-12-31 10:40:26 -05:00
|
|
|
{
|
|
|
|
LDAPMod **a;
|
2003-11-16 03:35:08 -05:00
|
|
|
int l, i;
|
2004-02-16 03:20:29 -05:00
|
|
|
s48_value e;
|
|
|
|
S48_DECLARE_GC_PROTECT(2);
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-02-16 03:20:29 -05:00
|
|
|
S48_GC_PROTECT_2(list, e);
|
|
|
|
l = length_scheme_list(list);
|
|
|
|
|
|
|
|
if ((a = (LDAPMod **) calloc(l + 1, sizeof(LDAPMod *))) == NULL)
|
|
|
|
s48_raise_out_of_memory_error();
|
2003-12-31 10:40:26 -05:00
|
|
|
a[l] = NULL;
|
2004-02-16 03:20:29 -05:00
|
|
|
|
|
|
|
for (e = list, i = 0; e != NULL; e = S48_CDR(e), i++)
|
|
|
|
a[i] = scsh_create_ldapmod(S48_CAR(e));
|
|
|
|
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_GC_UNPROTECT();
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2004-02-16 03:20:29 -05:00
|
|
|
void scsh_free_ldapmod_array(LDAPMod **a)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
LDAPMod *e;
|
|
|
|
|
|
|
|
for (e = a[0], i = 0; e != NULL; e = a[++i])
|
|
|
|
ldap_memfree(e);
|
|
|
|
free(a);
|
|
|
|
}
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
void raise_ldap_memory_alloc_error(void)
|
|
|
|
{
|
|
|
|
s48_raise_scheme_exception(condition_ldap_memory_alloc_error, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void raise_ldap_feature_not_supported(void)
|
2003-10-28 10:27:54 -05:00
|
|
|
{
|
2003-12-31 10:40:26 -05:00
|
|
|
s48_raise_scheme_exception(condition_ldap_feature_not_supported, 0);
|
|
|
|
}
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
void raise_ldap_read_only_option(void)
|
|
|
|
{
|
|
|
|
s48_raise_scheme_exception(condition_ldap_read_only_option, 0);
|
|
|
|
}
|
2003-10-28 10:27:54 -05:00
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
void scsh_init_ldap_bindings(void)
|
|
|
|
{
|
|
|
|
ffit_init_hook();
|
|
|
|
|
2004-02-11 07:46:39 -05:00
|
|
|
scsh_ldap_gc_protect_globals(); /* generated by ffi-tools */
|
|
|
|
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_GC_PROTECT_GLOBAL(condition_ldap_memory_alloc_error);
|
|
|
|
S48_GC_PROTECT_GLOBAL(condition_ldap_feature_not_supported);
|
|
|
|
S48_GC_PROTECT_GLOBAL(condition_ldap_read_only_option);
|
|
|
|
|
|
|
|
condition_ldap_memory_alloc_error =
|
|
|
|
s48_get_imported_binding("condition-ldap-memory-alloc-error");
|
|
|
|
condition_ldap_feature_not_supported =
|
|
|
|
s48_get_imported_binding("condition-ldap-feature-not-supported");
|
|
|
|
condition_ldap_read_only_option =
|
|
|
|
s48_get_imported_binding("condition-ldap-read-only-option");
|
|
|
|
|
2004-02-11 07:46:39 -05:00
|
|
|
scsh_ldap_enter_ldap_constants(); /* generated by ffi-tools */
|
|
|
|
scsh_ldap_constants_export_bindings(); /* generated by ffi-tools */
|
|
|
|
|
|
|
|
FFIT_RECORD_TYPE_INIT(scsh_ldap_record_type, "ldap-session");
|
|
|
|
FFIT_RECORD_TYPE_INIT(scsh_ldapmessage_record_type, "ldap-message");
|
|
|
|
FFIT_RECORD_TYPE_INIT(scsh_ldapmod_record_type, "ldap-modification");
|
2003-12-31 10:40:26 -05:00
|
|
|
|
2004-02-11 07:46:39 -05:00
|
|
|
FFIT_RECORD_TYPE_INIT(scsh_ldapapiinfo_record_type, "ldap-api-info");
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldapapiinfo_get_info_version);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldapapiinfo_get_protocol_version);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldapapiinfo_get_vendor_name);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldapapiinfo_get_vendor_version);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldapapiinfo_get_extensions);
|
2003-11-16 03:35:08 -05:00
|
|
|
|
2004-02-11 07:46:39 -05:00
|
|
|
FFIT_RECORD_TYPE_INIT(scsh_berelement_record_type, "ber-element");
|
2004-01-14 04:12:07 -05:00
|
|
|
|
2003-10-28 10:27:54 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_init);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_simple_bind_s);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_sasl_bind_s);
|
2003-10-28 10:27:54 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_unbind_s);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_error_string);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_result_error);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_memfree);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_msgfree);
|
2004-01-14 04:12:07 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_ber_free);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_search_s);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_search_st);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_compare_s);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_count_entries);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_first_entry);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_next_entry);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_first_message);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_next_message);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_count_messages);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_first_reference);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_next_reference);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_count_references);
|
2004-01-14 04:12:07 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_first_attribute);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_next_attribute);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_msgtype);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_msgid);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_get_dn);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_explode_dn);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_explode_rdn);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_dn2ufn);
|
2003-11-16 03:35:08 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_get_values);
|
2003-12-31 10:40:26 -05:00
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_modify);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_add);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_delete);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_abandon);
|
|
|
|
S48_EXPORT_FUNCTION(scsh_ldap_get_set_option);
|
2003-10-28 10:27:54 -05:00
|
|
|
}
|