dbconf.c 17 KB

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