/*++
/* NAME
/*	verify_clnt 3
/* SUMMARY
/*	address verification client interface
/* SYNOPSIS
/*	#include <verify_clnt.h>
/*
/*	int	verify_clnt_query(addr, status, why)
/*	const char *addr;
/*	int	*status;
/*	VSTRING	*why;
/*
/*	int	verify_clnt_update(addr, status, why)
/*	const char *addr;
/*	int	status;
/*	const char *why;
/* DESCRIPTION
/*	verify_clnt_query() requests information about the given address.
/*	The result value is one of the valid status values (see
/*	status description below).
/*	In all cases the \fBwhy\fR argument provides additional
/*	information.
/*
/*	verify_clnt_update() requests that the status of the specified
/*	address be updated. The result status is DEL_REQ_RCPT_STAT_OK upon
/*	success, DEL_REQ_RCPT_STAT_DEFER upon failure.
/*
/*	Arguments
/* .IP addr
/*	The email address in question.
/* .IP status
/*	One of the following status codes:
/* .RS
/* .IP DEL_REQ_RCPT_STAT_OK
/*	The mail system did not detect any problems.
/* .IP DEL_REQ_RCPT_STAT_DEFER
/*	The status of the address is indeterminate.
/* .IP DEL_REQ_RCPT_STAT_BOUNCE
/*	The address is permanently undeliverable.
/* .RE
/* .IP why
/*	textual description of the status.
/* DIAGNOSTICS
/*	These functions return VRFY_STAT_OK in case of success,
/*	VRFY_STAT_BAD in case of a malformed request, and
/*	VRFY_STAT_FAIL when the operation failed.
/* SEE ALSO
/*	verify(8) Postfix address verification server
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <unistd.h>
#include <errno.h>

/* Utility library. */

#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <attr.h>

/* Global library. */

#include <mail_params.h>
#include <mail_proto.h>
#include <clnt_stream.h>
#include <verify_clnt.h>

CLNT_STREAM *vrfy_clnt;

/* verify_clnt_handshake - receive server protocol announcement */

static int verify_clnt_handshake(VSTREAM *stream)
{
    return (attr_scan(stream, ATTR_FLAG_STRICT,
		   RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_VERIFY),
		      ATTR_TYPE_END));
}

/* verify_clnt_init - initialize */

static void verify_clnt_init(void)
{
    if (vrfy_clnt != 0)
	msg_panic("verify_clnt_init: multiple initialization");
    vrfy_clnt = clnt_stream_create(MAIL_CLASS_PRIVATE, var_verify_service,
				   var_ipc_idle_limit, var_ipc_ttl_limit,
				   verify_clnt_handshake);
}

/* verify_clnt_query - request address verification status */

int     verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
{
    VSTREAM *stream;
    int     request_status;
    int     count = 0;

    /*
     * Do client-server plumbing.
     */
    if (vrfy_clnt == 0)
	verify_clnt_init();

    /*
     * Request status for this address.
     */
    for (;;) {
	stream = clnt_stream_access(vrfy_clnt);
	errno = 0;
	count += 1;
	if (stream == 0
	    || attr_print(stream, ATTR_FLAG_NONE,
			  SEND_ATTR_STR(MAIL_ATTR_REQ, VRFY_REQ_QUERY),
			  SEND_ATTR_STR(MAIL_ATTR_ADDR, addr),
			  ATTR_TYPE_END) != 0
	    || vstream_fflush(stream)
	    || attr_scan(stream, ATTR_FLAG_MISSING,
			 RECV_ATTR_INT(MAIL_ATTR_STATUS, &request_status),
			 RECV_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status),
			 RECV_ATTR_STR(MAIL_ATTR_WHY, why),
			 ATTR_TYPE_END) != 3) {
	    if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
		msg_warn("problem talking to service %s: %m",
			 var_verify_service);
	} else {
	    break;
	}
	sleep(1);
	clnt_stream_recover(vrfy_clnt);
    }
    return (request_status);
}

/* verify_clnt_update - request address status update */

