hcoop release
[hcoop/debian/courier-authlib.git] / authmysqllib.c
CommitLineData
d9898ee8 1/*
b0322a85 2** Copyright 2000-2010 Double Precision, Inc. See COPYING for
d9898ee8 3** distribution information.
4*/
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10#include <ctype.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#include <mysql.h>
14#include <time.h>
15
16#include "authmysql.h"
17#include "authmysqlrc.h"
18#include "auth.h"
19#include "courierauthdebug.h"
20
21#define err courier_auth_err
22
d9898ee8 23static const char *read_env(const char *env)
24{
b0322a85
CE
25 return authgetconfig(AUTHMYSQLRC, env);
26}
d9898ee8 27
b0322a85 28static MYSQL mysql_buf;
d9898ee8 29
b0322a85 30static MYSQL *mysql=0;
d9898ee8 31
b0322a85
CE
32static char *escape_str(const char *c, size_t n)
33{
34 char *buf=malloc(n*2+1);
d9898ee8 35
b0322a85
CE
36 if (!buf)
37 {
38 perror("malloc");
39 return NULL;
d9898ee8 40 }
41
b0322a85
CE
42 mysql_real_escape_string(mysql, buf, c, n);
43 return buf;
d9898ee8 44}
45
ac40fd9e 46static void set_session_options(void)
47/*
48* session variables can be set once for the whole session
49*/
50{
51/* Anton Dobkin <anton@viansib.ru>, VIAN, Ltd. */
52#if MYSQL_VERSION_ID >= 41000
53 const char *character_set=read_env("MYSQL_CHARACTER_SET"), *check;
54
55 if(character_set){
56
57 /*
58 * This function works like the SET NAMES statement, but also sets
59 * the value of mysql->charset, and thus affects the character set
60 * used by mysql_real_escape_string()
61 *
62 * (return value apparently work the opposite of what is documented)
63 */
64 mysql_set_character_set(mysql, character_set);
65 check = mysql_character_set_name(mysql);
66 if (strcmp(character_set, check) != 0)
67 {
68 err("Cannot set MySQL character set \"%s\", working with \"%s\"\n",
69 character_set, check);
70 }
71 else
72 {
73 DPRINTF("Install of a character set for MySQL: %s", character_set);
74 }
75 }
76#endif /* 41000 */
77}
78
d9898ee8 79static int do_connect()
80{
81const char *server;
82const char *userid;
83const char *password;
84const char *database;
85const char *server_socket=0;
86unsigned int server_port=0;
87unsigned int server_opt=0;
88const char *p;
89
90const char *sslkey;
91const char *sslcert;
92const char *sslcacert;
93const char *sslcapath;
94const char *sslcipher;
95unsigned int use_ssl=0;
96
97/*
98** Periodically detect dead connections.
99*/
100 if (mysql)
101 {
102 static time_t last_time=0;
103 time_t t_check;
104
105 time(&t_check);
106
107 if (t_check < last_time)
108 last_time=t_check; /* System clock changed */
109
110 if (t_check < last_time + 60)
111 return (0);
112
113 last_time=t_check;
114
115 if (mysql_ping(mysql) == 0) return (0);
116
117 DPRINTF("authmysqllib: mysql_ping failed, connection lost");
118 mysql_close(mysql);
119 mysql=0;
120 }
121
122 server=read_env("MYSQL_SERVER");
123 userid=read_env("MYSQL_USERNAME");
124 password=read_env("MYSQL_PASSWORD");
125 database=read_env("MYSQL_DATABASE");
126
127#if MYSQL_VERSION_ID >= 32200
128 sslkey=read_env("MYSQL_SSL_KEY");
129 sslcert=read_env("MYSQL_SSL_CERT");
130 sslcacert=read_env("MYSQL_SSL_CACERT");
131 sslcapath=read_env("MYSQL_SSL_CAPATH");
132 sslcipher=read_env("MYSQL_SSL_CIPHER");
133
134 if ((sslcert != NULL) && ((sslcacert != NULL) || (sslcapath != NULL)))
135 {
136 use_ssl=1;
137 }
138#endif
139
140 server_socket=(char *) read_env("MYSQL_SOCKET");
141
142 if ((p=read_env("MYSQL_PORT")) != 0)
143 {
144 server_port=(unsigned int) atoi(p);
145 }
146
147 if ((p=read_env("MYSQL_OPT")) != 0)
148 {
149 server_opt=(unsigned int) atol(p);
150 }
151
152 if (!server && !server_socket)
153 {
154 err("authmysql: MYSQL_SERVER nor MYSQL_SOCKET set in"
155 AUTHMYSQLRC ".");
156 return (-1);
157 }
158
159 if (!userid)
160 {
161 err("authmysql: MYSQL_USERNAME not set in "
162 AUTHMYSQLRC ".");
163 return (-1);
164 }
165
166 if (!database)
167 {
168 err("authmysql: MYSQL_DATABASE not set in "
169 AUTHMYSQLRC ".");
170 return (-1);
171 }
172
173#if MYSQL_VERSION_ID >= 32200
174 mysql_init(&mysql_buf);
175 if (use_ssl)
176 {
177 mysql_ssl_set(&mysql_buf, sslkey, sslcert, sslcacert,
178 sslcapath, sslcipher);
179 }
180 mysql=mysql_real_connect(&mysql_buf, server, userid, password,
181 NULL,
182 server_port,
183 server_socket,
184 server_opt);
185#else
186 mysql=mysql_connect(&mysql_buf, server, userid, password);
187#endif
188 if (!mysql)
189 {
190 err("failed to connect to mysql server (server=%s, userid=%s): %s",
191 server ? server : "<null>",
192 userid ? userid : "<null>",
193 mysql_error(&mysql_buf));
194 return (-1);
195 }
196
197 if (mysql_select_db(mysql, database))
198 {
199 err("authmysql: mysql_select_db(%s) error: %s",
200 database, mysql_error(mysql));
201 mysql_close(mysql);
202 mysql=0;
203 return (-1);
204 }
ac40fd9e 205
206 DPRINTF("authmysqllib: connected. Versions: "
207 "header %lu, "
208 "client %lu, "
209 "server %lu",
210 (long)MYSQL_VERSION_ID,
211 mysql_get_client_version(),
212 mysql_get_server_version(mysql));
213
214 set_session_options();
d9898ee8 215 return (0);
216}
217
218void auth_mysql_cleanup()
219{
220 if (mysql)
221 {
222 mysql_close(mysql);
223 mysql=0;
224 }
225}
226
227static struct authmysqluserinfo ui={0, 0, 0, 0, 0, 0, 0, 0};
228
229static void initui()
230{
231
232 if (ui.username)
233 free(ui.username);
234 if (ui.cryptpw)
235 free(ui.cryptpw);
236 if (ui.clearpw)
237 free(ui.clearpw);
238 if (ui.home)
239 free(ui.home);
240 if (ui.maildir)
241 free(ui.maildir);
242 if (ui.quota)
243 free(ui.quota);
244 if (ui.fullname)
245 free(ui.fullname);
246 if (ui.options)
247 free(ui.options);
248 memset(&ui, 0, sizeof(ui));
249}
250
d9898ee8 251struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username,
252 const char *service)
253{
254const char *defdomain =NULL;
ac40fd9e 255char *querybuf;
d9898ee8 256MYSQL_ROW row;
257MYSQL_RES *result;
258int num_fields;
259char *endp;
260
261const char *select_clause; /* siefca@pld.org.pl */
262
ac40fd9e 263#define DEFAULT_SELECT_QUERY "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = '%s%s%s' %s%s%s", \
264 login_field, crypt_field, clear_field, uid_field,\
265 gid_field, home_field, maildir_field, quota_field,\
266 name_field, options_field, user_table, login_field,\
267 username_escaped,\
268 has_domain || !*defdomain ? "":"@", has_domain ? "":defdomain, \
269 *where_clause ? " AND (":"", where_clause,\
270 *where_clause ? ")":""
d9898ee8 271
272 if (do_connect()) return (0);
273
274 initui();
275
276 select_clause=read_env("MYSQL_SELECT_CLAUSE");
277 defdomain=read_env("DEFAULT_DOMAIN");
278 if (!defdomain) defdomain="";
279
280 if (!select_clause) /* siefca@pld.org.pl */
281 {
282 const char *user_table,
283 *crypt_field,
284 *clear_field,
285 *name_field,
286 *uid_field,
287 *gid_field,
288 *login_field,
289 *home_field,
290 *maildir_field,
291 *quota_field,
292 *options_field,
293 *where_clause;
ac40fd9e 294 char *username_escaped;
295 size_t query_size;
296 char dummy_buf[1];
297 int has_domain;
d9898ee8 298
299 user_table=read_env("MYSQL_USER_TABLE");
300
301 if (!user_table)
302 {
303 err("authmysql: MYSQL_USER_TABLE not set in "
304 AUTHMYSQLRC ".");
305 return (0);
306 }
307
308 crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
309 clear_field=read_env("MYSQL_CLEAR_PWFIELD");
310 name_field=read_env("MYSQL_NAME_FIELD");
311
312 if (!crypt_field && !clear_field)
313 {
314 err("authmysql: MYSQL_CRYPT_PWFIELD and "
315 "MYSQL_CLEAR_PWFIELD not set in " AUTHMYSQLRC ".");
316 return (0);
317 }
318 if (!crypt_field) crypt_field="\"\"";
319 if (!clear_field) clear_field="\"\"";
320 if (!name_field) name_field="\"\"";
321
322 uid_field = read_env("MYSQL_UID_FIELD");
323 if (!uid_field) uid_field = "uid";
324
325 gid_field = read_env("MYSQL_GID_FIELD");
326 if (!gid_field) gid_field = "gid";
327
328 login_field = read_env("MYSQL_LOGIN_FIELD");
329 if (!login_field) login_field = "id";
330
331 home_field = read_env("MYSQL_HOME_FIELD");
332 if (!home_field) home_field = "home";
333
334 maildir_field=read_env(service && strcmp(service, "courier")==0
335 ? "MYSQL_DEFAULTDELIVERY"
336 : "MYSQL_MAILDIR_FIELD");
337 if (!maildir_field) maildir_field="\"\"";
338
339 quota_field=read_env("MYSQL_QUOTA_FIELD");
340 if (!quota_field) quota_field="\"\"";
341
342 options_field=read_env("MYSQL_AUXOPTIONS_FIELD");
343 if (!options_field) options_field="\"\"";
344
345 where_clause=read_env("MYSQL_WHERE_CLAUSE");
346 if (!where_clause) where_clause = "";
347
ac40fd9e 348 username_escaped=malloc(strlen(username)*2+1);
d9898ee8 349
ac40fd9e 350 if (!username_escaped)
d9898ee8 351 {
352 perror("malloc");
353 return (0);
354 }
355
ac40fd9e 356 mysql_real_escape_string(mysql, username_escaped,
357 username, strlen(username));
d9898ee8 358
ac40fd9e 359 has_domain=strchr(username, '@') != NULL;
d9898ee8 360
ac40fd9e 361 query_size=snprintf(dummy_buf, 1, DEFAULT_SELECT_QUERY);
362
363 querybuf=malloc(query_size+1);
364
365 if (!querybuf)
366 {
367 free(username_escaped);
368 perror("malloc");
369 return(0);
d9898ee8 370 }
ac40fd9e 371
372 snprintf(querybuf, query_size+1, DEFAULT_SELECT_QUERY);
373 free(username_escaped);
374
d9898ee8 375 }
376 else
377 {
378 /* siefca@pld.org.pl */
b0322a85
CE
379 querybuf=auth_parse_select_clause (escape_str,
380 select_clause, username,
381 defdomain, service);
d9898ee8 382 if (!querybuf)
383 {
384 DPRINTF("parse_select_clause failed (DEFAULT_DOMAIN not set?)");
385 return 0;
386 }
387 }
388
389 DPRINTF("SQL query: %s", querybuf);
390 if (mysql_query (mysql, querybuf))
391 {
392 /* <o.blasnik@nextra.de> */
393
394 DPRINTF("mysql_query failed, reconnecting: %s", mysql_error(mysql));
395 auth_mysql_cleanup();
396
397 if (do_connect())
398 {
399 free(querybuf);
400 return (0);
401 }
402
403 if (mysql_query (mysql, querybuf))
404 {
405 DPRINTF("mysql_query failed second time, giving up: %s", mysql_error(mysql));
406 free(querybuf);
407 auth_mysql_cleanup();
408 /* Server went down, that's OK,
409 ** try again next time.
410 */
411 return (0);
412 }
413 }
414 free(querybuf);
415
416 result = mysql_store_result (mysql);
417 if (result)
418 {
419 if (mysql_num_rows(result))
420 {
421 row = mysql_fetch_row (result);
422 num_fields = mysql_num_fields (result);
423
424 if (num_fields < 6)
425 {
426 DPRINTF("incomplete row, only %d fields returned",
427 num_fields);
428 mysql_free_result(result);
429 return(0);
430 }
431
432 if (row[0] && row[0][0])
433 ui.username=strdup(row[0]);
434 if (row[1] && row[1][0])
435 ui.cryptpw=strdup(row[1]);
436 if (row[2] && row[2][0])
437 ui.clearpw=strdup(row[2]);
438 /* perhaps authmysql needs a glob_uid/glob_gid feature
439 like authldap? */
440 if (!row[3] || !row[3][0] ||
441 (ui.uid=strtol(row[3], &endp, 10), endp[0] != '\0'))
442 {
443 DPRINTF("invalid value for uid: '%s'",
444 row[3] ? row[3] : "<null>");
445 mysql_free_result(result);
446 return 0;
447 }
448 if (!row[4] || !row[4][0] ||
449 (ui.gid=strtol(row[4], &endp, 10), endp[0] != '\0'))
450 {
451 DPRINTF("invalid value for gid: '%s'",
452 row[4] ? row[4] : "<null>");
453 mysql_free_result(result);
454 return 0;
455 }
456 if (row[5] && row[5][0])
457 ui.home=strdup(row[5]);
458 else
459 {
460 DPRINTF("required value for 'home' (column 6) is missing");
461 mysql_free_result(result);
462 return(0);
463 }
464 if (num_fields > 6 && row[6] && row[6][0])
465 ui.maildir=strdup(row[6]);
466 if (num_fields > 7 && row[7] && row[7][0])
467 ui.quota=strdup(row[7]);
468 if (num_fields > 8 && row[8] && row[8][0])
469 ui.fullname=strdup(row[8]);
470 if (num_fields > 9 && row[9] && row[9][0])
471 ui.options=strdup(row[9]);
472 }
473 else
474 {
475 DPRINTF("zero rows returned");
476 mysql_free_result(result);
477 return (&ui); /* User not found */
478 }
479 }
480 else
481 {
482 DPRINTF("mysql_store_result failed");
483 return (0);
484 }
485 mysql_free_result(result);
486 return (&ui);
487}
488
489int auth_mysql_setpass(const char *user, const char *pass,
490 const char *oldpass)
491{
492 char *newpass_crypt;
d9898ee8 493 char *sql_buf;
d9898ee8 494 int rc=0;
495
ac40fd9e 496 char *clear_escaped;
497 char *crypt_escaped;
498
d9898ee8 499 const char *clear_field =NULL,
500 *crypt_field =NULL,
501 *defdomain =NULL,
502 *where_clause =NULL,
503 *user_table =NULL,
504 *login_field =NULL,
505 *chpass_clause =NULL; /* siefca@pld.org.pl */
506
ac40fd9e 507 if (do_connect()) return (-1);
d9898ee8 508
509 if (!(newpass_crypt=authcryptpasswd(pass, oldpass)))
510 return (-1);
511
ac40fd9e 512 clear_escaped=malloc(strlen(pass)*2+1);
513
514 if (!clear_escaped)
d9898ee8 515 {
ac40fd9e 516 perror("malloc");
517 free(newpass_crypt);
518 return -1;
d9898ee8 519 }
520
ac40fd9e 521 crypt_escaped=malloc(strlen(newpass_crypt)*2+1);
522
523 if (!crypt_escaped)
524 {
525 perror("malloc");
526 free(clear_escaped);
527 free(newpass_crypt);
528 return -1;
529 }
530
531 mysql_real_escape_string(mysql, clear_escaped, pass, strlen(pass));
532 mysql_real_escape_string(mysql, crypt_escaped,
533 newpass_crypt, strlen(newpass_crypt));
534
d9898ee8 535 /* siefca@pld.org.pl */
536 chpass_clause=read_env("MYSQL_CHPASS_CLAUSE");
537 defdomain=read_env("DEFAULT_DOMAIN");
538 user_table=read_env("MYSQL_USER_TABLE");
539 if (!chpass_clause)
540 {
ac40fd9e 541 int has_domain=strchr(user, '@') != NULL;
542 char *username_escaped;
543 char dummy_buf[1];
544 size_t sql_buf_size;
545
546 username_escaped=malloc(strlen(user)*2+1);
547
548 if (!username_escaped)
549 {
550 perror("malloc");
551 free(clear_escaped);
552 free(crypt_escaped);
553 free(newpass_crypt);
554 return -1;
555 }
556
557 mysql_real_escape_string(mysql, username_escaped,
558 user, strlen(user));
559
d9898ee8 560 login_field = read_env("MYSQL_LOGIN_FIELD");
561 if (!login_field) login_field = "id";
562 crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
563 clear_field=read_env("MYSQL_CLEAR_PWFIELD");
564 where_clause=read_env("MYSQL_WHERE_CLAUSE");
d9898ee8 565
ac40fd9e 566 if (!where_clause)
567 where_clause="";
d9898ee8 568
ac40fd9e 569 if (!crypt_field)
570 crypt_field="";
d9898ee8 571
ac40fd9e 572 if (!clear_field)
573 clear_field="";
d9898ee8 574
b0322a85
CE
575 if (!defdomain)
576 defdomain="";
d9898ee8 577
ac40fd9e 578#define DEFAULT_SETPASS_UPDATE \
579 "UPDATE %s SET %s%s%s%s %s %s%s%s%s WHERE %s='%s%s%s' %s%s%s", \
580 user_table, \
581 *clear_field ? clear_field:"", \
582 *clear_field ? "='":"", \
583 *clear_field ? clear_escaped:"", \
584 *clear_field ? "'":"", \
585 \
586 *clear_field && *crypt_field ? ",":"", \
587 \
588 *crypt_field ? crypt_field:"", \
589 *crypt_field ? "='":"", \
590 *crypt_field ? crypt_escaped:"", \
591 *crypt_field ? "'":"", \
592 login_field, \
593 username_escaped, \
594 has_domain || !*defdomain ? "":"@", \
595 has_domain ? "":defdomain, \
596 *where_clause ? " AND (":"", where_clause, \
597 *where_clause ? ")":""
d9898ee8 598
d9898ee8 599
ac40fd9e 600 sql_buf_size=snprintf(dummy_buf, 1, DEFAULT_SETPASS_UPDATE);
d9898ee8 601
ac40fd9e 602 sql_buf=malloc(sql_buf_size+1);
d9898ee8 603
ac40fd9e 604 if (sql_buf)
605 snprintf(sql_buf, sql_buf_size+1,
606 DEFAULT_SETPASS_UPDATE);
d9898ee8 607
ac40fd9e 608 free(username_escaped);
d9898ee8 609 }
610 else
611 {
b0322a85
CE
612 sql_buf=auth_parse_chpass_clause(escape_str,
613 chpass_clause,
614 user,
615 defdomain,
616 clear_escaped,
617 crypt_escaped);
d9898ee8 618 }
ac40fd9e 619
620 free(clear_escaped);
621 free(crypt_escaped);
622 free(newpass_crypt);
d9898ee8 623
624 if (courier_authdebug_login_level >= 2)
625 {
626 DPRINTF("setpass SQL: %s", sql_buf);
627 }
628 if (mysql_query (mysql, sql_buf))
629 {
630 DPRINTF("setpass SQL failed");
631 rc= -1;
632 auth_mysql_cleanup();
633 }
634 free(sql_buf);
635 return (rc);
636}
637
638void auth_mysql_enumerate( void(*cb_func)(const char *name,
639 uid_t uid,
640 gid_t gid,
641 const char *homedir,
642 const char *maildir,
643 const char *options,
644 void *void_arg),
645 void *void_arg)
646{
647 const char *defdomain, *select_clause;
ac40fd9e 648 char *querybuf;
d9898ee8 649 MYSQL_ROW row;
650 MYSQL_RES *result;
651
b0322a85
CE
652 if (do_connect())
653 {
654 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
655 return;
656 }
d9898ee8 657
658 initui();
659
660 select_clause=read_env("MYSQL_ENUMERATE_CLAUSE");
661 defdomain=read_env("DEFAULT_DOMAIN");
662 if (!defdomain || !defdomain[0])
663 defdomain="*"; /* otherwise parse_select_clause fails */
664
665 if (!select_clause)
666 {
667 const char *user_table,
668 *uid_field,
669 *gid_field,
670 *login_field,
671 *home_field,
672 *maildir_field,
673 *options_field,
674 *where_clause;
ac40fd9e 675 char dummy_buf[1];
676 size_t query_len;
d9898ee8 677
678 user_table=read_env("MYSQL_USER_TABLE");
679
680 if (!user_table)
681 {
682 err("authmysql: MYSQL_USER_TABLE not set in "
683 AUTHMYSQLRC ".");
684 return;
685 }
686
687 uid_field = read_env("MYSQL_UID_FIELD");
688 if (!uid_field) uid_field = "uid";
689
690 gid_field = read_env("MYSQL_GID_FIELD");
691 if (!gid_field) gid_field = "gid";
692
693 login_field = read_env("MYSQL_LOGIN_FIELD");
694 if (!login_field) login_field = "id";
695
696 home_field = read_env("MYSQL_HOME_FIELD");
697 if (!home_field) home_field = "home";
698
699 maildir_field=read_env("MYSQL_MAILDIR_FIELD");
700 if (!maildir_field) maildir_field="\"\"";
701
702 options_field=read_env("MYSQL_AUXOPTIONS_FIELD");
703 if (!options_field) options_field="\"\"";
704
705 where_clause=read_env("MYSQL_WHERE_CLAUSE");
706 if (!where_clause) where_clause = "";
707
ac40fd9e 708
709#define DEFAULT_ENUMERATE_QUERY \
710 "SELECT %s, %s, %s, %s, %s, %s FROM %s %s%s",\
711 login_field, uid_field, gid_field, \
712 home_field, maildir_field, \
713 options_field, user_table, \
714 *where_clause ? " WHERE ":"", \
715 where_clause
716
717 query_len=snprintf(dummy_buf, 1, DEFAULT_ENUMERATE_QUERY);
718
719 querybuf=malloc(query_len+1);
d9898ee8 720
721 if (!querybuf)
722 {
723 perror("malloc");
724 return;
725 }
726
ac40fd9e 727 snprintf(querybuf, query_len+1, DEFAULT_ENUMERATE_QUERY);
d9898ee8 728 }
729 else
730 {
731 /* siefca@pld.org.pl */
b0322a85
CE
732 querybuf=auth_parse_select_clause (escape_str,
733 select_clause, "*",
734 defdomain, "enumerate");
d9898ee8 735 if (!querybuf)
736 {
737 DPRINTF("authmysql: parse_select_clause failed");
738 return;
739 }
740 }
741 DPRINTF("authmysql: enumerate query: %s", querybuf);
742
743 if (mysql_query (mysql, querybuf))
744 {
745 DPRINTF("mysql_query failed, reconnecting: %s", mysql_error(mysql));
746 /* <o.blasnik@nextra.de> */
747
748 auth_mysql_cleanup();
749
750 if (do_connect())
751 {
752 free(querybuf);
753 return;
754 }
755
756 if (mysql_query (mysql, querybuf))
757 {
758 DPRINTF("mysql_query failed second time, giving up: %s", mysql_error(mysql));
759 free(querybuf);
760 auth_mysql_cleanup();
761 return;
762 }
763 }
764 free(querybuf);
765
766 result = mysql_use_result (mysql);
767 if (result)
768 {
769 const char *username;
770 uid_t uid;
771 gid_t gid;
772 const char *homedir;
773 const char *maildir;
774 const char *options;
775
776 while ((row = mysql_fetch_row (result)) != NULL)
777 {
778 if(!row[0] || !row[0][0]
ac40fd9e 779 || !row[1] || !row[1][0]
780 || !row[2] || !row[2][0]
781 || !row[3] || !row[3][0])
d9898ee8 782 {
783 continue;
784 }
785
786 username=row[0];
787 uid=atol(row[1]); /* FIXME use strtol to validate */
788 gid=atol(row[2]);
789 homedir=row[3];
790 maildir=row[4];
791 options=row[5];
792
793 if (maildir && !*maildir)
794 maildir=NULL;
795
796 (*cb_func)(username, uid, gid, homedir,
797 maildir, options, void_arg);
798 }
799 }
800 /* NULL row could indicate end of result or an error */
801 if (mysql_errno(mysql))
802 {
803 DPRINTF("mysql error during enumeration: %s", mysql_error(mysql));
804 }
805 else
806 (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
807
808 if (result) mysql_free_result(result);
809}