dbconf.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. #include <string.h>
  42. #include <malloc.h>
  43. #include <ctype.h>
  44. #include <ldaputil/errors.h>
  45. #include <ldaputil/certmap.h>
  46. #include <ldaputil/encode.h>
  47. #include <ldaputil/dbconf.h>
  48. #define BIG_LINE 1024
  49. static const char *DB_DIRECTIVE = "directory";
  50. static const int DB_DIRECTIVE_LEN = 9; /* strlen("DB_DIRECTIVE") */
  51. static const char *ENCODED = "encoded";
  52. static void insert_dbinfo_propval(DBConfDBInfo_t *db_info,
  53. DBPropVal_t *propval)
  54. {
  55. if (db_info->lastprop) {
  56. db_info->lastprop->next = propval;
  57. }
  58. else {
  59. db_info->firstprop = propval;
  60. }
  61. db_info->lastprop = propval;
  62. }
  63. static void insert_dbconf_dbinfo(DBConfInfo_t *conf_info,
  64. DBConfDBInfo_t *db_info)
  65. {
  66. if (conf_info->lastdb) {
  67. conf_info->lastdb->next = db_info;
  68. }
  69. else {
  70. conf_info->firstdb = db_info;
  71. }
  72. conf_info->lastdb = db_info;
  73. }
  74. void dbconf_free_propval (DBPropVal_t *propval)
  75. {
  76. if (propval) {
  77. if (propval->prop) free(propval->prop);
  78. if (propval->val) free(propval->val);
  79. memset((void *)propval, 0, sizeof(DBPropVal_t));
  80. free(propval);
  81. }
  82. }
  83. NSAPI_PUBLIC void dbconf_free_dbinfo (DBConfDBInfo_t *db_info)
  84. {
  85. if (db_info) {
  86. DBPropVal_t *next;
  87. DBPropVal_t *cur;
  88. if (db_info->dbname) free(db_info->dbname);
  89. if (db_info->url) free(db_info->url);
  90. cur = db_info->firstprop;
  91. while(cur) {
  92. next = cur->next;
  93. dbconf_free_propval(cur);
  94. cur = next;
  95. }
  96. memset((void *)db_info, 0, sizeof(DBConfDBInfo_t));
  97. free(db_info);
  98. }
  99. }
  100. NSAPI_PUBLIC void dbconf_free_confinfo (DBConfInfo_t *conf_info)
  101. {
  102. DBConfDBInfo_t *next;
  103. DBConfDBInfo_t *cur;
  104. if (conf_info) {
  105. cur = conf_info->firstdb;
  106. while (cur) {
  107. next = cur->next;
  108. dbconf_free_dbinfo(cur);
  109. cur = next;
  110. }
  111. memset((void *)conf_info, 0, sizeof(DBConfInfo_t));
  112. free(conf_info);
  113. }
  114. }
  115. static int skip_blank_lines_and_spaces(FILE *fp, char *buf, char **ptr_out,
  116. int *eof)
  117. {
  118. char *ptr = buf;
  119. char *end;
  120. while(buf && (*buf || fgets(buf, BIG_LINE, fp))) {
  121. ptr = buf;
  122. /* skip leading whitespace */
  123. while(*ptr && isspace(*ptr)) ++ptr;
  124. /* skip blank line or comment */
  125. if (!*ptr || *ptr == '#') {
  126. *buf = 0; /* to force reading of next line */
  127. continue;
  128. }
  129. /* Non-blank line found */
  130. break;
  131. }
  132. *ptr_out = ptr;
  133. if (!*ptr) {
  134. *eof = 1;
  135. }
  136. else {
  137. /* skip trailing whitespace */
  138. end = ptr + strlen(ptr) - 1;
  139. while(isspace(*end)) *end-- = 0;
  140. }
  141. return LDAPU_SUCCESS;
  142. }
  143. static int dbconf_parse_propval (char *buf, char *ptr,
  144. DBConfDBInfo_t *db_info)
  145. {
  146. char *dbname = db_info->dbname;
  147. int dbname_len = strlen(dbname);
  148. char *prop;
  149. char *val;
  150. DBPropVal_t *propval;
  151. char *delimeter_chars = " \t";
  152. char *lastchar;
  153. int end_of_prop;
  154. char *encval = 0; /* encoded value */
  155. char *origprop = 0;
  156. if ((ptr - buf + dbname_len > BIG_LINE) ||
  157. strncmp(ptr, dbname, dbname_len) ||
  158. !(ptr[dbname_len] == ':' || isspace(ptr[dbname_len])))
  159. {
  160. /* Not a prop-val for the current db but not an error */
  161. return LDAPU_ERR_NOT_PROPVAL;
  162. }
  163. /* remove the last char if it is newline */
  164. lastchar = strrchr(buf, '\n');
  165. if (lastchar) *lastchar = '\0';
  166. prop = ptr + dbname_len + 1;
  167. while(*prop && (isspace(*prop) || *prop == ':')) ++prop;
  168. if (!*prop) {
  169. return LDAPU_ERR_PROP_IS_MISSING;
  170. }
  171. end_of_prop = strcspn(prop, delimeter_chars);
  172. if (prop[end_of_prop] != '\0') {
  173. /* buf doesn't end here -- val is present */
  174. prop[end_of_prop] = '\0';
  175. val = &prop[end_of_prop + 1];
  176. while(*val && isspace(*val)) ++val;
  177. if (*val == '\0') val = 0;
  178. }
  179. else {
  180. val = 0;
  181. }
  182. /*
  183. * The prop-val line could be one of the following:
  184. * "<dbname>:prop val" OR "<dbname>:encoded prop encval"
  185. * If (prop == "encoded") then the val has "prop encval".
  186. * Get the actual prop from val and get encval (i.e. encoded value)
  187. * and decode it. If it is encoded then the val part must be non-NULL.
  188. */
  189. if (val && *val && !strcmp(prop, ENCODED)) {
  190. /* val has the actual prop followed by the encoded value */
  191. origprop = prop;
  192. prop = val;
  193. while(*prop && (isspace(*prop) || *prop == ':')) ++prop;
  194. if (!*prop) {
  195. return LDAPU_ERR_PROP_IS_MISSING;
  196. }
  197. end_of_prop = strcspn(prop, delimeter_chars);
  198. if (prop[end_of_prop] != '\0') {
  199. /* buf doesn't end here -- encval is present */
  200. prop[end_of_prop] = '\0';
  201. encval = &prop[end_of_prop + 1];
  202. while(*encval && isspace(*encval)) ++encval;
  203. if (*encval == '\0') encval = 0;
  204. }
  205. else {
  206. encval = 0;
  207. }
  208. if (!encval) {
  209. /* special case - if encval is null, "encoded" itself is a
  210. * property and what we have in prop now is the value. */
  211. val = prop;
  212. prop = origprop;
  213. }
  214. else {
  215. /* decode the value */
  216. val = dbconf_decodeval(encval);
  217. }
  218. }
  219. /* Success - we have prop & val */
  220. propval = (DBPropVal_t *)malloc(sizeof(DBPropVal_t));
  221. if (!propval) return LDAPU_ERR_OUT_OF_MEMORY;
  222. memset((void *)propval, 0, sizeof(DBPropVal_t));
  223. propval->prop = strdup(prop);
  224. propval->val = val ? strdup(val) : 0;
  225. if (!propval->prop || (val && !propval->val)) {
  226. dbconf_free_propval(propval);
  227. return LDAPU_ERR_OUT_OF_MEMORY;
  228. }
  229. if (encval) free(val); /* val was allocated by dbconf_decodeval */
  230. insert_dbinfo_propval(db_info, propval);
  231. return LDAPU_SUCCESS;
  232. }
  233. static int dbconf_read_propval (FILE *fp, char *buf, DBConfDBInfo_t *db_info,
  234. int *eof)
  235. {
  236. int rv;
  237. char *ptr = buf;
  238. while(buf && (*buf || fgets(buf, BIG_LINE, fp))) {
  239. ptr = buf;
  240. rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof);
  241. if (rv != LDAPU_SUCCESS || *eof) return rv;
  242. /* We have a non-blank line which could be prop-val pair for the
  243. * dbname in the db_info. parse the prop-val pair and continue.
  244. */
  245. rv = dbconf_parse_propval(buf, ptr, db_info);
  246. if (rv == LDAPU_ERR_NOT_PROPVAL) return LDAPU_SUCCESS;
  247. if (rv != LDAPU_SUCCESS) return rv;
  248. *buf = 0; /* to force reading of next line */
  249. }
  250. if (!*buf) *eof = 1;
  251. return LDAPU_SUCCESS;
  252. }
  253. static int parse_directive(char *buf, const char *directive,
  254. const int directive_len,
  255. DBConfDBInfo_t **db_info_out)
  256. {
  257. DBConfDBInfo_t *db_info;
  258. char *dbname;
  259. char *url;
  260. int end_of_dbname;
  261. char *delimeter_chars = " \t";
  262. char *lastchar;
  263. /* remove the last char if it is newline */
  264. lastchar = strrchr(buf, '\n');
  265. if (lastchar) *lastchar = '\0';
  266. if (strncmp(buf, directive, directive_len) ||
  267. !isspace(buf[directive_len]))
  268. {
  269. return LDAPU_ERR_DIRECTIVE_IS_MISSING;
  270. }
  271. dbname = buf + directive_len + 1;
  272. while(*dbname && isspace(*dbname)) ++dbname;
  273. if (!*dbname) {
  274. return LDAPU_ERR_DBNAME_IS_MISSING;
  275. }
  276. end_of_dbname = strcspn(dbname, delimeter_chars);
  277. if (dbname[end_of_dbname] != '\0') {
  278. /* buf doesn't end here -- url is present */
  279. dbname[end_of_dbname] = '\0';
  280. url = &dbname[end_of_dbname + 1];
  281. while(*url && isspace(*url)) ++url;
  282. if (*url == '\0') url = 0;
  283. }
  284. else {
  285. url = 0;
  286. }
  287. /* Success - we have dbname & url */
  288. db_info = (DBConfDBInfo_t *)malloc(sizeof(DBConfDBInfo_t));
  289. if (!db_info) return LDAPU_ERR_OUT_OF_MEMORY;
  290. memset((void *)db_info, 0, sizeof(DBConfDBInfo_t));
  291. db_info->dbname = strdup(dbname);
  292. db_info->url = url ? strdup(url) : 0;
  293. if (!db_info->dbname || (url && !db_info->url)) {
  294. dbconf_free_dbinfo(db_info);
  295. return LDAPU_ERR_OUT_OF_MEMORY;
  296. }
  297. *db_info_out = db_info;
  298. return LDAPU_SUCCESS;
  299. }
  300. /* Read the next database info from the file and put it in db_info_out. The
  301. * buf may contain first line of the database info. When this function
  302. * finishes, the buf may contain unprocessed information (which should be
  303. * passed to the next call to read_db_info).
  304. */
  305. static int read_db_info (FILE *fp, char *buf, DBConfDBInfo_t **db_info_out,
  306. const char *directive, const int directive_len,
  307. int *eof)
  308. {
  309. char *ptr;
  310. DBConfDBInfo_t *db_info;
  311. int rv;
  312. *db_info_out = 0;
  313. rv = skip_blank_lines_and_spaces(fp, buf, &ptr, eof);
  314. if (rv != LDAPU_SUCCESS || *eof) return rv;
  315. /* We possibly have a directive of the form "directory <name> <url>" */
  316. rv = parse_directive(ptr, directive, directive_len, &db_info);
  317. if (rv != LDAPU_SUCCESS) return rv;
  318. /* We have parsed the directive successfully -- lets look for additional
  319. * property-value pairs for the database.
  320. */
  321. if (!fgets(buf, BIG_LINE, fp)) {
  322. *eof = 1;
  323. rv = LDAPU_SUCCESS;
  324. }
  325. else {
  326. rv = dbconf_read_propval(fp, buf, db_info, eof);
  327. }
  328. if (rv != LDAPU_SUCCESS) {
  329. dbconf_free_dbinfo(db_info);
  330. *db_info_out = 0;
  331. }
  332. else {
  333. *db_info_out = db_info;
  334. }
  335. return rv;
  336. }
  337. int dbconf_read_config_file_sub (const char *file,
  338. const char *directive,
  339. const int directive_len,
  340. DBConfInfo_t **conf_info_out)
  341. {
  342. FILE *fp;
  343. DBConfInfo_t *conf_info;
  344. DBConfDBInfo_t *db_info;
  345. char buf[BIG_LINE];
  346. int rv;
  347. int eof;
  348. buf[0] = 0;
  349. #ifdef XP_WIN32
  350. if ((fp = fopen(file, "rt")) == NULL)
  351. #else
  352. if ((fp = fopen(file, "r")) == NULL)
  353. #endif
  354. {
  355. return LDAPU_ERR_CANNOT_OPEN_FILE;
  356. }
  357. /* Allocate DBConfInfo_t */
  358. conf_info = (DBConfInfo_t *)malloc(sizeof(DBConfInfo_t));
  359. if (!conf_info) {
  360. fclose(fp);
  361. return LDAPU_ERR_OUT_OF_MEMORY;
  362. }
  363. memset((void *)conf_info, 0, sizeof(DBConfInfo_t));
  364. /* Read each db info */
  365. eof = 0;
  366. while(!eof &&
  367. ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS))
  368. {
  369. insert_dbconf_dbinfo(conf_info, db_info);
  370. }
  371. if (rv != LDAPU_SUCCESS) {
  372. dbconf_free_confinfo(conf_info);
  373. *conf_info_out = 0;
  374. }
  375. else {
  376. *conf_info_out = conf_info;
  377. }
  378. fclose(fp);
  379. return rv;
  380. }
  381. NSAPI_PUBLIC int dbconf_read_config_file (const char *file, DBConfInfo_t **conf_info_out)
  382. {
  383. return dbconf_read_config_file_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN,
  384. conf_info_out);
  385. }
  386. int dbconf_read_default_dbinfo_sub (const char *file,
  387. const char *directive,
  388. const int directive_len,
  389. DBConfDBInfo_t **db_info_out)
  390. {
  391. FILE *fp;
  392. DBConfDBInfo_t *db_info;
  393. char buf[BIG_LINE];
  394. int rv;
  395. int eof;
  396. buf[0] = 0;
  397. #ifdef XP_WIN32
  398. if ((fp = fopen(file, "rt")) == NULL)
  399. #else
  400. if ((fp = fopen(file, "r")) == NULL)
  401. #endif
  402. {
  403. return LDAPU_ERR_CANNOT_OPEN_FILE;
  404. }
  405. /* Read each db info until eof or dbname == default*/
  406. eof = 0;
  407. while(!eof &&
  408. ((rv = read_db_info(fp, buf, &db_info, directive, directive_len, &eof)) == LDAPU_SUCCESS))
  409. {
  410. if (!strcmp(db_info->dbname, DBCONF_DEFAULT_DBNAME)) break;
  411. dbconf_free_dbinfo(db_info);
  412. }
  413. if (rv != LDAPU_SUCCESS) {
  414. *db_info_out = 0;
  415. }
  416. else {
  417. *db_info_out = db_info;
  418. }
  419. fclose(fp);
  420. return rv;
  421. }
  422. NSAPI_PUBLIC int dbconf_read_default_dbinfo (const char *file,
  423. DBConfDBInfo_t **db_info_out)
  424. {
  425. return dbconf_read_default_dbinfo_sub(file, DB_DIRECTIVE, DB_DIRECTIVE_LEN,
  426. db_info_out);
  427. }
  428. /*
  429. * ldapu_strcasecmp - is like strcasecmp on UNIX but also accepts null strings.
  430. */
  431. int ldapu_strcasecmp (const char *s1, const char *s2)
  432. {
  433. #ifdef XP_WIN32
  434. int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */
  435. #endif
  436. if (!s1) return !s2 ? 0 : 0-tolower(*s2);
  437. else if (!s2) return tolower(*s1);
  438. #ifdef XP_WIN32
  439. while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; }
  440. if (!*s1)
  441. return *s2 ? 0-tolower(*s2) : 0;
  442. else if (!*s2)
  443. return tolower(*s1);
  444. else
  445. return ls1 - ls2;
  446. #else
  447. return strcasecmp(s1, s2);
  448. #endif
  449. }
  450. NSAPI_PUBLIC int ldapu_dbinfo_attrval (DBConfDBInfo_t *db_info,
  451. const char *attr, char **val)
  452. {
  453. /* Look for given attr in the db_info and return its value */
  454. int rv = LDAPU_ATTR_NOT_FOUND;
  455. DBPropVal_t *next;
  456. *val = 0;
  457. if (db_info) {
  458. next = db_info->firstprop;
  459. while (next) {
  460. rv = ldapu_strcasecmp(attr, next->prop);
  461. if (!rv) {
  462. /* Found the property */
  463. *val = next->val ? strdup(next->val) : 0;
  464. if (next->val && !*val) {
  465. rv = LDAPU_ERR_OUT_OF_MEMORY;
  466. }
  467. else {
  468. rv = LDAPU_SUCCESS;
  469. }
  470. break;
  471. }
  472. next = next->next;
  473. }
  474. }
  475. return rv;
  476. }
  477. void dbconf_print_propval (DBPropVal_t *propval)
  478. {
  479. if (propval) {
  480. fprintf(stderr, "\tprop: \"%s\"\tval: \"%s\"\n", propval->prop,
  481. propval->val ? propval->val : "");
  482. }
  483. else {
  484. fprintf(stderr, "Null propval\n");
  485. }
  486. }
  487. void dbconf_print_dbinfo (DBConfDBInfo_t *db_info)
  488. {
  489. DBPropVal_t *next;
  490. if (db_info) {
  491. fprintf(stderr, "dbname: \"%s\"\n", db_info->dbname);
  492. fprintf(stderr, "url: \t\"%s\"\n", db_info->url ? db_info->url : "");
  493. next = db_info->firstprop;
  494. while (next) {
  495. dbconf_print_propval(next);
  496. next = next->next;
  497. }
  498. }
  499. else {
  500. fprintf(stderr, "Null db_info\n");
  501. }
  502. }
  503. void dbconf_print_confinfo (DBConfInfo_t *conf_info)
  504. {
  505. DBConfDBInfo_t *next;
  506. if (conf_info) {
  507. next = conf_info->firstdb;
  508. while (next) {
  509. dbconf_print_dbinfo(next);
  510. next = next->next;
  511. }
  512. }
  513. else {
  514. fprintf(stderr, "Null conf_info\n");
  515. }
  516. }
  517. NSAPI_PUBLIC int dbconf_output_db_directive (FILE *fp, const char *dbname,
  518. const char *url)
  519. {
  520. fprintf(fp, "%s %s %s\n", DB_DIRECTIVE, dbname, url);
  521. return LDAPU_SUCCESS;
  522. }
  523. NSAPI_PUBLIC int dbconf_output_propval (FILE *fp, const char *dbname,
  524. const char *prop, const char *val, const int encoded)
  525. {
  526. if (encoded && val && *val) {
  527. char *new_val = dbconf_encodeval(val);
  528. if (!new_val) return LDAPU_ERR_OUT_OF_MEMORY;
  529. fprintf(fp, "%s:%s %s %s\n", dbname, ENCODED,
  530. prop, new_val);
  531. free(new_val);
  532. }
  533. else {
  534. fprintf(fp, "%s:%s %s\n", dbname, prop, val ? val : "");
  535. }
  536. return LDAPU_SUCCESS;
  537. }
  538. NSAPI_PUBLIC int dbconf_get_dbnames (const char *dbmap, char ***dbnames_out, int *cnt_out)
  539. {
  540. DBConfInfo_t *conf_info = 0;
  541. DBConfDBInfo_t *db = 0;
  542. int cnt = 0;
  543. char **dbnames = 0;
  544. char *heap = 0;
  545. int rv;
  546. *dbnames_out = 0;
  547. *cnt_out = 0;
  548. rv = dbconf_read_config_file(dbmap, &conf_info);
  549. if (rv != LDAPU_SUCCESS) return rv;
  550. db = conf_info->firstdb;
  551. dbnames = (char **)malloc(32*1024);
  552. heap = (char *)dbnames + 2*1024;
  553. if (!dbnames) {
  554. dbconf_free_confinfo(conf_info);
  555. return LDAPU_ERR_OUT_OF_MEMORY;
  556. }
  557. *dbnames_out = dbnames;
  558. while(db) {
  559. *dbnames++ = heap;
  560. strcpy(heap, db->dbname);
  561. heap += strlen(db->dbname)+1;
  562. db = db->next;
  563. cnt++;
  564. }
  565. *dbnames = NULL;
  566. *cnt_out = cnt;
  567. dbconf_free_confinfo(conf_info);
  568. return LDAPU_SUCCESS;
  569. }
  570. NSAPI_PUBLIC int dbconf_free_dbnames (char **dbnames)
  571. {
  572. if (dbnames)
  573. free(dbnames);
  574. return LDAPU_SUCCESS;
  575. }