int     verify_clnt_update(const char *addr, int addr_status, const char *why)
{
    VSTREAM *stream;
    int     request_status;

    /*
     * Do client-server plumbing.
     */
    if (vrfy_clnt == 0)
	verify_clnt_init();

    /*
     * Send status for this address. Supply a default status if the address
     * verification service is unavailable.
     */
    for (;;) {
	stream = clnt_stream_access(vrfy_clnt);
	errno = 0;
	if (stream == 0
	    || attr_print(stream, ATTR_FLAG_NONE,
			  SEND_ATTR_STR(MAIL_ATTR_REQ, VRFY_REQ_UPDATE),
			  SEND_ATTR_STR(MAIL_ATTR_ADDR, addr),
			  SEND_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status),
			  SEND_ATTR_STR(MAIL_ATTR_WHY, why),
			  ATTR_TYPE_END) != 0
	    || attr_scan(stream, ATTR_FLAG_MISSING,
			 RECV_ATTR_INT(MAIL_ATTR_STATUS, &request_status),
			 ATTR_TYPE_END) != 1) {
	    if (msg_verbose || (errno != EPIPE && errno != ENOENT))
		msg_warn("problem talking to service %s: %m",
			 var_verify_service);
	} else {
	    break;
	}
	sleep(1);
	clnt_stream_recover(vrfy_clnt);
    }
    return (request_status);
}

 /*
  * Proof-of-concept test client program.
  */
#ifdef TEST

#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <msg_vstream.h>
#include <stringops.h>
#include <vstring_vstream.h>
#include <mail_conf.h>

#define STR(x) vstring_str(x)

static NORETURN usage(char *myname)
{
    msg_fatal("usage: %s [-v]", myname);
}

static void query(char *query, VSTRING *buf)
{
    int     status;

    switch (verify_clnt_query(query, &status, buf)) {
    case VRFY_STAT_OK:
	vstream_printf("%-10s %d\n", "status", status);
	vstream_printf("%-10s %s\n", "text", STR(buf));
	vstream_fflush(VSTREAM_OUT);
	break;
    case VRFY_STAT_BAD:
	msg_warn("bad request format");
	break;
    case VRFY_STAT_FAIL:
	msg_warn("request failed");
	break;
    }
}

static void update(char *query)
{
    char   *addr;
    char   *status_text;
    char   *cp = query;

    if ((addr = mystrtok(&cp, CHARS_SPACE)) == 0
	|| (status_text = mystrtok(&cp, CHARS_SPACE)) == 0) {
	msg_warn("bad request format");
	return;
    }
    while (*cp && ISSPACE(*cp))
	cp++;
    if (*cp == 0) {
	msg_warn("bad request format");
	return;
    }
    switch (verify_clnt_update(query, atoi(status_text), cp)) {
    case VRFY_STAT_OK:
	vstream_printf("OK\n");
	vstream_fflush(VSTREAM_OUT);
	break;
    case VRFY_STAT_BAD:
	msg_warn("bad request format");
	break;
    case VRFY_STAT_FAIL:
	msg_warn("request failed");
	break;
    }
}

int     main(int argc, char **argv)
{
    VSTRING *buffer = vstring_alloc(1);
    char   *cp;
    int     ch;
    char   *command;

    signal(SIGPIPE, SIG_IGN);

    msg_vstream_init(argv[0], VSTREAM_ERR);

    mail_conf_read();
    msg_info("using config files in %s", var_config_dir);
    if (chdir(var_queue_dir) < 0)
	msg_fatal("chdir %s: %m", var_queue_dir);

    while ((ch = GETOPT(argc, argv, "v")) > 0) {
	switch (ch) {
	case 'v':
	    msg_verbose++;
	    break;
	default:
	    usage(argv[0]);
	}
    }
    if (argc - optind > 1)
	usage(argv[0]);

    while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
	cp = STR(buffer);
	if ((command = mystrtok(&cp, CHARS_SPACE)) == 0)
	    continue;
	if (strcmp(command, "query") == 0)
	    query(cp, buffer);
	else if (strcmp(command, "update") == 0)
	    update(cp);
	else
	    msg_warn("unrecognized command: %s", command);
    }
    vstring_free(buffer);
    return (0);
}

#endif
