2 ** Copyright 2000-2004 Double Precision, Inc. See COPYING for
3 ** distribution information.
12 #include <sys/types.h>
17 #include "authpgsql.h"
18 #include "authpgsqlrc.h"
20 #include "courierauthdebug.h"
22 #define err courier_auth_err
24 /* tom@minnesota.com */
25 #define MAX_SUBSTITUTION_LEN 32
26 #define SV_BEGIN_MARK "$("
27 #define SV_END_MARK ")"
28 #define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1)
29 #define SV_END_LEN ((sizeof(SV_END_MARK))-1)
31 static const char rcsid
[]="$Id: authpgsqllib.c,v 1.18 2006/10/28 19:22:52 mrsam Exp $";
33 /* tom@minnesota.com */
41 /* tom@minnesota.com */
42 typedef int (*parsefunc
)(const char *, size_t, void *);
44 static const char *read_env(const char *env
)
46 static char *pgsqlauth
=0;
47 static size_t pgsqlauth_size
=0;
54 FILE *f
=fopen(AUTHPGSQLRC
, "r");
58 if (fstat(fileno(f
), &buf
) ||
59 (pgsqlauth
=malloc(buf
.st_size
+2)) == 0)
64 if (fread(pgsqlauth
, buf
.st_size
, 1, f
) != 1)
71 pgsqlauth
[pgsqlauth_size
=buf
.st_size
]=0;
73 for (i
=0; i
<pgsqlauth_size
; i
++)
74 if (pgsqlauth
[i
] == '\n')
75 { /* tom@minnesota.com */
76 if (!i
|| pgsqlauth
[i
-1] != '\\')
82 pgsqlauth
[i
]=pgsqlauth
[i
-1]= ' ';
88 for (i
=0; i
<pgsqlauth_size
; )
91 if (memcmp(p
, env
, l
) == 0 &&
92 isspace((int)(unsigned char)p
[l
]))
95 while (*p
&& *p
!= '\n' &&
96 isspace((int)(unsigned char)*p
))
101 while (i
< pgsqlauth_size
)
102 if (pgsqlauth
[i
++] == 0) break;
105 if (i
< pgsqlauth_size
)
110 static PGresult
*pgresult
=0;
112 static PGconn
*pgconn
=0;
115 static FILE *DEBUG=0;
118 static int do_connect()
122 const char *password
;
123 const char *database
;
124 const char *server_port
=0;
125 const char *server_opt
=0;
128 DEBUG=fopen("/tmp/courier.debug","a");
130 fprintf(DEBUG,"Apro il DB!\n");
135 ** Periodically detect dead connections.
139 static time_t last_time
=0;
144 if (t_check
< last_time
)
145 last_time
=t_check
; /* System clock changed */
147 if (t_check
< last_time
+ 60)
152 if (PQstatus(pgconn
) == CONNECTION_OK
) return (0);
154 DPRINTF("authpgsqllib: PQstatus failed, connection lost");
159 server
=read_env("PGSQL_HOST");
160 server_port
=read_env("PGSQL_PORT");
161 userid
=read_env("PGSQL_USERNAME");
162 password
=read_env("PGSQL_PASSWORD");
163 database
=read_env("PGSQL_DATABASE");
164 server_opt
=read_env("PGSQL_OPT");
167 fprintf(DEBUG,"Letti i parametri!\n");
173 err("authpgsql: PGSQL_USERNAME not set in "
180 err("authpgsql: PGSQL_DATABASE not set in "
186 fprintf(DEBUG,"Connecting to db:%s:%s\%s user=%s pass=%s\n",server,server_port,database,userid,password);
189 pgconn
= PQsetdbLogin(server
, server_port
, server_opt
, NULL
, database
,userid
,password
);
191 if (PQstatus(pgconn
) == CONNECTION_BAD
)
193 err("Connection to server '%s' userid '%s' database '%s' failed.",
194 server
? server
: "<null>",
195 userid
? userid
: "<null>",
197 err("%s", PQerrorMessage(pgconn
));
202 fprintf(DEBUG,"Connected!\n");
210 void auth_pgsql_cleanup()
219 static struct authpgsqluserinfo ui
={0, 0, 0, 0, 0, 0, 0, 0};
221 static void append_username(char *p
, const char *username
,
222 const char *defdomain
)
224 for (strcpy(p
, username
); *p
; p
++)
225 if (*p
== '\'' || *p
== '"' || *p
== '\\' ||
226 (int)(unsigned char)*p
< ' ')
227 *p
=' '; /* No funny business */
228 if (strchr(username
, '@') == 0 && defdomain
&& *defdomain
)
229 strcat(strcpy(p
, "@"), defdomain
);
232 /* tom@minnesota.com */
233 static struct var_data
*get_variable (const char *begin
, size_t len
,
234 struct var_data
*vdt
)
236 struct var_data
*vdp
;
238 if (!begin
|| !vdt
) /* should never happend */
240 err("authpgsql: critical error while "
241 "parsing substitution variable");
246 err("authpgsql: unknown empty substitution "
247 "variable - aborting");
250 if (len
> MAX_SUBSTITUTION_LEN
)
252 err("authpgsql: variable name too long "
253 "while parsing substitution. "
256 "%.*s...", MAX_SUBSTITUTION_LEN
, begin
);
260 for (vdp
=vdt
; vdp
->name
; vdp
++)
261 if (vdp
->size
== len
+1 &&
262 !strncmp(begin
, vdp
->name
, len
))
266 if (!vdp
->value_length
) /* length cache */
267 vdp
->value_length
= strlen (vdp
->value
);
271 err("authpgsql: unknown substitution variable "
280 /* tom@minnesota.com */
281 static int ParsePlugin_counter (const char *p
, size_t length
, void *vp
)
283 if (!p
|| !vp
|| length
< 0)
285 err("authpgsql: bad arguments while counting "
290 *((size_t *)vp
) += length
;
295 /* tom@minnesota.com */
296 static int ParsePlugin_builder (const char *p
, size_t length
, void *vp
)
298 char **strptr
= (char **) vp
;
300 if (!p
|| !vp
|| length
< 0)
302 err("authpgsql: bad arguments while building "
307 if (!length
) return 0;
308 memcpy ((void *) *strptr
, (void *) p
, length
);
314 /* tom@minnesota.com */
315 static int parse_core (const char *source
, struct var_data
*vdt
,
316 parsefunc outfn
, void *result
)
320 const char *p
, *q
, *e
,
323 struct var_data
*v_ptr
;
329 err("authpgsql: no memory allocated for result "
330 "while parser core was invoked");
335 err("authpgsql: no substitution table found "
336 "while parser core was invoked");
341 while ( (p
=strstr(q
, SV_BEGIN_MARK
)) )
343 e
= strstr (p
, SV_END_MARK
);
346 err("authpgsql: syntax error in "
348 "- no closing symbol found! "
349 "bad variable begins with:"
350 "%.*s...", MAX_SUBSTITUTION_LEN
, p
);
356 ** __________sometext$(variable_name)_________
358 ** t_begin' t_end' `v_begin `v_end
362 v_begin
= p
+SV_BEGIN_LEN
; /* variable field ptr */
363 v_end
= e
-SV_END_LEN
; /* variable field last character */
364 v_size
= v_end
-v_begin
+1;/* variable field length */
366 t_begin
= q
; /* text field ptr */
367 t_end
= p
-1; /* text field last character */
368 t_size
= t_end
-t_begin
+1;/* text field length */
371 if ( (outfn (t_begin
, t_size
, result
)) == -1 )
374 /* work on variable */
375 v_ptr
= get_variable (v_begin
, v_size
, vdt
);
376 if (!v_ptr
) return -1;
378 if ( (outfn (v_ptr
->value
, v_ptr
->value_length
, result
)) == -1 )
384 /* work on last part of text if any */
386 if ( (outfn (q
, strlen(q
), result
)) == -1 )
392 /* tom@minnesota.com */
393 static char *parse_string (const char *source
, struct var_data
*vdt
)
395 struct var_data
*vdp
= NULL
;
396 char *output_buf
= NULL
,
400 if (source
== NULL
|| *source
== '\0' ||
401 vdt
== NULL
|| vdt
[0].name
== NULL
)
403 err("authpgsql: source clause is empty "
404 "- this is critical error");
408 /* zero var_data length cache - important! */
409 for (vdp
=vdt
; vdp
->name
; vdp
++)
410 vdp
->value_length
= 0;
413 /* phase 1 - count and validate string */
414 if ( (parse_core (source
, vdt
, &ParsePlugin_counter
, &buf_size
)) != 0)
417 /* phase 2 - allocate memory */
418 output_buf
= malloc (buf_size
);
424 pass_buf
= output_buf
;
426 /* phase 3 - build the output string */
427 if ( (parse_core (source
, vdt
, &ParsePlugin_builder
, &pass_buf
)) != 0)
437 /* tom@minnesota.com */
438 static const char *get_localpart (const char *username
)
441 const char *l_end
, *p
;
443 static char localpart_buf
[130];
445 if (!username
|| *username
== '\0') return NULL
;
447 p
= strchr(username
,'@');
450 if ((p
-username
) > 128)
456 if ((lbuf
= strlen(username
)) > 128)
458 l_end
= username
+ lbuf
;
464 while (*p
&& p
!= l_end
)
465 if (*p
== '\"' || *p
== '\\' ||
466 *p
== '\'' || (int)(unsigned char)*p
< ' ')
472 return localpart_buf
;
475 /* tom@minnesota.com */
476 static const char *get_domain (const char *username
, const char *defdomain
)
478 static char domain_buf
[260];
482 if (!username
|| *username
== '\0') return NULL
;
483 p
= strchr(username
,'@');
485 if (!p
|| *(p
+1) == '\0')
487 if (defdomain
&& *defdomain
)
494 if ((strlen(p
)) > 256)
499 if (*p
== '\"' || *p
== '\\' ||
500 *p
== '\'' || (int)(unsigned char)*p
< ' ')
509 /* tom@minnesota.com */
511 static const char *validate_password (const char *password
)
513 static char pass_buf
[2][540]; /* Use two buffers, see parse_chpass_clause */
514 static int next_pass
=0;
518 if (!password
|| *password
== '\0' || (strlen(password
)) > 256)
521 next_pass
= 1-next_pass
;
524 q
= pass_buf
[next_pass
];
525 endq
= q
+ sizeof pass_buf
[next_pass
];
527 while (*p
&& q
< endq
)
529 if (*p
== '\"' || *p
== '\\' || *p
== '\'')
538 return pass_buf
[next_pass
];
541 /* tom@minnesota.com */
542 static char *parse_select_clause (const char *clause
, const char *username
,
543 const char *defdomain
,
546 static struct var_data vd
[]={
547 {"local_part", NULL
, sizeof("local_part"), 0},
548 {"domain", NULL
, sizeof("domain"), 0},
549 {"service", NULL
, sizeof("service"), 0},
552 if (clause
== NULL
|| *clause
== '\0' ||
553 !username
|| *username
== '\0')
556 vd
[0].value
= get_localpart (username
);
557 vd
[1].value
= get_domain (username
, defdomain
);
558 if (!vd
[0].value
|| !vd
[1].value
)
560 vd
[2].value
= service
;
562 return (parse_string (clause
, vd
));
565 /* tom@minnesota.com */
566 static char *parse_chpass_clause (const char *clause
, const char *username
,
567 const char *defdomain
, const char *newpass
,
568 const char *newpass_crypt
)
570 static struct var_data vd
[]={
571 {"local_part", NULL
, sizeof("local_part"), 0},
572 {"domain", NULL
, sizeof("domain"), 0},
573 {"newpass", NULL
, sizeof("newpass"), 0},
574 {"newpass_crypt", NULL
, sizeof("newpass_crypt"), 0},
577 if (clause
== NULL
|| *clause
== '\0' ||
578 !username
|| *username
== '\0' ||
579 !newpass
|| *newpass
== '\0' ||
580 !newpass_crypt
|| *newpass_crypt
== '\0') return NULL
;
582 vd
[0].value
= get_localpart (username
);
583 vd
[1].value
= get_domain (username
, defdomain
);
584 vd
[2].value
= validate_password (newpass
);
585 vd
[3].value
= validate_password (newpass_crypt
);
587 if (!vd
[0].value
|| !vd
[1].value
||
588 !vd
[2].value
|| !vd
[3].value
) return NULL
;
590 return (parse_string (clause
, vd
));
612 memset(&ui
, 0, sizeof(ui
));
615 struct authpgsqluserinfo
*auth_pgsql_getuserinfo(const char *username
,
618 const char *defdomain
, *select_clause
;
621 static const char query
[]=
622 "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = '";
624 if (do_connect()) return (0);
629 fprintf(DEBUG,"1Leggo parametri\n");
632 select_clause
=read_env("PGSQL_SELECT_CLAUSE");
633 defdomain
=read_env("DEFAULT_DOMAIN");
634 if (!defdomain
) defdomain
="";
636 if (!select_clause
) /* tom@minnesota.com */
638 const char *user_table
,
651 user_table
=read_env("PGSQL_USER_TABLE");
655 err("authpgsql: PGSQL_USER_TABLE not set in "
660 crypt_field
=read_env("PGSQL_CRYPT_PWFIELD");
661 clear_field
=read_env("PGSQL_CLEAR_PWFIELD");
662 name_field
=read_env("PGSQL_NAME_FIELD");
664 if (!crypt_field
&& !clear_field
)
666 err("authpgsql: PGSQL_CRYPT_PWFIELD and "
667 "PGSQL_CLEAR_PWFIELD not set in " AUTHPGSQLRC
".");
670 if (!crypt_field
) crypt_field
="''";
671 if (!clear_field
) clear_field
="''";
672 if (!name_field
) name_field
="''";
674 uid_field
= read_env("PGSQL_UID_FIELD");
675 if (!uid_field
) uid_field
= "uid";
677 gid_field
= read_env("PGSQL_GID_FIELD");
678 if (!gid_field
) gid_field
= "gid";
680 login_field
= read_env("PGSQL_LOGIN_FIELD");
681 if (!login_field
) login_field
= "id";
683 home_field
= read_env("PGSQL_HOME_FIELD");
684 if (!home_field
) home_field
= "home";
686 maildir_field
=read_env(service
&& strcmp(service
, "courier")==0
687 ? "PGSQL_DEFAULTDELIVERY"
688 : "PGSQL_MAILDIR_FIELD");
689 if (!maildir_field
) maildir_field
="''";
691 quota_field
=read_env("PGSQL_QUOTA_FIELD");
692 if (!quota_field
) quota_field
="''";
694 options_field
=read_env("PGSQL_AUXOPTIONS_FIELD");
695 if (!options_field
) options_field
="''";
697 where_clause
=read_env("PGSQL_WHERE_CLAUSE");
698 if (!where_clause
) where_clause
= "";
700 querybuf
=malloc(sizeof(query
) + 100
701 + 2 * strlen(login_field
)
702 + strlen(crypt_field
)
703 + strlen(clear_field
)
704 + strlen(uid_field
) + strlen(gid_field
)
706 + strlen(maildir_field
)
707 + strlen(quota_field
)
709 + strlen(options_field
)
713 + strlen(where_clause
));
721 sprintf(querybuf
, query
, login_field
, crypt_field
, clear_field
,
722 uid_field
, gid_field
, home_field
, maildir_field
,
726 user_table
, login_field
);
727 p
=querybuf
+strlen(querybuf
);
729 append_username(p
, username
, defdomain
);
732 if (strcmp(where_clause
, "")) {
734 strcat(p
, where_clause
);
740 /* tom@minnesota.com */
741 querybuf
=parse_select_clause (select_clause
, username
,
745 DPRINTF("authpgsql: parse_select_clause failed (DEFAULT_DOMAIN not defined?)");
750 DPRINTF("SQL query: %s", querybuf
);
751 pgresult
= PQexec(pgconn
, querybuf
);
752 if (!pgresult
|| PQresultStatus(pgresult
) != PGRES_TUPLES_OK
)
754 DPRINTF("PQexec failed, reconnecting: %s", PQerrorMessage(pgconn
));
755 if (pgresult
) PQclear(pgresult
);
757 /* <o.blasnik@nextra.de> */
759 auth_pgsql_cleanup();
767 pgresult
= PQexec(pgconn
, querybuf
);
768 if (!pgresult
|| PQresultStatus(pgresult
) != PGRES_TUPLES_OK
)
770 DPRINTF("PQexec failed second time, giving up: %s", PQerrorMessage(pgconn
));
771 if (pgresult
) PQclear(pgresult
);
773 auth_pgsql_cleanup();
774 /* Server went down, that's OK,
775 ** try again next time.
782 if (PQntuples(pgresult
)>0)
785 int num_fields
= PQnfields(pgresult
);
789 DPRINTF("incomplete row, only %d fields returned",
795 t
=PQgetvalue(pgresult
,0,0);
796 if (t
&& t
[0]) ui
.username
=strdup(t
);
797 t
=PQgetvalue(pgresult
,0,1);
798 if (t
&& t
[0]) ui
.cryptpw
=strdup(t
);
799 t
=PQgetvalue(pgresult
,0,2);
800 if (t
&& t
[0]) ui
.clearpw
=strdup(t
);
801 t
=PQgetvalue(pgresult
,0,3);
803 (ui
.uid
=strtol(t
, &endp
, 10), endp
[0] != '\0'))
805 DPRINTF("invalid value for uid: '%s'",
810 t
=PQgetvalue(pgresult
,0,4);
812 (ui
.gid
=strtol(t
, &endp
, 10), endp
[0] != '\0'))
814 DPRINTF("invalid value for gid: '%s'",
819 t
=PQgetvalue(pgresult
,0,5);
824 DPRINTF("required value for 'home' (column 6) is missing");
828 t
=num_fields
> 6 ? PQgetvalue(pgresult
,0,6) : 0;
829 if (t
&& t
[0]) ui
.maildir
=strdup(t
);
830 t
=num_fields
> 7 ? PQgetvalue(pgresult
,0,7) : 0;
831 if (t
&& t
[0]) ui
.quota
=strdup(t
);
832 t
=num_fields
> 8 ? PQgetvalue(pgresult
,0,8) : 0;
833 if (t
&& t
[0]) ui
.fullname
=strdup(t
);
834 t
=num_fields
> 9 ? PQgetvalue(pgresult
,0,9) : 0;
835 if (t
&& t
[0]) ui
.options
=strdup(t
);
839 DPRINTF("zero rows returned");
848 int auth_pgsql_setpass(const char *user
, const char *pass
,
858 const char *clear_field
=NULL
;
859 const char *crypt_field
=NULL
;
860 const char *defdomain
=NULL
;
861 const char *where_clause
=NULL
;
862 const char *user_table
=NULL
;
863 const char *login_field
=NULL
;
864 const char *chpass_clause
=NULL
; /* tom@minnesota.com */
870 if (!(newpass_crypt
=authcryptpasswd(pass
, oldpass
)))
873 for (l
=0, p
=pass
; *p
; p
++)
875 if ((int)(unsigned char)*p
< ' ')
880 if (*p
== '"' || *p
== '\\')
885 /* tom@minnesota.com */
886 chpass_clause
=read_env("PGSQL_CHPASS_CLAUSE");
887 defdomain
=read_env("DEFAULT_DOMAIN");
888 user_table
=read_env("PGSQL_USER_TABLE");
891 login_field
= read_env("PGSQL_LOGIN_FIELD");
892 if (!login_field
) login_field
= "id";
893 crypt_field
=read_env("PGSQL_CRYPT_PWFIELD");
894 clear_field
=read_env("PGSQL_CLEAR_PWFIELD");
895 where_clause
=read_env("PGSQL_WHERE_CLAUSE");
896 sql_buf
=malloc(strlen(crypt_field
? crypt_field
:"")
897 + strlen(clear_field
? clear_field
:"")
898 + strlen(defdomain
? defdomain
:"")
899 + strlen(login_field
) + l
+ strlen(newpass_crypt
)
901 + strlen(where_clause
? where_clause
:"")
906 sql_buf
=parse_chpass_clause(chpass_clause
,
919 if (!chpass_clause
) /* tom@minnesota.com */
921 sprintf(sql_buf
, "UPDATE %s SET", user_table
);
925 if (clear_field
&& *clear_field
)
929 strcat(strcat(strcat(sql_buf
, " "), clear_field
),
932 q
=sql_buf
+strlen(sql_buf
);
935 if (*pass
== '"' || *pass
== '\\')
943 if (crypt_field
&& *crypt_field
)
945 strcat(strcat(strcat(strcat(strcat(strcat(sql_buf
, comma
),
954 strcat(strcat(strcat(sql_buf
, " WHERE "),
958 append_username(sql_buf
+strlen(sql_buf
), user
, defdomain
);
960 strcat(sql_buf
, "'");
962 if (where_clause
&& *where_clause
)
964 strcat(sql_buf
, " AND (");
965 strcat(sql_buf
, where_clause
);
966 strcat(sql_buf
, ")");
969 } /* end of: if (!chpass_clause) */
971 if (courier_authdebug_login_level
>= 2)
973 DPRINTF("setpass SQL: %s", sql_buf
);
975 pgresult
=PQexec (pgconn
, sql_buf
);
976 if (!pgresult
|| PQresultStatus(pgresult
) != PGRES_COMMAND_OK
)
978 DPRINTF("setpass SQL failed");
980 auth_pgsql_cleanup();
987 void auth_pgsql_enumerate( void(*cb_func
)(const char *name
,
996 const char *select_clause
, *defdomain
;
999 static const char query
[]=
1000 "SELECT %s, %s, %s, %s, %s, %s FROM %s WHERE 1=1";
1003 if (do_connect()) return;
1007 select_clause
=read_env("PGSQL_ENUMERATE_CLAUSE");
1008 defdomain
=read_env("DEFAULT_DOMAIN");
1009 if (!defdomain
|| !defdomain
[0])
1010 defdomain
="*"; /* otherwise parse_select_clause fails */
1012 if (!select_clause
) /* tom@minnesota.com */
1014 const char *user_table
,
1023 user_table
=read_env("PGSQL_USER_TABLE");
1027 err("authpgsql: PGSQL_USER_TABLE not set in "
1032 uid_field
= read_env("PGSQL_UID_FIELD");
1033 if (!uid_field
) uid_field
= "uid";
1035 gid_field
= read_env("PGSQL_GID_FIELD");
1036 if (!gid_field
) gid_field
= "gid";
1038 login_field
= read_env("PGSQL_LOGIN_FIELD");
1039 if (!login_field
) login_field
= "id";
1041 home_field
= read_env("PGSQL_HOME_FIELD");
1042 if (!home_field
) home_field
= "home";
1044 maildir_field
=read_env("PGSQL_MAILDIR_FIELD");
1045 if (!maildir_field
) maildir_field
="''";
1047 options_field
=read_env("PGSQL_AUXOPTIONS_FIELD");
1048 if (!options_field
) options_field
="''";
1050 where_clause
=read_env("PGSQL_WHERE_CLAUSE");
1051 if (!where_clause
) where_clause
= "";
1053 querybuf
=malloc(sizeof(query
) + 100
1054 + strlen(login_field
)
1055 + strlen(uid_field
) + strlen(gid_field
)
1056 + strlen(home_field
)
1057 + strlen(maildir_field
)
1058 + strlen(options_field
)
1059 + strlen(user_table
)
1060 + strlen(where_clause
));
1068 sprintf(querybuf
, query
, login_field
,
1069 uid_field
, gid_field
, home_field
, maildir_field
,
1070 options_field
, user_table
);
1071 p
=querybuf
+strlen(querybuf
);
1073 if (strcmp(where_clause
, "")) {
1074 strcat(p
, " AND (");
1075 strcat(p
, where_clause
);
1081 /* tom@minnesota.com */
1082 querybuf
=parse_select_clause (select_clause
, "*",
1083 defdomain
, "enumerate");
1086 DPRINTF("authpgsql: parse_select_clause failed");
1090 DPRINTF("authpgsql: enumerate query: %s", querybuf
);
1092 if (PQsendQuery(pgconn
, querybuf
) == 0)
1094 DPRINTF("PQsendQuery failed, reconnecting: %s",PQerrorMessage(pgconn
));
1096 auth_pgsql_cleanup();
1104 if (PQsendQuery(pgconn
, querybuf
) == 0)
1106 DPRINTF("PQsendQuery failed second time, giving up: %s",PQerrorMessage(pgconn
));
1108 auth_pgsql_cleanup();
1114 while ((pgresult
= PQgetResult(pgconn
)) != NULL
)
1116 if (PQresultStatus(pgresult
) != PGRES_TUPLES_OK
)
1118 DPRINTF("pgsql error during enumeration: %s",PQerrorMessage(pgconn
));
1123 for (n
=PQntuples(pgresult
), i
=0; i
<n
; i
++)
1125 const char *username
;
1128 const char *homedir
;
1129 const char *maildir
;
1130 const char *options
;
1132 username
=PQgetvalue(pgresult
,i
,0);
1133 uid
=atol(PQgetvalue(pgresult
,i
,1));
1134 gid
=atol(PQgetvalue(pgresult
,i
,2));
1135 homedir
=PQgetvalue(pgresult
,i
,3);
1136 maildir
=PQgetvalue(pgresult
,i
,4);
1137 options
=PQgetvalue(pgresult
,i
,5);
1139 if (!username
|| !*username
|| !homedir
|| !*homedir
)
1142 if (maildir
&& !*maildir
)
1145 (*cb_func
)(username
, uid
, gid
, homedir
,
1146 maildir
, options
, void_arg
);
1151 /* FIXME: is it possible that a NULL result from PQgetResult could
1152 indicate an error rather than EOF? The documentation is not clear */
1153 (*cb_func
)(NULL
, 0, 0, NULL
, NULL
, NULL
, void_arg
);