liquid_feedback_frontend
diff lib/mldap/mldap.c @ 1071:58f48a8a202a
Imported and merged LDAP patch
author | bsw |
---|---|
date | Fri Jul 18 21:42:59 2014 +0200 (2014-07-18) |
parents | |
children | 96336212e94d |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/lib/mldap/mldap.c Fri Jul 18 21:42:59 2014 +0200 1.3 @@ -0,0 +1,567 @@ 1.4 +/* 1.5 + * minimalistic Lua LDAP interface library 1.6 + * 1.7 + * The library does not set any global variable itself and must thus be 1.8 + * loaded with 1.9 + * 1.10 + * mldap = require("mldap") 1.11 + * 1.12 + * or a similar statement. 1.13 + * 1.14 + * The library provides two functions, conn = bind{...} and unbind(conn) 1.15 + * to connect to and disconnect from the LDAP server respectively. The 1.16 + * unbind function is also provided as a method of the connection userdata 1.17 + * object (see below). 1.18 + * 1.19 + * The arguments to the bind{...} function are documented in the source code 1.20 + * (see C function mldap_bind). In case of success, the bind function returns 1.21 + * a userdata object that provides two methods: query{...} and unbind(). In 1.22 + * case of error, the bind function returns nil as first return value, an 1.23 + * error string as second return value, and a numeric error code as third 1.24 + * return value. A positive error code is an LDAP resultCode, a negative 1.25 + * error code is an OpenLDAP API error code: 1.26 + * 1.27 + * connection, error_string, numeric_error_code = mldap.bind{...} 1.28 + * 1.29 + * For translating numeric error codes to an identifying (machine readable) 1.30 + * string identifier, the library provides in addition to the two functions 1.31 + * a table named 'errorcodes', for example: 1.32 + * 1.33 + * 49 == mldap.errorcodes["invalid_credentials"] 1.34 + * 1.35 + * and 1.36 + * 1.37 + * mldap.errorcodes[49] == "invalid_credentials" 1.38 + * 1.39 + * The arguments and return value of the query{...} method of the connection 1.40 + * userdata object are also documented in the source code below (see 1.41 + * C function mldap_query). Error conditions are reported the same way as the 1.42 + * bind{...} function does. 1.43 + * 1.44 + * To close the connection, either the unbind() function of the library or 1.45 + * the unbind() method can be called; it is allowed to call them multiple 1.46 + * times, and they are also invoked by the garbage collector. 1.47 + * 1.48 + */ 1.49 + 1.50 +// Lua header inclusions: 1.51 +#include <lua.h> 1.52 +#include <lauxlib.h> 1.53 + 1.54 +// OpenLDAP header inclusions: 1.55 +#include <ldap.h> 1.56 + 1.57 +// Standard C inclusions: 1.58 +#include <stdlib.h> 1.59 +#include <stdbool.h> 1.60 +#include <unistd.h> 1.61 + 1.62 +// Error code translation is included from separate C file: 1.63 +#include "mldap_errorcodes.c" 1.64 + 1.65 +// Provide compatibility with Lua 5.1: 1.66 +#if LUA_VERSION_NUM < 502 1.67 +#define luaL_newlib(L, t) lua_newtable((L)); luaL_register(L, NULL, t) 1.68 +#define lua_rawlen lua_objlen 1.69 +#define lua_len lua_objlen 1.70 +#define luaL_setmetatable(L, regkey) \ 1.71 + lua_getfield((L), LUA_REGISTRYINDEX, (regkey)); \ 1.72 + lua_setmetatable((L), -2); 1.73 +#endif 1.74 + 1.75 +// prefix for all Lua registry entries of this library: 1.76 +#define MLDAP_REGKEY "556aeaf3c864af2e_mldap_" 1.77 + 1.78 + 1.79 +static const char *mldap_get_named_string_arg( 1.80 + // gets a named argument of type "string" from a table at the given stack position 1.81 + 1.82 + lua_State *L, // pointer to lua_State variable 1.83 + int idx, // stack index of the table containing the named arguments 1.84 + const char *argname, // name of the argument 1.85 + int mandatory // if not 0, then the argument is mandatory and an error is raised if it isn't found 1.86 + 1.87 + // leaves the string as new element on top of the stack 1.88 +) { 1.89 + 1.90 + // pushes the table entry with the given argument name on top of the stack: 1.91 + lua_getfield(L, idx, argname); 1.92 + 1.93 + // check, if the entry is nil or false: 1.94 + if (!lua_toboolean(L, -1)) { 1.95 + 1.96 + // throw error, if named argument is mandatory: 1.97 + if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), NULL; 1.98 + 1.99 + // return NULL pointer, if named argument is not mandatory: 1.100 + return NULL; 1.101 + 1.102 + } 1.103 + 1.104 + // throw error, if the value of the argument is not string: 1.105 + if (lua_type(L, -1) != LUA_TSTRING) return luaL_error(L, "Named argument '%s' is not a string", argname), NULL; 1.106 + 1.107 + // return a pointer to the string, leaving the string on top of the stack to avoid garbage collection: 1.108 + return lua_tostring(L, -1); 1.109 + 1.110 + // leaves one element on the stack 1.111 +} 1.112 + 1.113 + 1.114 +static int mldap_get_named_number_arg( 1.115 + // gets a named argument of type "number" from a table at the given stack position 1.116 + 1.117 + lua_State *L, // pointer to lua_State variable 1.118 + int idx, // stack index of the table containing the named arguments 1.119 + const char *argname, // name of the argument 1.120 + int mandatory, // if not 0, then the argument is mandatory and an error is raised if it isn't found 1.121 + lua_Number default_value // default value to return, if the argument is not mandatory and nil or false 1.122 + 1.123 + // opposed to 'mldap_get_named_string_arg', this function leaves no element on the stack 1.124 +) { 1.125 + 1.126 + lua_Number value; // value to return 1.127 + 1.128 + // pushes the table entry with the given argument name on top of the stack: 1.129 + lua_getfield(L, idx, argname); 1.130 + 1.131 + // check, if the entry is nil or false: 1.132 + if (lua_isnil(L, -1)) { 1.133 + 1.134 + // throw error, if named argument is mandatory: 1.135 + if (mandatory) return luaL_error(L, "Named argument '%s' missing", argname), 0; 1.136 + 1.137 + // set default value as return value, if named argument is not mandatory: 1.138 + value = default_value; 1.139 + 1.140 + } else { 1.141 + 1.142 + // throw error, if the value of the argument is not a number: 1.143 + if (lua_type(L, -1) != LUA_TNUMBER) return luaL_error(L, "Named argument '%s' is not a number", argname), 0; 1.144 + 1.145 + // set return value to the number: 1.146 + value = lua_tonumber(L, -1); 1.147 + 1.148 + } 1.149 + 1.150 + // remove unnecessary element from stack (not needed to avoid garbage collection): 1.151 + return value; 1.152 + 1.153 + // leaves no new elements on the stack 1.154 +} 1.155 + 1.156 + 1.157 +static int mldap_scope( 1.158 + // converts a string ("base", "onelevel", "subtree", "children") to an integer representing the LDAP scope 1.159 + // and throws an error for any unknown string 1.160 + 1.161 + lua_State *L, // pointer to lua_State variable (needed to throw errors) 1.162 + const char *scope_string // string that is either ("base", "onelevel", "subtree", "children") 1.163 + 1.164 + // does not affect or use the Lua stack at all 1.165 +) { 1.166 + 1.167 + // return integer according to string value: 1.168 + if (!strcmp(scope_string, "base")) return LDAP_SCOPE_BASE; 1.169 + if (!strcmp(scope_string, "onelevel")) return LDAP_SCOPE_ONELEVEL; 1.170 + if (!strcmp(scope_string, "subtree")) return LDAP_SCOPE_SUBTREE; 1.171 + if (!strcmp(scope_string, "children")) return LDAP_SCOPE_CHILDREN; 1.172 + 1.173 + // throw error for unknown string values: 1.174 + return luaL_error(L, "Invalid LDAP scope: '%s'", scope_string), 0; 1.175 + 1.176 +} 1.177 + 1.178 + 1.179 +static int mldap_bind(lua_State *L) { 1.180 + // Lua C function that takes named arguments as a table 1.181 + // and returns a userdata object, representing the LDAP connection 1.182 + // or returns nil, an error string, and an error code (integer) on error 1.183 + 1.184 + // named arguments: 1.185 + // "uri" (string) server URI to connect to 1.186 + // "who" (string) DN to bind as 1.187 + // "password" (string) password for DN to bind as 1.188 + // "timeout" (number) timeout in seconds 1.189 + 1.190 + static const int ldap_version = LDAP_VERSION3; // providing a pointer (&ldap_version) to set LDAP protocol version 3 1.191 + const char *uri; // C string for "uri" argument 1.192 + const char *who; // C string for "who" argument 1.193 + struct berval cred; // credentials ("password") are stored as struct berval 1.194 + lua_Number timeout_float; // float (lua_Number) for timeout 1.195 + struct timeval timeout; // timeout is stored as struct timeval 1.196 + int ldap_error; // LDAP error code (as returned by libldap calls) 1.197 + LDAP *ldp; // pointer to an opaque OpenLDAP structure representing the connection 1.198 + LDAP **ldp_ptr; // pointer to a Lua userdata structure (that only contains the 'ldp' pointer) 1.199 + 1.200 + // throw error if first argument is not a table: 1.201 + if (lua_type(L, 1) != LUA_TTABLE) { 1.202 + luaL_error(L, "Argument to function 'bind' is not a table."); 1.203 + } 1.204 + 1.205 + // extract arguments: 1.206 + uri = mldap_get_named_string_arg(L, 1, "uri", true); 1.207 + who = mldap_get_named_string_arg(L, 1, "who", false); 1.208 + cred.bv_val = mldap_get_named_string_arg(L, 1, "password", false); 1.209 + if (cred.bv_val) cred.bv_len = strlen(cred.bv_val); 1.210 + else cred.bv_len = 0; 1.211 + timeout_float = mldap_get_named_number_arg(L, 1, "timeout", false, -1); 1.212 + timeout.tv_sec = timeout_float; 1.213 + timeout.tv_usec = (timeout_float - timeout.tv_sec) * 1000000; 1.214 + 1.215 + // initialize OpenLDAP structure and provide URI for connection: 1.216 + ldap_error = ldap_initialize(&ldp, uri); 1.217 + // on error, jump to label "mldap_queryconn_error1", as no ldap_unbind_ext_s() is needed: 1.218 + if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error1; 1.219 + 1.220 + // set LDAP protocol version 3: 1.221 + ldap_error = ldap_set_option(ldp, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); 1.222 + // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called: 1.223 + if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2; 1.224 + 1.225 + // set timeout for asynchronous OpenLDAP library calls: 1.226 + ldap_error = ldap_set_option(ldp, LDAP_OPT_TIMEOUT, &timeout); 1.227 + // on error, jump to label "mldap_queryconn_error2", as ldap_unbind_ext_s() must be called: 1.228 + if (ldap_error != LDAP_SUCCESS) goto mldap_queryconn_error2; 1.229 + 1.230 + // connect to LDAP server: 1.231 + ldap_error = ldap_sasl_bind_s( 1.232 + ldp, // pointer to opaque OpenLDAP structure representing the connection 1.233 + who, // DN to bind as 1.234 + LDAP_SASL_SIMPLE, // SASL mechanism "simple" for password authentication 1.235 + &cred, // password as struct berval 1.236 + NULL, // no server controls 1.237 + NULL, // no client controls 1.238 + NULL // do not store server credentials 1.239 + ); 1.240 + 1.241 + // error handling: 1.242 + if (ldap_error != LDAP_SUCCESS) { 1.243 + 1.244 + // error label to jump to, if a call of ldap_unbind_ext_s() is required: 1.245 + mldap_queryconn_error2: 1.246 + 1.247 + // close connection and free resources: 1.248 + ldap_unbind_ext_s(ldp, NULL, NULL); 1.249 + 1.250 + // error label to jump to, if no call of ldap_unbind_ext_s() is required: 1.251 + mldap_queryconn_error1: 1.252 + 1.253 + // return three values: 1.254 + lua_pushnil(L); // return nil as first value 1.255 + lua_pushstring(L, ldap_err2string(ldap_error)); // return error string as second value 1.256 + lua_pushinteger(L, ldap_error); // return error code (integer) as third value 1.257 + return 3; 1.258 + 1.259 + } 1.260 + 1.261 + // create new Lua userdata object (that will contain the 'ldp' pointer) on top of stack: 1.262 + ldp_ptr = lua_newuserdata(L, sizeof(LDAP *)); 1.263 + 1.264 + // set metatable of Lua userdata object: 1.265 + luaL_setmetatable(L, MLDAP_REGKEY "connection_metatable"); 1.266 + 1.267 + // write contents of Lua userdata object (the 'ldp' pointer): 1.268 + *ldp_ptr = ldp; 1.269 + 1.270 + // return Lua userdata object from top of stack: 1.271 + return 1; 1.272 + 1.273 +} 1.274 + 1.275 + 1.276 +static int mldap_search(lua_State *L) { 1.277 + // Lua C function used as "search" method of Lua userdata object representing the LDAP connection 1.278 + // that takes a Lua userdata object (the LDAP connection) as first argument, 1.279 + // a table with named arguments as second argument, 1.280 + // and returns a result table on success (see below) 1.281 + // or returns nil, an error string, and an error code (integer) on error 1.282 + 1.283 + // named arguments: 1.284 + // "base" (string) DN of the entry at which to start the search 1.285 + // "scope" (string) scope of the search, one of: 1.286 + // "base" to search the object itself 1.287 + // "onelevel" to search the object's immediate children 1.288 + // "subtree" to search the object and all its descendants 1.289 + // "children" to search all of the descendants 1.290 + // "filter" (string) string representation of the filter to apply in the search 1.291 + // (conforming to RFC 4515 as extended in RFC 4526) 1.292 + // "attrs" (table) list of attribute descriptions (each a string) to return from matching entries 1.293 + 1.294 + // structure of result table: 1.295 + // { 1.296 + // { dn = <distinguished name (DN)>, 1.297 + // <attr1> = { <value1>, <value2>, ... }, 1.298 + // <attr2> = { <value1>, <value2>, ... }, 1.299 + // ... 1.300 + // }, 1.301 + // { dn = <distinguished name (DN)>, 1.302 + // <attr1> = { <value1>, <value2>, ... }, 1.303 + // <attr2> = { <value1>, <value2>, ... }, 1.304 + // ... 1.305 + // }, 1.306 + // ... 1.307 + // } 1.308 + 1.309 + const char *base; // C string for "base" argument 1.310 + const char *scope_string; // C string for "scope" argument 1.311 + int scope; // integer representing the scope 1.312 + const char *filter; // C string for "filter" argument 1.313 + size_t nattrs; // number of attributes in "attrs" argument 1.314 + char **attrs; // C string array of "attrs" argument 1.315 + size_t attr_idx; // index variable for building the C string array of "attrs" 1.316 + int ldap_error; // LDAP error code (as returned by libldap calls) 1.317 + LDAP **ldp_ptr; // pointer to a pointer to the OpenLDAP structure representing the connection 1.318 + LDAPMessage *res; // pointer to the result of ldap_search_ext_s() call 1.319 + LDAPMessage *ent; // pointer to an entry in the result of ldap_search_ext_s() call 1.320 + int i; // integer to fill the Lua table returned as result 1.321 + 1.322 + // truncate the Lua stack to 2 elements: 1.323 + lua_settop(L, 2); 1.324 + 1.325 + // check if the first argument is a Lua userdata object with the correct metatable 1.326 + // and get a C pointer to that userdata object: 1.327 + ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable"); 1.328 + 1.329 + // throw an error, if the connection has already been closed: 1.330 + if (!*ldp_ptr) { 1.331 + return luaL_error(L, "LDAP connection has already been closed"); 1.332 + } 1.333 + 1.334 + // check if the second argument is a table, and throw an error if it's not a table: 1.335 + if (lua_type(L, 2) != LUA_TTABLE) { 1.336 + luaL_error(L, "Argument to function 'bind' is not a table."); 1.337 + } 1.338 + 1.339 + // extract named arguments (requires memory allocation for 'attrs'): 1.340 + base = mldap_get_named_string_arg(L, 2, "base", true); // pushed to 3 1.341 + scope_string = mldap_get_named_string_arg(L, 2, "scope", true); // pushed to 4 1.342 + scope = mldap_scope(L, scope_string); 1.343 + lua_pop(L, 1); // removes stack element 4 1.344 + filter = mldap_get_named_string_arg(L, 2, "filter", false); // pushed to 4 1.345 + lua_getfield(L, 2, "attrs"); // pushed to 5 1.346 + nattrs = lua_len(L, -1); 1.347 + attrs = calloc(nattrs + 1, sizeof(char *)); // memory allocation, +1 for terminating NULL 1.348 + if (!attrs) return luaL_error(L, "Memory allocation error in C function 'mldap_queryconn'"); 1.349 + for (attr_idx=0; attr_idx<nattrs; attr_idx++) { 1.350 + lua_pushinteger(L, attr_idx+1); 1.351 + lua_gettable(L, 5); // pushed onto variable stack position 1.352 + if (lua_type(L, -1) != LUA_TSTRING) { 1.353 + free(attrs); 1.354 + return luaL_error(L, "Element in attribute list is not a string"); 1.355 + } 1.356 + attrs[i] = lua_tostring(L, -1); 1.357 + } 1.358 + // attrs[nattrs] = NULL; // unnecessary due to calloc 1.359 + 1.360 + // execute LDAP search and store pointer to the result in 'res': 1.361 + ldap_error = ldap_search_ext_s( 1.362 + *ldp_ptr, // pointer to the opaque OpenLDAP structure representing the connection 1.363 + base, // DN of the entry at which to start the search 1.364 + scope, // scope of the search 1.365 + filter, // string representation of the filter to apply in the search 1.366 + attrs, // array of attribute descriptions (array of strings) 1.367 + 0, // do not only request attribute descriptions but also values 1.368 + NULL, // no server controls 1.369 + NULL, // no client controls 1.370 + NULL, // do not set another timeout 1.371 + 0, // no sizelimit 1.372 + &res // store result in 'res' 1.373 + ); 1.374 + 1.375 + // free data structures that have been allocated for the 'attrs' array: 1.376 + free(attrs); 1.377 + 1.378 + // return nil, an error string, and an error code (integer) in case of error: 1.379 + if (ldap_error != LDAP_SUCCESS) { 1.380 + lua_pushnil(L); 1.381 + lua_pushstring(L, ldap_err2string(ldap_error)); 1.382 + lua_pushinteger(L, ldap_error); 1.383 + return 3; 1.384 + } 1.385 + 1.386 + // clear Lua stack: 1.387 + lua_settop(L, 0); 1.388 + 1.389 + // create result table for all entries at stack position 1: 1.390 + lua_newtable(L); 1.391 + 1.392 + // use ldap_first_entry() and ldap_next_entry() functions 1.393 + // to iterate through all entries in the result 'res' 1.394 + // and count 'i' from 1 up: 1.395 + for ( 1.396 + ent=ldap_first_entry(*ldp_ptr, res), i=1; 1.397 + ent; 1.398 + ent=ldap_next_entry(*ldp_ptr, ent), i++ 1.399 + ) { 1.400 + 1.401 + char *attr; // name of attribute 1.402 + BerElement *ber; // structure to iterate through attributes 1.403 + char *dn; // LDAP entry name (distinguished name) 1.404 + 1.405 + // create result table for one entry at stack position 2: 1.406 + lua_newtable(L); 1.407 + 1.408 + // use ldap_first_attribute() and ldap_next_attribute() 1.409 + // as well as 'BerElement *ber' to iterate through all attributes 1.410 + // ('ber' must be free'd with ber_free(ber, 0) call later): 1.411 + for ( 1.412 + attr=ldap_first_attribute(*ldp_ptr, ent, &ber); 1.413 + attr; 1.414 + attr=ldap_next_attribute(*ldp_ptr, ent, ber) 1.415 + ) { 1.416 + 1.417 + struct berval **vals; // pointer to (first element of) array of values 1.418 + struct berval **val; // pointer to a value represented as 'struct berval' structure 1.419 + int j; // integer to fill a Lua table 1.420 + 1.421 + // push the attribute name to Lua stack position 3: 1.422 + lua_pushstring(L, attr); 1.423 + 1.424 + // create a new table for the attribute's values on Lua stack position 4: 1.425 + lua_newtable(L); 1.426 + 1.427 + // call ldap_get_values_len() to obtain the values 1.428 + // (required to be free'd later with ldap_value_free_len()): 1.429 + vals = ldap_get_values_len(*ldp_ptr, ent, attr); 1.430 + 1.431 + // iterate through values and count 'j' from 1 up: 1.432 + for (val=vals, j=1; *val; val++, j++) { 1.433 + 1.434 + // push value to Lua stack position 5: 1.435 + lua_pushlstring(L, (*val)->bv_val, (*val)->bv_len); 1.436 + 1.437 + // pop value from Lua stack position 5 1.438 + // and store it in table on Lua stack position 4: 1.439 + lua_rawseti(L, 4, j); 1.440 + 1.441 + } 1.442 + 1.443 + // free data structure of values: 1.444 + ldap_value_free_len(vals); 1.445 + 1.446 + // pop attribute name from Lua stack position 3 1.447 + // and pop value table from Lua stack position 4 1.448 + // and store them in result table for entry at Lua stack position 2: 1.449 + lua_settable(L, 2); 1.450 + 1.451 + } 1.452 + 1.453 + // free 'BerElement *ber' stucture that has been used to iterate through all attributes 1.454 + // (second argument is zero due to manpage of ldap_first_attribute()): 1.455 + ber_free(ber, 0); 1.456 + 1.457 + // store distinguished name (DN) on Lua stack position 3 1.458 + // (aquired memory is free'd with ldap_memfree()): 1.459 + dn = ldap_get_dn(*ldp_ptr, ent); 1.460 + lua_pushstring(L, dn); 1.461 + ldap_memfree(dn); 1.462 + 1.463 + // pop distinguished name (DN) from Lua stack position 3 1.464 + // and store it in field "dn" of entry result table at stack position 2 1.465 + lua_setfield(L, 2, "dn"); 1.466 + 1.467 + // pop entry result table from Lua stack position 2 1.468 + // and store it in table at stack position 1: 1.469 + lua_rawseti(L, 1, i); 1.470 + 1.471 + } 1.472 + 1.473 + // return result table from top of Lua stack (stack position 1): 1.474 + return 1; 1.475 + 1.476 +} 1.477 + 1.478 +static int mldap_unbind(lua_State *L) { 1.479 + // Lua C function used as "unbind" function of module and "unbind" method of Lua userdata object 1.480 + // closing the LDAP connection (if still open) 1.481 + // returning nothing 1.482 + 1.483 + LDAP **ldp_ptr; // pointer to a pointer to the OpenLDAP structure representing the connection 1.484 + 1.485 + // check if the first argument is a Lua userdata object with the correct metatable 1.486 + // and get a C pointer to that userdata object: 1.487 + ldp_ptr = luaL_checkudata(L, 1, MLDAP_REGKEY "connection_metatable"); 1.488 + 1.489 + // check if the Lua userdata object still contains a pointer: 1.490 + if (*ldp_ptr) { 1.491 + 1.492 + // if it does, then call ldap_unbind_ext_s(): 1.493 + ldap_unbind_ext_s( 1.494 + *ldp_ptr, // pointer to the opaque OpenLDAP structure representing the connection 1.495 + NULL, // no server controls 1.496 + NULL // no client controls 1.497 + ); 1.498 + 1.499 + // store NULL pointer in Lua userdata to mark connection as closed 1.500 + *ldp_ptr = NULL; 1.501 + } 1.502 + 1.503 + // returning nothing: 1.504 + return 0; 1.505 + 1.506 +} 1.507 + 1.508 + 1.509 +// registration information for library functions: 1.510 +static const struct luaL_Reg mldap_module_functions[] = { 1.511 + {"bind", mldap_bind}, 1.512 + {"unbind", mldap_unbind}, 1.513 + {NULL, NULL} 1.514 +}; 1.515 + 1.516 + 1.517 +// registration information for methods of connection object: 1.518 +static const struct luaL_Reg mldap_connection_methods[] = { 1.519 + {"search", mldap_search}, 1.520 + {"unbind", mldap_unbind}, 1.521 + {NULL, NULL} 1.522 +}; 1.523 + 1.524 + 1.525 +// registration information for connection metatable: 1.526 +static const struct luaL_Reg mldap_connection_metamethods[] = { 1.527 + {"__gc", mldap_unbind}, 1.528 + {NULL, NULL} 1.529 +}; 1.530 + 1.531 + 1.532 +// luaopen function to initialize/register library: 1.533 +int luaopen_mldap(lua_State *L) { 1.534 + 1.535 + // clear Lua stack: 1.536 + lua_settop(L, 0); 1.537 + 1.538 + // create table with library functions on Lua stack position 1: 1.539 + luaL_newlib(L, mldap_module_functions); 1.540 + 1.541 + // create metatable for connection objects on Lua stack position 2: 1.542 + luaL_newlib(L, mldap_connection_metamethods); 1.543 + 1.544 + // create table with methods for connection object on Lua stack position 3: 1.545 + luaL_newlib(L, mldap_connection_methods); 1.546 + 1.547 + // pop table with methods for connection object from Lua stack position 3 1.548 + // and store it as "__index" in metatable: 1.549 + lua_setfield(L, 2, "__index"); 1.550 + 1.551 + // pop table with metatable for connection objects from Lua stack position 2 1.552 + // and store it in the Lua registry: 1.553 + lua_setfield(L, LUA_REGISTRYINDEX, MLDAP_REGKEY "connection_metatable"); 1.554 + 1.555 + // create table for error code mappings on Lua stack position 2: 1.556 + lua_newtable(L); 1.557 + 1.558 + // fill table for error code mappings 1.559 + // that maps integer error codes to error code strings 1.560 + // and vice versa: 1.561 + mldap_set_errorcodes(L); 1.562 + 1.563 + // pop table for error code mappings from Lua stack position 2 1.564 + // and store it as "errorcodes" in table with library functions: 1.565 + lua_setfield(L, 1, "errorcodes"); 1.566 + 1.567 + // return table with library functions from top of Lua stack: 1.568 + return 1; 1.569 + 1.570 +}