text:
/*	$NetBSD: smtp.h,v 1.1.1.4 2014/07/06 19:27:55 tron Exp $	*/

/*++
/* NAME
/*	smtp 3h
/* SUMMARY
/*	smtp client program
/* SYNOPSIS
/*	#include "smtp.h"
/* DESCRIPTION
/* .nf

 /*
  * System library.
  */
#include <string.h>

 /*
  * Utility library.
  */
#include <vstream.h>
#include <vstring.h>
#include <argv.h>
#include <htable.h>
#include <dict.h>

 /*
  * Global library.
  */
#include <deliver_request.h>
#include <scache.h>
#include <string_list.h>
#include <maps.h>
#include <tok822.h>
#include <dsn_buf.h>
#include <header_body_checks.h>

 /*
  * Postfix TLS library.
  */
#include <tls.h>

 /*
  * Global iterator support. This is updated by the connection-management
  * loop, and contains dynamic context that appears in lookup keys for SASL
  * passwords, TLS policy, cached SMTP connections, and cached TLS session
  * keys.
  *
  * For consistency and maintainability, context that is used in more than one
  * lookup key is formatted with smtp_key_format().
  */
typedef struct SMTP_ITERATOR {
    /* Public members. */
    VSTRING *request_nexthop;		/* request nexhop or empty */
    VSTRING *dest;			/* current nexthop */
    VSTRING *host;			/* hostname or empty */
    VSTRING *addr;			/* printable address or empty */
    unsigned port;			/* network byte order or null */
    struct DNS_RR *rr;			/* DNS resource record or null */
    struct DNS_RR *mx;			/* DNS resource record or null */
    /* Private members. */
    VSTRING *saved_dest;		/* saved current nexthop */
    struct SMTP_STATE *parent;		/* parent linkage */
} SMTP_ITERATOR;

#define SMTP_ITER_INIT(iter, _dest, _host, _addr, _port, state) do { \
	vstring_strcpy((iter)->dest, (_dest)); \
	vstring_strcpy((iter)->host, (_host)); \
	vstring_strcpy((iter)->addr, (_addr)); \
	(iter)->port = (_port); \
	(iter)->mx = (iter)->rr = 0; \
	vstring_strcpy((iter)->saved_dest, ""); \
	(iter)->parent = (state); \
    } while (0)

#define SMTP_ITER_CLOBBER(iter, _dest, _host, _addr) do { \
	vstring_strcpy((iter)->dest, (_dest)); \
	vstring_strcpy((iter)->host, (_host)); \
	vstring_strcpy((iter)->addr, (_addr)); \
    } while (0)

#define SMTP_ITER_SAVE_DEST(iter) do { \
	vstring_strcpy((iter)->saved_dest, STR((iter)->dest)); \
    } while (0)

#define SMTP_ITER_RESTORE_DEST(iter) do { \
	vstring_strcpy((iter)->dest, STR((iter)->saved_dest)); \
    } while (0)

 /*
  * TLS Policy support.
  */
#ifdef USE_TLS

typedef struct SMTP_TLS_POLICY {
    int     level;			/* TLS enforcement level */
    char   *protocols;			/* Acceptable SSL protocols */
    char   *grade;			/* Cipher grade: "export", ... */
    VSTRING *exclusions;		/* Excluded SSL ciphers */
    ARGV   *matchargv;			/* Cert match patterns */
    DSN_BUF *why;			/* Lookup error status */
    TLS_DANE *dane;			/* DANE TLSA digests */
} SMTP_TLS_POLICY;

 /*
  * smtp_tls_policy.c
  */
extern void smtp_tls_list_init(void);
extern int smtp_tls_policy_cache_query(DSN_BUF *, SMTP_TLS_POLICY *, SMTP_ITERATOR *);
extern void smtp_tls_policy_cache_flush(void);

 /*
  * Macros must use distinct names for local temporary variables, otherwise
  * there will be bugs due to shadowing. This happened when an earlier
  * version of smtp_tls_policy_dummy() invoked smtp_tls_policy_init(), but it
  * could also happen without macro nesting.
  *
  * General principle: use all or part of the macro name in each temporary
  * variable name. Then, append suffixes to the names if needed.
  */
#define smtp_tls_policy_dummy(t) do { \
	SMTP_TLS_POLICY *_tls_policy_dummy_tmp = (t); \
	smtp_tls_policy_init(_tls_policy_dummy_tmp, (DSN_BUF *) 0); \
	_tls_policy_dummy_tmp->level = TLS_LEV_NONE; \
    } while (0)

 /* This macro is not part of the module external interface. */
#define smtp_tls_policy_init(t, w) do { \
	SMTP_TLS_POLICY *_tls_policy_init_tmp = (t); \
	_tls_policy_init_tmp->protocols = 0; \
	_tls_policy_init_tmp->grade = 0; \
	_tls_policy_init_tmp->exclusions = 0; \
	_tls_policy_init_tmp->matchargv = 0; \
	_tls_policy_init_tmp->why = (w); \
	_tls_policy_init_tmp->dane = 0; \
    } while (0)

#endif

 /*
  * State information associated with each SMTP delivery request.
  * Session-specific state is stored separately.
  */
typedef struct SMTP_STATE {
    int     misc_flags;			/* processing flags, see below */
    VSTREAM *src;			/* queue file stream */
    const char *service;		/* transport name */
    DELIVER_REQUEST *request;		/* envelope info, offsets */
    struct SMTP_SESSION *session;	/* network connection */
    int     status;			/* delivery status */
    ssize_t space_left;			/* output length control */

    /*
     * Global iterator.
     */
    SMTP_ITERATOR iterator[1];		/* Usage: state->iterator->member */

    /*
     * Global iterator.
     */
#ifdef USE_TLS
    SMTP_TLS_POLICY tls[1];		/* Usage: state->tls->member */
#endif

    /*
     * Connection cache support.
     */
    HTABLE *cache_used;			/* cached addresses that were used */
    VSTRING *dest_label;		/* cached logical/physical binding */
    VSTRING *dest_prop;			/* binding properties, passivated */
    VSTRING *endp_label;		/* cached session physical endpoint */
    VSTRING *endp_prop;			/* endpoint properties, passivated */

    /*
     * Flags and counters to control the handling of mail delivery errors.
     * There is some redundancy for sanity checking. At the end of an SMTP
     * session all recipients should be marked one way or the other.
     */
    int     rcpt_left;			/* recipients left over */
    int     rcpt_drop;			/* recipients marked as drop */
    int     rcpt_keep;			/* recipients marked as keep */

    /*
     * DSN Support introduced major bloat in error processing.
     */
    DSN_BUF *why;			/* on-the-fly formatting buffer */
} SMTP_STATE;

 /*
  * TODO: use the new SMTP_ITER name space.
  */
#define SET_NEXTHOP_STATE(state, nexthop) { \
	vstring_strcpy((state)->iterator->request_nexthop, nexthop); \
    }

#define FREE_NEXTHOP_STATE(state) { \
	STR((state)->iterator->request_nexthop)[0] = 0; \
    }

#define HAVE_NEXTHOP_STATE(state) (STR((state)->iterator->request_nexthop)[0] != 0)

 /*
  * Server features.
  */
#define SMTP_FEATURE_ESMTP		(1<<0)
#define SMTP_FEATURE_8BITMIME		(1<<1)
#define SMTP_FEATURE_PIPELINING		(1<<2)
#define SMTP_FEATURE_SIZE		(1<<3)
#define SMTP_FEATURE_STARTTLS		(1<<4)
#define SMTP_FEATURE_AUTH		(1<<5)
#define SMTP_FEATURE_XFORWARD_NAME	(1<<7)
#define SMTP_FEATURE_XFORWARD_ADDR	(1<<8)
#define SMTP_FEATURE_XFORWARD_PROTO	(1<<9)
#define SMTP_FEATURE_XFORWARD_HELO	(1<<10)
#define SMTP_FEATURE_XFORWARD_DOMAIN	(1<<11)
#define SMTP_FEATURE_BEST_MX		(1<<12)	/* for next-hop or fall-back */
#define SMTP_FEATURE_RSET_REJECTED	(1<<13)	/* RSET probe rejected */
#define SMTP_FEATURE_FROM_CACHE		(1<<14)	/* cached connection */
#define SMTP_FEATURE_DSN		(1<<15)	/* DSN supported */
#define SMTP_FEATURE_PIX_NO_ESMTP	(1<<16)	/* PIX smtp fixup mode */
#define SMTP_FEATURE_PIX_DELAY_DOTCRLF	(1<<17)	/* PIX smtp fixup mode */
#define SMTP_FEATURE_XFORWARD_PORT	(1<<18)
#define SMTP_FEATURE_EARLY_TLS_MAIL_REPLY (1<<19)	/* CVE-2009-3555 */
#define SMTP_FEATURE_XFORWARD_IDENT	(1<<20)

 /*
  * Features that passivate under the endpoint.
  */
#define SMTP_FEATURE_ENDPOINT_MASK \
	(~(SMTP_FEATURE_BEST_MX | SMTP_FEATURE_RSET_REJECTED \
	| SMTP_FEATURE_FROM_CACHE))

 /*
  * Features that passivate under the logical destination.
  */
#define SMTP_FEATURE_DESTINATION_MASK (SMTP_FEATURE_BEST_MX)

 /*
  * Misc flags.
  */
#define SMTP_MISC_FLAG_LOOP_DETECT	(1<<0)
#define	SMTP_MISC_FLAG_IN_STARTTLS	(1<<1)
#define SMTP_MISC_FLAG_FIRST_NEXTHOP	(1<<2)
#define SMTP_MISC_FLAG_FINAL_NEXTHOP	(1<<3)
#define SMTP_MISC_FLAG_FINAL_SERVER	(1<<4)
#define SMTP_MISC_FLAG_CONN_LOAD	(1<<5)
#define SMTP_MISC_FLAG_CONN_STORE	(1<<6)
#define SMTP_MISC_FLAG_COMPLETE_SESSION	(1<<7)
#define SMTP_MISC_FLAG_PREF_IPV6	(1<<8)
#define SMTP_MISC_FLAG_PREF_IPV4	(1<<9)

#define SMTP_MISC_FLAG_CONN_CACHE_MASK \
	(SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE)

 /*
  * smtp.c
  */
#define SMTP_HAS_DSN(why)	(STR((why)->status)[0] != 0)
#define SMTP_HAS_SOFT_DSN(why)	(STR((why)->status)[0] == '4')
#define SMTP_HAS_HARD_DSN(why)	(STR((why)->status)[0] == '5')
#define SMTP_HAS_LOOP_DSN(why) \
    (SMTP_HAS_DSN(why) && strcmp(STR((why)->status) + 1, ".4.6") == 0)

#define SMTP_SET_SOFT_DSN(why)	(STR((why)->status)[0] = '4')
#define SMTP_SET_HARD_DSN(why)	(STR((why)->status)[0] = '5')

extern int smtp_host_lookup_mask;	/* host lookup methods to use */

#define SMTP_HOST_FLAG_DNS	(1<<0)
#define SMTP_HOST_FLAG_NATIVE	(1<<1)

extern int smtp_dns_support;		/* dns support level */

#define SMTP_DNS_INVALID	(-1)	/* smtp_dns_support_level = <bogus> */
#define SMTP_DNS_DISABLED	0	/* smtp_dns_support_level = disabled */
#define SMTP_DNS_ENABLED	1	/* smtp_dns_support_level = enabled */
#define SMTP_DNS_DNSSEC		2	/* smtp_dns_support_level = dnssec */

extern SCACHE *smtp_scache;		/* connection cache instance */
extern STRING_LIST *smtp_cache_dest;	/* cached destinations */

extern MAPS *smtp_ehlo_dis_maps;	/* ehlo keyword filter */

extern MAPS *smtp_pix_bug_maps;		/* PIX workarounds */

extern MAPS *smtp_generic_maps;		/* make internal address valid */
extern int smtp_ext_prop_mask;		/* address externsion propagation */
extern unsigned smtp_dns_res_opt;	/* DNS query flags */

#ifdef USE_TLS

extern TLS_APPL_STATE *smtp_tls_ctx;	/* client-side TLS engine */

#endif

extern HBC_CHECKS *smtp_header_checks;	/* limited header checks */
extern HBC_CHECKS *smtp_body_checks;	/* limited body checks */

 /*
  * smtp_session.c
  */

typedef struct SMTP_SESSION {
    VSTREAM *stream;			/* network connection */
    SMTP_ITERATOR *iterator;		/* dest, host, addr, port */
    char   *namaddr;			/* mail exchanger */
    char   *helo;			/* helo response */
    unsigned port;			/* network byte order */
    char   *namaddrport;		/* mail exchanger, incl. port */

    VSTRING *buffer;			/* I/O buffer */
    VSTRING *scratch;			/* scratch buffer */
    VSTRING *scratch2;			/* scratch buffer */

    int     features;			/* server features */
    off_t   size_limit;			/* server limit or unknown */

    ARGV   *history;			/* transaction log */
    int     error_mask;			/* error classes */
    struct MIME_STATE *mime_state;	/* mime state machine */

    int     send_proto_helo;		/* XFORWARD support */

    time_t  expire_time;		/* session reuse expiration time */
    int     reuse_count;		/* # of times reused (for logging) */
    int     dead;			/* No further I/O allowed */

#ifdef USE_SASL_AUTH
    char   *sasl_mechanism_list;	/* server mechanism list */
    char   *sasl_username;		/* client username */
    char   *sasl_passwd;		/* client password */
    struct XSASL_CLIENT *sasl_client;	/* SASL internal state */
    VSTRING *sasl_reply;		/* client response */
#endif

    /*
     * TLS related state, don't forget to initialize in session_tls_init()!
     */
#ifdef USE_TLS
    TLS_SESS_STATE *tls_context;	/* TLS library session state */
    char   *tls_nexthop;		/* Nexthop domain for cert checks */
    int     tls_retry_plain;		/* Try plain when TLS handshake fails */
    SMTP_TLS_POLICY *tls;		/* TEMPORARY */
#endif

    SMTP_STATE *state;			/* back link */
} SMTP_SESSION;

extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, SMTP_ITERATOR *, time_t, int);
extern void smtp_session_new_stream(SMTP_SESSION *, VSTREAM *, time_t, int);
extern int smtp_sess_plaintext_ok(SMTP_ITERATOR *, int);
extern void smtp_session_free(SMTP_SESSION *);
extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *);
extern SMTP_SESSION *smtp_session_activate(int, SMTP_ITERATOR *, VSTRING *, VSTRING *);

 /*
  * What's in a name?
  */
#define SMTP_HNAME(rr) (var_smtp_cname_overr ? (rr)->rname : (rr)->qname)

 /*
  * smtp_connect.c
  */
extern int smtp_connect(SMTP_STATE *);

 /*
  * smtp_proto.c
  */
extern int smtp_helo(SMTP_STATE *);
extern int smtp_xfer(SMTP_STATE *);
extern int smtp_rset(SMTP_STATE *);
extern int smtp_quit(SMTP_STATE *);

extern HBC_CALL_BACKS smtp_hbc_callbacks[];

 /*
  * A connection is re-usable if session->expire_time is > 0 and the
  * expiration time has not been reached.  This is subtle because the timer
  * can expire between sending a command and receiving the reply for that
  * command.
  *
  * But wait, there is more! When SMTP command pipelining is enabled, there are
  * two protocol loops that execute at very different times: one loop that
  * generates commands, and one loop that receives replies to those commands.
  * These will be called "sender loop" and "receiver loop", respectively. At
  * well-defined protocol synchronization points, the sender loop pauses to
  * let the receiver loop catch up.
  *
  * When we choose to reuse a connection, both the sender and receiver protocol
  * loops end with "." (mail delivery) or "RSET" (address probe). When we
  * choose not to reuse, both the sender and receiver protocol loops end with
  * "QUIT". The problem is that we must make the same protocol choices in
  * both the sender and receiver loops, even though those loops may execute
  * at completely different times.
  *
  * We "freeze" the choice in the sender loop, just before we generate "." or
  * "RSET". The reader loop leaves the connection cachable even if the timer
  * expires by the time the response arrives. The connection cleanup code
  * will call smtp_quit() for connections with an expired cache expiration
  * timer.
  *
  * We could have made the programmer's life a lot simpler by not making a
  * choice at all, and always leaving it up to the connection cleanup code to
  * call smtp_quit() for connections with an expired cache expiration timer.
  *
  * As a general principle, neither the sender loop nor the receiver loop must
  * modify the connection caching state, if that can affect the receiver
  * state machine for not-yet processed replies to already-generated
  * commands. This restriction does not apply when we have to exit the
  * protocol loops prematurely due to e.g., timeout or connection loss, so
  * that those pending replies will never be received.
  *
  * But wait, there is even more! Only the first good connection for a specific
  * destination may be cached under both the next-hop destination name and
  * the server address; connections to alternate servers must be cached under
  * the server address alone. This means we must distinguish between bad
  * connections and other reasons why connections cannot be cached.
  */
#define THIS_SESSION_IS_CACHED \
	(!THIS_SESSION_IS_DEAD && session->expire_time > 0)

#define THIS_SESSION_IS_EXPIRED \
	(THIS_SESSION_IS_CACHED \
	    && (session->expire_time < vstream_ftime(session->stream) \
		|| (var_smtp_reuse_count > 0 \
		    && session->reuse_count >= var_smtp_reuse_count)))

#define THIS_SESSION_IS_BAD \
	(!THIS_SESSION_IS_DEAD && session->expire_time < 0)

#define THIS_SESSION_IS_DEAD \
	(session->dead != 0)

 /* Bring the bad news. */

#define DONT_CACHE_THIS_SESSION \
	(session->expire_time = 0)

#define DONT_CACHE_BAD_SESSION \
	(session->expire_time = -1)

#define DONT_USE_DEAD_SESSION \
	(session->dead = 1)

 /* Initialization. */

#define USE_NEWBORN_SESSION \
	(session->dead = 0)

#define CACHE_THIS_SESSION_UNTIL(when) \
	(session->expire_time = (when))

 /*
  * Encapsulate the following so that we don't expose details of of
  * connection management and error handling to the SMTP protocol engine.
  */
#define RETRY_AS_PLAINTEXT do { \
	session->tls_retry_plain = 1; \
	state->misc_flags &= ~SMTP_MISC_FLAG_FINAL_SERVER; \
    } while (0)

 /*
  * smtp_chat.c
  */
typedef struct SMTP_RESP {		/* server response */
    int     code;			/* SMTP code */
    const char *dsn;			/* enhanced status */
    char   *str;			/* full reply */
    VSTRING *dsn_buf;			/* status buffer */
    VSTRING *str_buf;			/* reply buffer */
} SMTP_RESP;

extern void PRINTFLIKE(2, 3) smtp_chat_cmd(SMTP_SESSION *, const char *,...);
extern DICT *smtp_chat_resp_filter;
extern SMTP_RESP *smtp_chat_resp(SMTP_SESSION *);
extern void smtp_chat_init(SMTP_SESSION *);
extern void smtp_chat_reset(SMTP_SESSION *);
extern void smtp_chat_notify(SMTP_SESSION *);

#define SMTP_RESP_FAKE(resp, _dsn) \
    ((resp)->code = 0, \
     (resp)->dsn = (_dsn), \
     (resp)->str = DSN_BY_LOCAL_MTA, \
     (resp))

#define DSN_BY_LOCAL_MTA	((char *) 0)	/* DSN issued by local MTA */

 /*
  * These operations implement a redundant mark-and-sweep algorithm that
  * explicitly accounts for the fate of every recipient. The interface is
  * documented in smtp_rcpt.c, which also implements the sweeping. The
  * smtp_trouble.c module does most of the marking after failure.
  *
  * When a delivery fails or succeeds, take one of the following actions:
  *
  * - Mark the recipient as KEEP (deliver to alternate MTA) and do not update
  * the delivery request status.
  *
  * - Mark the recipient as DROP (remove from delivery request), log whether
  * delivery succeeded or failed, delete the recipient from the queue file
  * and/or update defer or bounce logfiles, and update the delivery request
  * status.
  *
  * At the end of a delivery attempt, all recipients must be marked one way or
  * the other. Failure to do so will trigger a panic.
  */
#define SMTP_RCPT_STATE_KEEP	1	/* send to backup host */
#define SMTP_RCPT_STATE_DROP	2	/* remove from request */
#define SMTP_RCPT_INIT(state) do { \
	    (state)->rcpt_drop = (state)->rcpt_keep = 0; \
	    (state)->rcpt_left = state->request->rcpt_list.len; \
	} while (0)

#define SMTP_RCPT_DROP(state, rcpt) do { \
	    (rcpt)->u.status = SMTP_RCPT_STATE_DROP; (state)->rcpt_drop++; \
	} while (0)

#define SMTP_RCPT_KEEP(state, rcpt) do { \
	    (rcpt)->u.status = SMTP_RCPT_STATE_KEEP; (state)->rcpt_keep++; \
	} while (0)

#define SMTP_RCPT_ISMARKED(rcpt) ((rcpt)->u.status != 0)

#define SMTP_RCPT_LEFT(state) (state)->rcpt_left

extern void smtp_rcpt_cleanup(SMTP_STATE *);
extern void smtp_rcpt_done(SMTP_STATE *, SMTP_RESP *, RECIPIENT *);

 /*
  * smtp_trouble.c
  */
extern int smtp_sess_fail(SMTP_STATE *);
extern int PRINTFLIKE(4, 5) smtp_site_fail(SMTP_STATE *, const char *,
				             SMTP_RESP *, const char *,...);
extern int PRINTFLIKE(4, 5) smtp_mesg_fail(SMTP_STATE *, const char *,
				             SMTP_RESP *, const char *,...);
extern void PRINTFLIKE(5, 6) smtp_rcpt_fail(SMTP_STATE *, RECIPIENT *,
					          const char *, SMTP_RESP *,
					            const char *,...);
extern int smtp_stream_except(SMTP_STATE *, int, const char *);

 /*
  * smtp_unalias.c
  */
extern const char *smtp_unalias_name(const char *);
extern VSTRING *smtp_unalias_addr(VSTRING *, const char *);

 /*
  * smtp_state.c
  */
extern SMTP_STATE *smtp_state_alloc(void);
extern void smtp_state_free(SMTP_STATE *);

 /*
  * smtp_map11.c
  */
extern int smtp_map11_external(VSTRING *, MAPS *, int);
extern int smtp_map11_tree(TOK822 *, MAPS *, int);
extern int smtp_map11_internal(VSTRING *, MAPS *, int);

 /*
  * smtp_key.c
  */
char   *smtp_key_prefix(VSTRING *, const char *, SMTP_ITERATOR *, int);

#define SMTP_KEY_FLAG_SERVICE		(1<<0)	/* service name */
#define SMTP_KEY_FLAG_SENDER		(1<<1)	/* sender address */
#define SMTP_KEY_FLAG_REQ_NEXTHOP	(1<<2)	/* request nexthop */
#define SMTP_KEY_FLAG_NEXTHOP		(1<<3)	/* current nexthop */
#define SMTP_KEY_FLAG_HOSTNAME		(1<<4)	/* remote host name */
#define SMTP_KEY_FLAG_ADDR		(1<<5)	/* remote address */
#define SMTP_KEY_FLAG_PORT		(1<<6)	/* remote port */

#define SMTP_KEY_MASK_ALL \
	(SMTP_KEY_FLAG_SERVICE | SMTP_KEY_FLAG_SENDER | \
	SMTP_KEY_FLAG_REQ_NEXTHOP | \
	SMTP_KEY_FLAG_NEXTHOP | SMTP_KEY_FLAG_HOSTNAME | \
	SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT)

 /*
  * Conditional lookup-key flags for cached connections that may be
  * SASL-authenticated with a per-{sender, nexthop, or hostname} credential.
  * Each bit corresponds to one type of smtp_sasl_password_file lookup key,
  * and is turned on only when the corresponding main.cf parameter is turned
  * on.
  */
#define COND_SASL_SMTP_KEY_FLAG_SENDER \
	((var_smtp_sender_auth && *var_smtp_sasl_passwd) ? \
	    SMTP_KEY_FLAG_SENDER : 0)

#define COND_SASL_SMTP_KEY_FLAG_NEXTHOP \
	(*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_NEXTHOP : 0)

#define COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
	(*var_smtp_sasl_passwd ? SMTP_KEY_FLAG_HOSTNAME : 0)

 /*
  * Connection-cache destination lookup key. The SENDER attribute is a proxy
  * for sender-dependent SASL credentials (or absence thereof), and prevents
  * false connection sharing when different SASL credentials may be required
  * for different deliveries to the same domain and port. The SERVICE
  * attribute is a proxy for all request-independent configuration details.
  */
#define SMTP_KEY_MASK_SCACHE_DEST_LABEL \
	(SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
	| SMTP_KEY_FLAG_REQ_NEXTHOP)

 /*
  * Connection-cache endpoint lookup key. The SENDER, NEXTHOP, and HOSTNAME
  * attributes are proxies for SASL credentials (or absence thereof), and
  * prevent false connection sharing when different SASL credentials may be
  * required for different deliveries to the same IP address and port.
  */
#define SMTP_KEY_MASK_SCACHE_ENDP_LABEL \
	(SMTP_KEY_FLAG_SERVICE | COND_SASL_SMTP_KEY_FLAG_SENDER \
	| COND_SASL_SMTP_KEY_FLAG_NEXTHOP | COND_SASL_SMTP_KEY_FLAG_HOSTNAME \
	| SMTP_KEY_FLAG_ADDR | SMTP_KEY_FLAG_PORT)

 /*
  * Silly little macros.
  */
#define STR(s) vstring_str(s)
#define LEN(s) VSTRING_LEN(s)

extern int smtp_mode;

#define SMTP_X(x) (smtp_mode ? VAR_SMTP_##x : VAR_LMTP_##x)
#define X_SMTP(x) (smtp_mode ? x##_SMTP : x##_LMTP)

/* 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
/*
/*	TLS support originally by:
/*	Lutz Jaenicke
/*	BTU Cottbus
/*	Allgemeine Elektrotechnik
/*	Universitaetsplatz 3-4
/*	D-03044 Cottbus, Germany
/*
/*	Victor Duchovni
/*	Morgan Stanley
/*--*/

redpajama_set_name: RedPajamaGithub


text:
package text

import (
	"fmt"
	"html/template"
)

// The text/plain content type.
type Plain struct{}

// Since the display content is sanitized, this content type is safe.
func (c *Plain) Safe() bool {
	return true
}

// Sanitize the output, replace newlines with HTML line breaks, and return
// the modified content.
func (c *Plain) RenderDisplayContent(content interface{}) (string, error) {
	contentStr := template.HTMLEscapeString(content.(string))
	return fmt.Sprintf("<pre>%s</pre>", contentStr), nil
}

// Simply return the content.
func (c *Plain) RenderIndexContent(content interface{}) (string, error) {
	return content.(string), nil
}


text:
using Common.Algebra;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common.RealAnalysis
{
    public class UniformGridRealFunction : BaseRealFunction
    {
        double a;
        double b;
        int n;
        List<double> vals = new List<double>();

        private FuncRealFunction funcWrapper;

        public UniformGridRealFunction(Matrix<double> source, double a, double b, int n)
        {
            for(int i = 0; i < source.RowsCount; i++)
            {
                vals.Add(source[i, 0]);
            }
            this.a = a;
            this.b = b;
            this.n = n;

            funcWrapper = new FuncRealFunction(x => GetValue(x));
        }

        public override double GetValue(double arg)
        {
            double h = (b - a) / n;
            if(arg > b)
                return vals.Last();
            if(arg < a)
                return vals.First();
            int index = (int)((arg - a) / h);
            if(index == vals.Count - 1)
                return vals[index];
            else
            {
                double lambda = 1 - (arg - (index * h + a)) / h;
                return vals[index] *lambda + vals[index + 1] * (1 - lambda);
            }
        }

        public override BaseRealFunction GetNthFunctionalDerivative(int n)
        {
            return funcWrapper.GetNthFunctionalDerivative(n);
        }

        public override BaseRealFunction Mult(BaseRealFunction f)
        {
            return funcWrapper.Mult(f);
        }

        public override BaseRealFunction Sum(BaseRealFunction f)
        {
            return funcWrapper.Sum(f);
        }

        public override BaseRealFunction Minus()
        {
            return funcWrapper.Minus();
        }
    }
}


text:
import {AxiosHTTPClient, Consumer, ConsumerObject} from '../../src';

describe('AxiosHTTPClient', function () {

    beforeEach(function () {
        jasmine.Ajax.install();

        jasmine.Ajax
            .stubRequest('http://example.com/api/posts?id=1&id=2&id=3')
            .andReturn({status: 200, responseText: '[{"title":"FooBar"}, {"title":"FooBaz"}]'});
        jasmine.Ajax
            .stubRequest('http://example.com/api/posts?id[]=1&id[]=2&id[]=3')
            .andReturn({status: 200, responseText: '[{"title":"FooBar"}, {"title":"FooBaz"}]'});
    });

    afterEach(function () {
        jasmine.Ajax.uninstall();
    });

    it('should respect non-trailing slash base url in combination with empty path(issue #31)', () => {
        class PostConsumer extends Consumer {
            constructor(endpoint = 'http://example.com/api') {
                super(endpoint);
            }
        }

        const consumer = new PostConsumer();
        expect(consumer.client.baseURL).toBe(consumer.endpoint);

        const url = consumer.client.getURL('');
        expect(url).toBe('http://example.com/api');
    });

    it('should use repeat array mode by default', function (done) {
        class Post extends ConsumerObject {
        }

        class PostConsumer extends Consumer {
            constructor() {
                super('http://example.com/api', Post);
                this.client = new AxiosHTTPClient(this);
            }
        }

        let consumer = new PostConsumer();

        consumer.get('/posts', {'id': [1, 2, 3]})
            .then(() => {
                let request = jasmine.Ajax.requests.mostRecent();
                expect(request.url).toBe('http://example.com/api/posts?id=1&id=2&id=3')
                done();
            })
            .catch(() => {
                let request = jasmine.Ajax.requests.mostRecent();
                expect(request.url).toBe('http://example.com/api/posts?id=1&id=2&id=3')
                done();
            })
        ;
    });

    it('should use brackets array mode if specified', function (done) {
        class Post extends ConsumerObject {
        }

        class PostConsumer extends Consumer {
            constructor() {
                super('http://example.com/api', Post);
                this.client = new AxiosHTTPClient(this, {arrayFormat: "brackets"});
            }
        }

        let consumer = new PostConsumer();

        consumer.get('/posts', {'id': [1, 2, 3]})
            .then(() => {
                let request = jasmine.Ajax.requests.mostRecent();
                expect(request.url).toBe('http://example.com/api/posts?id[]=1&id[]=2&id[]=3')
                done();
            })
            .catch(() => {
                let request = jasmine.Ajax.requests.mostRecent();
                expect(request.url).toBe('http://example.com/api/posts?id[]=1&id[]=2&id[]=3')
                done();
            })
        ;
    });
})
;


text:
package influx

type ErrorReporter struct {
	Database
	tags   map[string]interface{}
	values map[string]interface{}
}

func (e ErrorReporter) Error(name string) (err error) {
	return e.Write(name, e.tags, e.values)
}


text:
from programy.utils.parsing.linenumxml import LineNumberingParser
import xml.etree.ElementTree as ET

import os
import unittest

#############################################################################
#

class LineNumberingParserTests(unittest.TestCase):

    def test_broken_xml(self):
        with self.assertRaises(ET.ParseError) as raised:
            ET.parse(os.path.dirname(__file__)+"/broken.xml", parser=LineNumberingParser())
        self.assertEqual(22, raised.exception.position[0])
        self.assertEqual(10, raised.exception.position[1])

    # TODO Work out why this fails ?
    """
    def test_working_xml(self):
        tree = ET.parse(os.path.dirname(__file__)+"/working.xml", parser=LineNumberingParser())
        aiml = tree.getroot()
        self.assertEqual(28, aiml._end_line_number)
        self.assertEqual(0, aiml._end_column_number)

        patterns = aiml.findall('category')
        self.assertEqual(1, len(patterns))
        self.assertEqual(26, patterns[0]._end_line_number)
        self.assertEqual(4, patterns[0]._end_column_number)
    """


text:
package spire

import spire.syntax.AllSyntax
import spire.std.AnyInstances

object implicits extends AnyInstances with AllSyntax


text:
/**
 * # flight-keyboard-shortcuts
 *
 * handles key press events, combos & sequences
 * =====================
 * ## Options
 *
 * ### shortcuts
 *
 * {
 *   esc: [
 *     {
 *       eventName: 'close-dialog'
 *     },
 *     {
 *       eventName: 'unfocus',
 *       selector: 'textarea, input'
 *     }
 *   ],
 *   c: [
 *     {
 *       eventName: 'compose-tweet'
 *     }
 *   ],
 *   'CTRL+ret': [
 *     {
 *       eventName: 'send-tweet'
 *       throttle: true
 *     }
 *   ]
 * }
 *
 *
 * ## Events
 *
 * ### shortcut-add
 * ---------------------
 * Options
 * shortcut     required    String    a key, combo string or key sequence
 * eventName    required    String    event to fire when key is pressed
 * selector     optional    String    CSS selector
 * ---------------------
 * #### single key:
 *
 * this.trigger('shortcut-add', {
 *   shortcut: 'c',
 *   eventName: 'compose-tweet'
 * });
 *
 * #### throttled (100ms):
 *
 * this.trigger('shortcut-add', {
 *   shortcut: 'c',
 *   eventName: 'compose-tweet',
 *   throttle: true
 * });
 *
 * #### named key:
 *
 * this.trigger('shortcut-add', {
 *   shortcut: 'esc',
 *   eventName: 'close-dialog'
 * });
 *
 * #### sequence:
 *
 * this.trigger('shortcut-add', {
 *   shortcut: 'g i',
 *   eventName: 'go-inbox'
 * });
 *
 * #### combo:
 *
 * this.trigger('shortcut-add', {
 *   shortcut: 'CTRL+ret',
 *   eventName: 'send-tweet'
 * });
 *
 * Text inputs (input, textarea) are ignored by default. if you want to listen to events on input
 * elements, add a selector as a third parameter:
 *
 * this.trigger('shortcut-add', {
 *   shortcut: 'esc',
 *   eventName: 'unfocus',
 *   selector: 'textarea, input'
 * });
 *
 * =====================
 * ## shortcut-remove
 * ---------------------
 * params
 * shortcut     Required    String      A key, combo string or key sequence
 * eventName    Optional    String      Event name
 * selector     Optional    String      CSS selector
 * ---------------------
 * ### usage
 *
 * #### remove all shortcuts for a key
 *
 * this.trigger('shortcut-remove', {
 *   shortcut: 'esc'
 * });
 */

define(function (require) {

  'use strict';

  /**
   * Module dependencies
   */

  var defineComponent = require('flight/lib/component');

  /**
   * Module exports
   */

  return defineComponent(keyboardShortcuts);

  /**
   * Module function
   */

  function keyboardShortcuts () {

    this.defaultAttrs({
      debounce: 200,
      throttle: 100,
      shortcuts: null,
      charCodes: {
        esc: 27,
        ret: 13,
        del: 46,
        left: 37,
        up: 38,
        right: 39,
        down: 40,
        bksp: 8,
        pageup: 33,
        pagedown: 34,
        home: 36,
        end: 35
      },
      defaultKeyEvent: 'keypress',
      keySequenceTimeoutDelay: 1000,
      modifiers: {
        ctrl: 'ctrlKey',
        alt: 'altKey',
        cmd: 'metaKey',
        shift: 'shiftKey'
      }
    });

    this.after('initialize', function () {

      this.activeSequences = {};
      this.sequenceStarters = {};
      this.singleKeys = {};
      this.combos = {};

      // register shortcuts
      for (var key in this.attr.shortcuts) {
        if (this.attr.shortcuts.hasOwnProperty(key)) {
          var shortcut = this.attr.shortcuts[key];
          if (typeof shortcut == 'string') {
            //shorthand sugar - shortcut is event name only
            this.addShortcut(key, this.shortcutEventHandlerFactory({event: shortcut}));
          } else {
            shortcut.forEach(function(shortcut) {
              // create a new callback for each shortcut to ensure with_key_handler
              // recognises it as unique.
              var callback = this.shortcutEventHandlerFactory({
                event: shortcut.eventName,
                throttle: shortcut.throttle
              });
              this.addShortcut(key, callback, shortcut.selector);
            }, this);
          }
        }
      }

      // listen for key events on the window
      this.on(window, 'keypress', this.handleKeyPress);
      this.on(window, 'keydown', this.handleKeyDown);

      // listen for shortcut events
      this.on('shortcut-add', this.handleShortcutAdd);
      this.on('shortcut-remove', this.handleShortcutRemove);
    });

    this.handleShortcutAdd = function (e, data) {
      var callback = this.shortcutEventHandlerFactory({
        event: data.eventName,
        throttle: data.throttle
      });
      this.addShortcut(data.shortcut, callback, data.selector)
    };

    this.handleShortcutRemove = function (e, data) {
      this.removeShortcut(data.shortcut, data.eventName, data.selector);
    };

    /**
     * get a matching shortcut for a keyboard event
     * @param e
     * @return {*}
     */
    this.getShortcutForEvent = function (e) {
      var charCode = e.which;
      var shortcut;

      if (this.eventHasModifier(e)) {
        shortcut = this.combos[charCode];
      }

      if (!shortcut && !this.eventHasFunctionModifier(e)) {
        // No combo defined for the given combination, fall back to
        // single key shortcut instead.
        // N.b. Function modifiers only apply to combo shortcuts. Do not fall back.
        shortcut = this.activeSequences[charCode] ||
          this.singleKeys[charCode] ||
          this.sequenceStarters[charCode];
      }

      return shortcut;
    };

    /**
     * delete any active sequence end shortcuts
     */
    this.clearActiveSequences = function () {
      this.activeSequences = {};
    };

    /**
     * Return true if ctrl, alt, cmd or shift is currently pressed
     * @param e
     * @return {*}
     */
    this.eventHasModifier = function (e) {
      return e.ctrlKey || e.altKey || e.metaKey || e.shiftKey;
    };

    /**
     * Return true if a function/command modifier is pressed
     */
    this.eventHasFunctionModifier = function (e) {
      return e.ctrlKey || e.metaKey;
    };

    /**
     * Return true if a text modifier is pressed. These can be used in combo
     * shortcuts (e.g. shift+space), but will generally be used to access single
     * key events (on a US keyboard '?' is accessed by pressing shift+'/' ).
     */
    this.eventHasTextModifier = function (e) {
      return e.altKey || e.shiftKey;
    };

    /**
     * Return a shortcut if the a keyEvent modifier matches the event modifier
     * Return undefined if there is no modifier key on this event
     * Return undefined if there is no matching shortcut
     * @param e
     * @param keyEvent
     * @return {*}
     */
    this.getModifiedKeyEvent = function (e, keyEvent) {

      var modifiedKeyEvent;

      if (this.eventHasModifier(e)) {
        // if modifier key is required, keyEvent should have correct modifier
        if (keyEvent.modifiers) {
          for (var key in keyEvent.modifiers) {
            if (keyEvent.modifiers.hasOwnProperty(key)) {
              if (e[key]) {
                modifiedKeyEvent = keyEvent.modifiers[key];
              }
            }
          }
        }
      }

      return modifiedKeyEvent;
    };

    /**
     * Handle a keypress event. If the key matches any registered shortcuts,
     * call the callback method for that shortcut.
     * @param e
     */
    this.handleKeyPress = function (e) {

      var shortcut;
      var shortcuts = [];
      var defaultSelector = ':not(input):not(textarea)';

      // normalize to lower case
      if (e.which >= 65 && e.which <= 90) {
        e.which += 32;
      }

      shortcut = this.getShortcutForEvent(e);

      if (!shortcut) {
        // if there's no matching shortcut, clear the active sequences
        this.clearActiveSequences();
      } else {
        // if this is a sequence starter
        if (shortcut.sequences) {

          // register all the sequence end shortcuts
          for (var key in shortcut.sequences) {
            if (shortcut.sequences.hasOwnProperty(key)) {
              this.addSequenceEnd(key, shortcut.sequences[key]);
            }
          }

          // cancel all sequence end shortcuts if no key is pressed
          setTimeout(function () {
            this.clearActiveSequences();
          }.bind(this), this.attr.keySequenceTimeoutDelay);

        } else {

          shortcuts = shortcut.shortcuts || [shortcut];

          // if this is a combo, get the corresponding shortcut
          shortcuts = shortcuts.map(function (shortcut) {
            if (shortcut.modifiers) {
              shortcut = this.getModifiedKeyEvent(e, shortcut);
            }
            return shortcut;
          }, this);

          // shortcuts will be undefined if modifier keys did not match
          shortcuts = shortcuts.filter(function (shortcut) {
            return shortcut !== undefined;
          });

          shortcuts.forEach(function (shortcut) {
            // check the target matches our selector
            var selector = shortcut.selector || defaultSelector;
            if ($(e.target).is(selector)) {
              // finally, fire the callback!
              shortcut.callback(e, shortcut.data);
            }
          });

          // pressing any key that isn't a sequence starter should clear all
          // active key sequences.
          this.clearActiveSequences();
        }
      }
    };

    /**
     * Return the ascii char code for a char or special key (del, ret, esc)
     * @param key {String}
     * @return {Number}
     */
    this.getCharCodeForKey = function (key) {

      var charCode;
      var validKey = false;
      var acceptedKeyCodeRanges = [
        [32, 64],
        [91, 126]
      ];

      if (typeof key !== 'string') {
        throw 'Key must be a string';
      }

      key = key.toLowerCase();

      if (key.length === 1) {
        // single character
        charCode = key.charCodeAt(0);
        acceptedKeyCodeRanges.forEach(function (range) {
          if (charCode >= range[0] && charCode <= range[1]) {
            validKey = true;
            return false;
          }
        });
        if (!validKey) {
          charCode = undefined;
        }
      } else {
        // special keys
        charCode = this.attr.charCodes[key];
      }

      return charCode;
    };

    /**
     * Register a single key shortcut
     * @param key {String}
     * @param callback {Function}
     * @param selector {String} Optional
     */
    this.addSingleKey = function (key, callback, selector, data) {

      var charCode;

      if (typeof callback !== 'function') {
        throw 'addSingleKey: no callback';
      }

      charCode = this.getCharCodeForKey(key);

      if (charCode === undefined) {
        throw 'addSingleKey: invalid key string';
      } else {
        // we have a valid key. add it to the list!
        if (this.singleKeys[charCode]) {

          var isUnique = this.singleKeys[charCode].shortcuts
            .every(function (shortcut) {
              return callback !== shortcut.callback ||
                selector !== shortcut.selector;
            }, this);

          if (!isUnique) {
            throw 'addSingleKey: attempted to add identical shortcut';
          }

        } else {
          // create a new handler
          this.singleKeys[charCode] = {
            shortcuts: []
          };
        }

        this.singleKeys[charCode].shortcuts.push({
          shortcut: key,
          callback: callback,
          selector: selector,
          data: data
        });
      }
    };

    /**
     * Register a sequence shortcut
     * @param sequence {String}
     * @param callback {Function}
     * @param selector {String} Optional
     */
    this.addSequence = function (sequence, callback, selector, data) {

      var keys;
      var startKey;
      var endKey;

      if (typeof callback !== 'function') {
        throw 'addSequence: no callback';
      }

      if (typeof sequence !== 'string') {
        throw 'addSequence: sequence must be a string';
      }

      keys = sequence.split(' ');

      if (keys.length !== 2) {
        throw 'addSequence: sequence should be in format "g a"';
      }

      startKey = this.getCharCodeForKey(keys[0]);
      if (startKey === undefined) {
        throw 'addSequence: sequence should be in format "g a"';
      }

      endKey = this.getCharCodeForKey(keys[1]);
      if (endKey === undefined) {
        throw 'addSequence: sequence should be in format "g a"';
      }

      if (!this.sequenceStarters[startKey]) {
        // register a new sequence starter
        this.sequenceStarters[startKey] = {
          sequences: {}
        };
      }

      // add a sequence end shortcut to the sequence
      this.sequenceStarters[startKey].sequences[keys[1]] = {
        shortcut: sequence,
        callback: callback,
        selector: selector,
        data: data
      };
    };

    /**
     * Register a sequence end shortcut
     * @param key {String}
     * @param sequence {Object}
     */
    this.addSequenceEnd = function (key, sequence) {

      var charCode;

      if (typeof sequence.callback !== 'function') {
        throw 'addSequenceEnd: no callback';
      }
      if (typeof key !== 'string') {
        throw 'addSequenceEnd: invalid shortcut key';
      }

      charCode = this.getCharCodeForKey(key);

      if (charCode === undefined) {
        throw 'addSequenceEnd: invalid shortcut key';
      }

      // register an active sequence
      this.activeSequences[charCode] = {
        shortcut: sequence.shortcut,
        callback: sequence.callback,
        selector: sequence.selector,
        data: sequence.data
      };
    };

    /**
     * Register a combo shortcut
     * @param combo {String}
     * @param callback {Function}
     * @param selector {String} Optional
     */
    this.addCombo = function (combo, callback, selector, data) {

      var keys;
      var modifier;
      var charCode;

      if (typeof callback !== 'function') {
        throw 'addCombo: no callback';
      }

      if (typeof combo !== 'string') {
        throw 'addCombo: invalid combo string';
      }

      keys = combo.split('+');

      if (keys.length !== 2) {
        throw 'addCombo: invalid combo';
      }

      modifier = this.attr.modifiers[keys[0].toLowerCase()];

      if (modifier === undefined) {
        throw 'addCombo: invalid modifier';
      }

      charCode = this.getCharCodeForKey(keys[1]);

      if (charCode === undefined) {
        throw 'addCombo: invalid shortcut key';
      }

      var shortcut = {
        modifiers: {}
      };

      // add a modifier to the combo
      shortcut.modifiers[modifier] = {
        shortcut: combo,
        callback: callback,
        selector: selector,
        data: data
      };

      if (!this.combos[charCode]) {
        this.combos[charCode] = {
          shortcuts: []
        };
      }

      this.combos[charCode].shortcuts.push(shortcut);

    };

    /**
     * Check if shortcut is a sequence (eg 'g a'
     * @param shortcut {String}
     * @return {Boolean}
     */
    this.isSequence = function (shortcut) {
      return typeof shortcut === 'string' && shortcut.indexOf(' ') > 0;
    };

    /**
     * Check if shortcut is a combo (eg 'CTRL+a')
     * @param shortcut {String}
     * @return {Boolean}
     */
    this.isCombo = function (shortcut) {
      return typeof shortcut === 'string' && shortcut.indexOf('+') > 0;
    };

    /**
     * Register a shortcut (single key, combo, sequence)
     * @param shortcut {String}
     * @param callback {Function}
     * @param selector {String} optional
     */
    this.addShortcut = function (shortcut, callback, selector, data) {

      if (arguments.length === 3 && typeof selector !== 'string') {
        data = selector;
        selector = undefined;
      }

      if (this.isCombo(shortcut)) {
        this.addCombo(shortcut, callback, selector, data);
      } else if (this.isSequence(shortcut)) {
        this.addSequence(shortcut, callback, selector, data);
      } else {
        this.addSingleKey(shortcut, callback, selector, data);
      }
    };

    /**
     * remove a single key
     * @param key {String}
     */
    this.removeSingleKey = function (key) {
      var charCode = this.getCharCodeForKey(key);
      if (this.singleKeys[charCode]) {
        delete this.singleKeys[charCode];
      }
    };

    /**
     * remove a combo
     * @param combo {String}
     */
    this.removeCombo = function (combo) {
      var charCode = this.getCharCodeForKey(combo.split('+')[1]);
      if (this.combos[charCode]) {
        delete this.combos[charCode];
      }
    };

    /**
     * remove a sequence
     * @param sequence {String}
     */
    this.removeSequence = function (sequence, eventName, selector) {
      var charCode = this.getCharCodeForKey(sequence.split(' ')[0]);
      if (this.sequenceStarters[charCode]) {
        // TODO delete for specific eventName & selector
        delete this.sequenceStarters[charCode];
      }
    };

    /**
     * remove a key, sequence or combo
     * @param shortcut {String}
     */
    this.removeShortcut = function (shortcut, eventName, selector) {
      if (this.isCombo(shortcut)) {
        this.removeCombo(shortcut, eventName, selector);
      } else if (this.isSequence(shortcut)) {
        this.removeSequence(shortcut, eventName, selector);
      } else {
        this.removeSingleKey(shortcut, eventName, selector);
      }
    };

    /**
     * Some keys fire a keydown event rather than keypress
     * @param e
     */
    this.handleKeyDown = function (e) {
      var keydownKeyCodes = [
        this.attr.charCodes.del,
        this.attr.charCodes.bksp,
        this.attr.charCodes.esc,
        this.attr.charCodes.ret,
        this.attr.charCodes.left,
        this.attr.charCodes.right,
        this.attr.charCodes.up,
        this.attr.charCodes.down,
        this.attr.charCodes.pagedown,
        this.attr.charCodes.pageup,
        this.attr.charCodes.home,
        this.attr.charCodes.end
      ];
      var shouldHandle = keydownKeyCodes.some(function (code) {
        return code === e.which;
      });

      if (shouldHandle) {
        this.handleKeyPress(e);
      }
      // otherwise ignore
    };

    this.throttle = function (fn, ms) {
      var lastCalled = null;
      return function (e) {
        var now = Date.now();
        if (!lastCalled || now > lastCalled + ms) {
          fn(e);
          lastCalled = now;
        }
      }
    };

    this.debounce = function (fn, ms) {
      var lastCalled = null;
      return function (e) {
        var now = Date.now();

        if (!lastCalled || now > lastCalled + ms) {
          fn(e);
        }

        lastCalled = now;
      }
    };

    /**
     * the shortcut has been triggered, so we should trigger the event for the shortcut
     * @param shortcut {Options}
     * @param shortcut.event {String} event name
     * @param shortcut.throttle {Boolean}
     */
    this.shortcutEventHandlerFactory = function(shortcut) {

      var throttledEventHandler;
      var debouncedEventHandler;
      var throttlePeriod;

      var eventHandler = function(e) {
        e.preventDefault();
        e.stopPropagation();

        this.trigger(e.target, shortcut.event);
      }.bind(this);

      if (shortcut.throttle !== undefined) {
        if (typeof shortcut.throttle === 'number') {
          throttlePeriod = shortcut.throttle;
        } else {
          throttlePeriod = this.attr.throttle;
        }
        throttledEventHandler = this.throttle(eventHandler, throttlePeriod);
        return throttledEventHandler;
      } else {
        debouncedEventHandler = this.debounce(eventHandler, this.attr.debounce);
        return debouncedEventHandler;
      }
    };
  }

});


text:
===============================
FIWARE Policy Manager GE: Cloto
===============================

| |Build Status| |Coverage Status| |Pypi Version| |Pypi License|

.. contents:: :local:

Introduction
============

This is the code repository for **FIWARE Policy Manager GE - Cloto**, a server that provides an API-REST to
create rules associated to servers, subscribe servers to Context Broker to get information about resources
consumption of that servers and launch actions described in rules when conditions are given.

This project is part of FIWARE_.
Check also the `FIWARE Catalogue entry for Policy Manager`__

__ `FIWARE Policy Manager - Catalogue`_

Any feedback on this documentation is highly welcome, including bugs, typos or
things you think should be included but aren't. You can use `github issues`__
to provide feedback.

__ `Fiware-cloto - GitHub issues`_

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

GEi overall description
=======================
Bosun GEri is the reference implementation of Policy Manager GE.

Bosun GEri offers decision-making ability, independently of the type of resource (physical/virtual resources,
network, service, etc.)  being able to solve complex problems within the Cloud field by reasoning about the knowledge
base, represented by facts and rules.
Bosun GEri provides the basic management of cloud resources based on rules, as well as management of the corresponding
resources within FIWARE Cloud instances based on infrastructure physical monitoring, resources and services
security monitoring or whatever that could be defined by facts, actions and rules.

The baseline for the Bosun GEri is PyCLIPS, which is a module to interact with CLIPS expert system implemented in
python language. The reason to take PyCLIPS is to extend the OpenStack ecosystem with an expert system, written in
the same language as the rest of the OpenStack services.
Besides, It provides notification service to your own HTTP server where you can define your
own actions based on the notifications launched by Policy Manager.
Last but not least, Bosun is integrated with the Monitoring GEri in order to recover the information of the (virtual)
system and calculate any possible change on it based on the knowledge database defined for it.

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Components
----------

Fiware-Cloto
    Fiware-cloto is part of FIWARE Policy Manager. It provides an API-REST to create rules associated to servers,
    subscribe servers to Context Broker to get information about resources consumption of that servers and launch actions
    described in rules when conditions are given.

Fiware-Facts
    Server to process the incoming facts from the
    `Orion Context Broker <https://github.com/telefonicaid/fiware-orion>`__
    and publish the result into a RabbitMQ queue to be analysed by Fiware-Cloto. The facts are the result of the server
    resources consumption.

For more information, please refer to the `documentation <doc/README.rst>`_.

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Build and Install
=================

Requirements
------------

- Operating systems: CentOS (RedHat) and Ubuntu (Debian), being CentOS 6.3 the
  reference operating system.

To install this module you have to install some components:

- Python 2.7
- PyClips 1.0 (http://sourceforge.net/projects/pyclips/files/)
- RabbitMQ Server 3.3.0 or above (http://www.rabbitmq.com/download.html)
- pip installed (http://docs.python-guide.org/en/latest/starting/install/linux/)
- MySQL 5.6.14 or above (http://dev.mysql.com/downloads/mysql/)
- gcc-c++ and gcc libraries

This module also needs the installation of these other components:

- Fiware-facts module installed (https://github.com/telefonicaid/fiware-facts)
- A running instance of Orion Context Broker (https://github.com/telefonicaid/fiware-orion)
- fiware-monitoring connected to the Orion instance to provide
  information about servers (https://github.com/telefonicaid/fiware-monitoring).

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Installation
------------

Once you have all prerequisites installed, you must create a DB named cloto in your MySQL server.
Ensure your mysql path is in your path. If not, you can add executing (change ``/usr/local/`` with your mysql folder):

.. code::

    $ export PATH=$PATH:/usr/local/mysql/bin

In addition, be sure you have installed mysql-devel package for development of MySQL applications.
You should be able to install it from yum or apt-get package managers.

Examples:

.. code::

    centos$ sudo yum install mysql-devel
    ubuntu$ sudo apt-get install mysql-devel

After all you must install fiware-cloto from pypi repository executing:

.. code::

    $ sudo pip install fiware-cloto

After finishing you must configure cloto configuration and ensure your database is created with mysql.

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Configuration file - Cloto
--------------------------
The configuration used by the fiware-cloto component is read from the configuration file.
This file is located here:

``/etc/fiware.d/fiware-cloto.cfg``

MYSQL cloto configuration must be filled before starting fiware-facts component, user and password are empty by default.
You can copy the `default configuration file <fiware_cloto/cloto_settings/fiware-cloto.cfg>`_ to the folder defined for your OS, and
complete data about cloto MYSQL configuration (user and password) and all openstack configuration.

In addition, user could have a copy of this file in other location and pass its location to the server in running
execution defining an environment variable called CLOTO_SETTINGS_FILE.

Options that user could define:

::

    [openstack]         # OPENSTACK information about KEYSTONE to validate tokens received
    OPENSTACK_URL: http://cloud.lab.fi-ware.org:4731/v2.0
    ADM_USER:
    ADM_PASS:
    ADM_TENANT_ID:
    ADM_TENANT_NAME:
    USER_DOMAIN_NAME: Default
    AUTH_API: v2.0

    [policy_manager]
    SETTINGS_TYPE: production
    DEFAULT_WINDOW_SIZE: 5
    MAX_WINDOW_SIZE: 10
    LOGGING_PATH: /var/log/fiware-cloto

    [context_broker]
    CONTEXT_BROKER_URL: http://130.206.81.44:1026/NGSI10
    NOTIFICATION_URL: http://127.0.0.1:5000/v1.0        # Public IP of fiware-facts module
    NOTIFICATION_TYPE: ONTIMEINTERVAL
    NOTIFICATION_TIME: PT5S

    [rabbitmq]
    RABBITMQ_URL: localhost     #URL Where RabbitMQ is listening (no port needed, it uses default port)

    [mysql]
    DB_CHARSET: utf8
    DB_HOST: localhost
    DB_NAME: cloto
    DB_USER:
    DB_PASSWD:

    [django]
    DEBUG: False
    DATABASE_ENGINE: django.db.backends.mysql
    ALLOWED_HOSTS: ['127.0.0.1', 'localhost']
    SECRET_KEY: TestingKey+faeogfjksrjgpjaspigjiopsjgvopjsopgvj         ### Must be a unique generated value. keep that key safe.

    [logging]
    level: INFO

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Running
=======

To run fiware-cloto, just execute:

.. code::

    $ gunicorn fiware_cloto.cloto.wsgi -b $IP

To stop fiware-cloto, you can stop gunicorn server, or kill it

NOTE: if you want to see gunicorn log if something is going wrong, you could execute the command before adding
``--log-file=-`` at the end of the command. This option will show the logs in your prompt.

Finally, ensure that you create a folder for logs ``/var/log/fiware-cloto/`` (by default), with the right permissions to write
in that folder.

::

    mkdir -m /var/log/fiware-cloto

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

API Overview
============

To create a new rule for a server, user should send the rule as body of a POST request to the cloto server, with the
condition and action that should be performed.

For example, this operation allows to create a specific rule associate to a server:

::

    curl -v -H 'X-Auth-Token: 86e096cd4de5490296fd647e21b7f0b4' -X POST http://130.206.81.71:8000/v1.0/6571e3422ad84f7d828ce2f30373b3d4/servers/32c23ac4-230d-42b6-81f2-db9bd7e5b790/rules/ -d '{"action": {"actionName": "notify-scale", "operation": "scaleUp"}, "name": "ScaleUpRule", "condition": { "cpu": { "value": 98, "operand": "greater" }, "mem": { "value": 95, "operand": "greater equal"}}}'

The result of this operation is a JSON with the Id of the server affected and the ruleId of the created rule:

::

    {
        "serverId": "32c23ac4-230d-42b6-81f2-db9bd7e5b790",
        "ruleId": "68edb416-bfc6-11e3-a8b9-fa163e202949"
    }

Then user could perform a subscription to that rule with a new operation.

Please have a look at the `API Reference Documentation`_ section below and
at the `user and programmer guide <doc/user_guide.rst>`_ for more description of the possibilities and operations.

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

API Reference Documentation
---------------------------

- `FIWARE Policy Manager v1 (Apiary)`__

__ `FIWARE Policy Manager - Apiary`_

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Testing
=======

Unit tests
----------

Download source code from github

::

    $ git clone https://github.com/telefonicaid/fiware-cloto.git

To execute the unit tests, you must set the environment variable pointing to the settings_test file.
Then you can use coverage to execute the tests and obtain the percentage of lines coveved by the tests.
You must execute the tests from project folder ``fiware-cloto``.
Once you were inside the right location, execute the required commands:

::

    $ export DJANGO_SETTINGS_MODULE=fiware_cloto.cloto_settings.settings_tests
    $ export CLOTO_SETTINGS_FILE=$(pwd)/fiware_cloto/cloto_settings/fiware-cloto.cfg
    $ python fiware_cloto/manage.py test

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

End-to-end tests
----------------

In order to check that fiware-cloto is up and running, you can execute a GET request similar to:

::

    curl -v -H 'X-Auth-Token: $AUTH_TOKEN' http://$HOST:8000/v1.0/$TENANT_ID/

Where:

- **$AUTH_TOKEN**: is a valid token owned by the user. You can request this token from keystone.
- **$HOST**: is the url/IP of the machine where fiware facts is installed, for example: (policymanager-host.org, 127.0.0.1, etc)
- **$TENANT_ID**: is a tenantId of the user, for example: 6571e3422ad84f7d828ce2f30373b3d4

the response should be similar to:

::

    {
        "owner": "Telefonica I+D",
        "doc": "https://forge.fi-ware.org/plugins/mediawiki/wiki/fiware/index.php/Policy_Manager_Open_RESTful_API_Specification",
        "runningfrom": "15/08/14 10:12:45",
        "version": "1.7.0",
        "windowsize": 2
    }

Please refer to the `Installation and administration guide
<doc/admin_guide.rst#end-to-end-testing>`_ for details.

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Acceptance tests
----------------

Requirements

- Python 2.7 or newer
- pip installed (http://docs.python-guide.org/en/latest/starting/install/linux/)
- virtualenv installed (pip install virtalenv)
- Git installed (yum install git-core / apt-get install git)

Environment preparation:

- Create a virtual environment somewhere, e.g. in ENV (virtualenv ENV)
- Activate the virtual environment (source ENV/bin/activate)
- Change to the test/acceptance folder of the project
- Install the requirements for the acceptance tests in the virtual environment (pip install -r requirements.txt --allow-all-external).
- Configure file in fiware-cloto/tests/acceptance_tests/commons/configuration.py adding the keystone url, and a valid, user, password and tenant ID.

Tests execution

Change to the fiware-cloto/tests/acceptance_tests folder of the project if not already on it and execute:

::

       $ lettuce_tools

In the following document you will find the steps to execute automated
tests for the Policy Manager GE:

- `Policy Manager acceptance tests <fiware_cloto/cloto/tests/acceptance_tests/README.md>`_

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Advanced topics
===============

- `Installation and administration <doc/admin_guide.rst>`_
- `User and programmers guide <doc/user_guide.rst>`_
- `Open RESTful API Specification <doc/open_spec.rst>`_
- `Architecture Description <doc/architecture.rst>`_

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

Support
=======

Ask your thorough programming questions using stackoverflow and your general questions on FIWARE Q&A.
In both cases please use the tag fiware-bosun

`Top`__.

__ `FIWARE Policy Manager GE: Cloto`_

License
=======

\(c) 2013-2014 Telefónica I+D, Apache License 2.0

.. IMAGES

.. |Build Status| image:: https://travis-ci.org/telefonicaid/fiware-cloto.svg?branch=develop
   :target: https://travis-ci.org/telefonicaid/fiware-cloto
.. |Coverage Status| image:: https://img.shields.io/coveralls/telefonicaid/fiware-cloto/develop.svg
   :target: https://coveralls.io/r/telefonicaid/fiware-cloto
.. |Pypi Version| image:: https://badge.fury.io/py/fiware-cloto.svg
   :target: https://pypi.python.org/pypi/fiware-cloto/
.. |Pypi License| image:: https://img.shields.io/pypi/l/fiware-cloto.svg
   :target: https://pypi.python.org/pypi/fiware-cloto/

.. REFERENCES

.. _FIWARE: https://www.fiware.org/
.. _FIWARE Ops: https://www.fiware.org/fiware-operations/
.. _FIWARE Policy Manager - Apiary: https://jsapi.apiary.io/apis/policymanager/reference.html
.. _Fiware-cloto - GitHub issues: https://github.com/telefonicaid/fiware-cloto/issues/new
.. _FIWARE Policy Manager - Catalogue: http://catalogue.fiware.org/enablers/policy-manager-bosun


text:
import gettext
import ldap
import ldap.dn
import re
import socket
import ssl
import sys
import tempfile
import io

from otopi import constants as otopicons
from otopi import plugin, util

from ovirt_engine_extension_aaa_ldap_setup import constants

def _(m):
    return gettext.dgettext(
        message=m,
        domain='ovirt-engine-extension-aaa-ldap-setup',
    )

@util.export
class Plugin(plugin.PluginBase):

    class SoftRuntimeError(RuntimeError):
        pass

    # _ldap._tcp.redhat.com.  600     IN      SRV     5 0 389 \
    # ldap01.intranet.prod.int.rdu2.redhat.com.
    _DOMAIN_RE = re.compile(
        flags=re.VERBOSE,
        pattern=r"""
            ^
            [\w._-]+
            \s+
            \d+
            \s+
            IN
            \s+
            SRV
            \s+
            (?P<priority>\d+)
            \s+
            \d+
            \s+
            (?P<port>\d+)
            \s+
            (?P<host>[\w._-]+)
            \s*
            $
        """
    )

    _SERVERSETS = (
        {
            'serverset': 'single',
            'display': _('Single server'),
            'prompt': _('host address'),
            'validate': lambda self, arg: self._resolveHost(arg),
            'key': constants.LDAPEnv.HOSTS,
        },
        {
            'serverset': 'srvrecord',
            'display': _('DNS domain LDAP SRV record'),
            'prompt': _('DNS domain'),
            'validate': lambda self, arg: self._resolveDomain(arg),
            'key': constants.LDAPEnv.DOMAIN,
        },
        {
            'serverset': 'round-robin',
            'display': _('Round-robin between multiple hosts'),
            'prompt': _('space separated list of hosts'),
            'validate': lambda self, arg: self._resolveHost(arg),
            'key': constants.LDAPEnv.HOSTS,
        },
        {
            'serverset': 'failover',
            'display': _('Failover between multiple hosts'),
            'prompt': _('space separated list of hosts'),
            'validate': lambda self, arg: self._resolveHost(arg),
            'key': constants.LDAPEnv.HOSTS,
        },
    )

    @staticmethod
    def _isAddress(candidate):
        ret = False

        if not ret:
            try:
                socket.inet_pton(socket.AF_INET, candidate)
                ret = True
            except AttributeError:
                try:
                    socket.inet_aton(candidate)
                    ret = candidate.count('.') == 3
                except socket.error:
                    pass
            except socket.error:
                pass

        if not ret:
            try:
                socket.inet_pton(socket.AF_INET6, candidate)
                ret = True
            except AttributeError:
                pass
            except socket.error:
                pass

        return ret

    @staticmethod
    def _fetchURL(url):
        if sys.version_info[0] < 3:
            import urllib
            return urllib.urlopen(url).read().splitlines()
        else:
            import urllib.request
            with urllib.request.urlopen(
                url
            ) as f:
                return f.read().decode('utf-8').splitlines()

    @staticmethod
    def _resolver(plugin, record, what):
        rc, stdout, stderr = plugin.execute(
            args=(
                (
                    plugin.command.get('dig'),
                    '+noall',
                    '+answer',
                    '+tcp',
                    what,
                    record
                )
            ),
        )
        return stdout

    def _resolveHost(self, arg):
        ret = True

        for h in arg.split():
            h = h.strip()

            if self._isAddress(h):
                if self.environment[constants.LDAPEnv.USE_DNS]:
                    self.logger.warning(
                        _(
                            "Detected plain IP address '{address}', "
                            "disabling DNS."
                        ).format(
                            address=h,
                        )
                    )
                    self.environment[constants.LDAPEnv.USE_DNS] = False
            else:
                self.logger.info(
                    _("Trying to resolve host '{host}'").format(
                        host=h,
                    )
                )
                if not (
                    self.environment[constants.LDAPEnv.RESOLVER](
                        plugin=self,
                        record='A',
                        what=h,
                    ) or
                    self.environment[constants.LDAPEnv.RESOLVER](
                        plugin=self,
                        record='AAAA',
                        what=h,
                    )
                ):
                    self.logger.error(
                        _("Cannot resolve host '{host}'").format(
                            host=h
                        )
                    )
                    ret = False

        return ret

    def _resolveDomain(self, arg):
        self.logger.info(
            _("Trying to resolve domain '{domain}'").format(
                domain=arg,
            )
        )
        if self.environment[constants.LDAPEnv.RESOLVER](
            plugin=self,
            record='SRV',
            what='_ldap._tcp.%s' % arg,
        ):
            return True
        else:
            self.logger.error(
                _("Cannot resolve LDAP service in domain '{domain}'").format(
                    domain=arg
                )
            )
            return False

    def _getURLs(self):

        def _buildURL(host, port):
            return '%s://%s:%s' % (
                (
                    'ldaps' if self.environment[
                        constants.LDAPEnv.PROTOCOL
                    ] == 'ldaps' else 'ldap'
                ),
                host,
                (
                    636 if self.environment[
                        constants.LDAPEnv.PROTOCOL
                    ] == 'ldaps'
                    else (389 if port is None else port)
                ),
            )

        ret = []
        if self.environment[constants.LDAPEnv.SERVERSET] == 'srvrecord':
            self.logger.info(
                _("Resolving SRV record '{record}'").format(
                    record=self.environment[
                        constants.LDAPEnv.DOMAIN
                    ],
                )
            )
            stdout = self.environment[constants.LDAPEnv.RESOLVER](
                self,
                'SRV',
                '_ldap._tcp.%s' % self.environment[
                    constants.LDAPEnv.DOMAIN
                ]
            )
            ret.extend(
                [
                    _buildURL(
                        m.group('host').rstrip('.'),
                        m.group('port'),
                    )
                    for m in sorted(
                        [
                            m for m in [
                                self._DOMAIN_RE.match(l) for l in stdout
                            ] if m
                        ],
                        key=lambda e: int(e.group('priority')),
                        reverse=True,
                    )
                ]
            )
        else:
            ret.extend([
                _buildURL(host.strip(), None)
                for host in self.environment[
                    constants.LDAPEnv.HOSTS
                ].split()
            ])

        self.logger.debug('URLs: %s', ret)
        return ret

    def _getCACert(self):
        insecure = False
        cacertfile = None
        cacert = None

        method = self.dialog.queryString(
            name='OVAAALDAP_LDAP_CACERT_METHOD',
            note=_(
                'Please select method to obtain PEM encoded CA certificate '
                '(@VALUES@): '
            ),
            prompt=True,
            caseSensitive=False,
            validValues=(
                _('File'),
                _('URL'),
                _('Inline'),
                _('System'),
                _('Insecure'),
            ),
        )
        if method == _('File').lower():
            filepath = self.dialog.queryString(
                name='OVAAALDAP_LDAP_CACERT_FILE',
                note=_(
                    'File path: '
                ),
                prompt=True,
            )
            try:
                with io.open(filepath, encoding='ascii', errors='ignore') as f:
                    cacert = f.read().splitlines()
            except IOError as e:
                raise self.SoftRuntimeError(
                    _("Cannot open CA file '{filepath}': {error}").format(
                        filepath=filepath,
                        error=e,
                    ),
                )
        elif method == _('URL').lower():
            url = self.dialog.queryString(
                name='OVAAALDAP_LDAP_CACERT_URL',
                note=_(
                    'URL: '
                ),
                prompt=True,
            )
            try:
                cacert = self._fetchURL(url)
            except Exception as e:
                raise self.SoftRuntimeError(
                    _(
                        "Cannot fetch CA certificate from "
                        "'{url}': {error}"
                    ).format(
                        url=url,
                        error=e,
                    ),
                )
        elif method == _('Inline').lower():
            cacert = self.dialog.queryMultiString(
                name='OVAAALDAP_LDAP_CACERT_INLINE',
                note=_('Please paste CA certificate'),
            )
        elif method == _('Insecure').lower():
            insecure = True

        if cacert is not None:
            _cacertfile = None
            try:
                _cacertfile = tempfile.NamedTemporaryFile(mode='w+')
                _cacertfile.write('\n'.join(cacert) + '\n')
                _cacertfile.flush()

                if getattr(ssl, 'create_default_context', None):
                    context = ssl.create_default_context()
                    parsed = None
                    try:
                        context.load_verify_locations(cafile=_cacertfile.name)
                        parsed = context.get_ca_certs()[0]
                    except Exception as e:
                        raise self.SoftRuntimeError(
                            _("Invalid CA certificate: {error}").format(
                                error=e,
                            ),
                        )

                    if parsed.get('subject') != parsed.get('issuer'):
                        raise self.SoftRuntimeError(
                            _("Not Root CA certificate.")
                        )

                cacertfile = _cacertfile
                _cacertfile = None
            finally:
                if _cacertfile is not None:
                    _cacertfile.close()

        return (cacert, cacertfile, insecure)

    def _connectLDAP(self, cafile=None, insecure=False):
        ret = None

        for url in self._getURLs():
            try:
                self.logger.info(
                    _("Connecting to LDAP using '{url}'").format(
                        url=url,
                    ),
                )
                c = ldap.initialize(url)

                if self.environment[constants.LDAPEnv.PROTOCOL] != 'plain':
                    if insecure:
                        c.set_option(
                            ldap.OPT_X_TLS_REQUIRE_CERT,
                            ldap.OPT_X_TLS_NEVER
                        )
                    else:
                        c.set_option(
                            ldap.OPT_X_TLS_REQUIRE_CERT,
                            ldap.OPT_X_TLS_DEMAND
                        )

                if cafile is None:
                    if self.environment[
                        constants.LDAPEnv.SYSTEM_CACERTS
                    ] is not None:
                        c.set_option(
                            ldap.OPT_X_TLS_CACERTFILE,
                            self.environment[
                                constants.LDAPEnv.SYSTEM_CACERTS
                            ],
                        )
                else:
                    c.set_option(
                        ldap.OPT_X_TLS_CACERTFILE,
                        cafile
                    )

                #
                # Force create a new ssl connection.
                # Must be last TLS option.
                #
                c.set_option(ldap.OPT_X_TLS_NEWCTX, 0)

                c.set_option(
                    ldap.OPT_REFERRALS,
                    0,
                )
                c.set_option(
                    ldap.OPT_PROTOCOL_VERSION,
                    ldap.VERSION3,
                )

                if self.environment[
                    constants.LDAPEnv.PROTOCOL
                ] == 'starttls':
                    self.logger.info(_("Executing startTLS"))
                    c.start_tls_s()

                self.logger.debug('Perform search')
                ret = c.search_st(
                    '',
                    ldap.SCOPE_BASE,
                    '(objectClass=*)',
                    ['supportedLDAPVersion'],
                    timeout=60,
                )
                self.logger.debug('Result: %s', ret)
                if ret:
                    self.logger.info(_("Connection succeeded"))
                    ret = c
                    break
            except Exception as e:
                self.logger.debug('Exception', exc_info=True)
                self.logger.warning(
                    _("Cannot connect using '{url}': {error}").format(
                        url=url,
                        error=e,
                    )
                )
        else:
            raise self.SoftRuntimeError(
                _('Cannot connect using any of available options')
            )

        return ret

    def _bindLDAP(self, connection, user, password):
        self.logger.info(
            _("Attempting to bind using '{user}'").format(
                user=user if user else '[Anonymous]',
            )
        )
        connection.simple_bind_s(
            user,
            password
        )

    def __init__(self, context):
        super(Plugin, self).__init__(context=context)

    @plugin.event(
        stage=plugin.Stages.STAGE_BOOT,
    )
    def _boot(self):
        self.environment[
            otopicons.CoreEnv.LOG_FILTER_KEYS
        ].append(
            constants.LDAPEnv.PASSWORD
        )

    @plugin.event(
        stage=plugin.Stages.STAGE_INIT,
        name=constants.Stages.LDAP_COMMON_INIT,
    )
    def _init(self):
        self.environment.setdefault(
            constants.LDAPEnv.PROFILE,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.AAA_PROFILE_NAME,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.AAA_USE_VM_SSO,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.SOCKET_FACTORY,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.USER,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.PASSWORD,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.USE_DNS,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.SERVERSET,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.DOMAIN,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.HOSTS,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.PROTOCOL,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.CACERT,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.SYSTEM_CACERTS,
            None
        )
        self.environment.setdefault(
            constants.LDAPEnv.INSECURE,
            False
        )
        self.environment.setdefault(
            constants.LDAPEnv.BASE_DN,
            None
        )
        self.environment[
            constants.LDAPEnv.RESOLVER
        ] = self._resolver
        self.environment[
            constants.LDAPEnv.AVAILABLE_PROFILES
        ] = []

    @plugin.event(
        stage=plugin.Stages.STAGE_SETUP,
    )
    def _setup(self):
        self.command.detect('dig')

    @plugin.event(
        stage=plugin.Stages.STAGE_CUSTOMIZATION,
        name=constants.Stages.LDAP_COMMON_CUSTOMIZATION_EARLY,
    )
    def _customization_early(self):

        self.dialog.note(
            text=_(
                'Welcome to LDAP extension configuration program'
            ),
        )

        if self.environment[constants.LDAPEnv.PROFILE] is not None:
            if self.environment[constants.LDAPEnv.PROFILE] not in [
                e['profile'] for e in self.environment[
                    constants.LDAPEnv.AVAILABLE_PROFILES
                ]
            ]:
                raise self.SoftRuntimeError(
                    _("Profile {profile} was not found.").format(
                        profile=self.environment[constants.LDAPEnv.PROFILE],
                    )
                )
        else:
            profiles = []
            values = {}
            for i, p in enumerate(
                sorted(
                    self.environment[
                        constants.LDAPEnv.AVAILABLE_PROFILES
                    ],
                    key=lambda e: e['display'],
                ),
                start=1,
            ):
                profiles.append(
                    '%2s - %s' % (
                        i,
                        p['display']
                    )
                )
                values[i] = p['profile']

            self.dialog.note(
                (_('Available LDAP implementations:'),) +
                tuple(profiles)
            )

            self.environment[
                constants.LDAPEnv.PROFILE
            ] = values[
                int(
                    self.dialog.queryString(
                        name='OVAAALDAP_LDAP_PROFILES',
                        note=_('Please select: '),
                        prompt=True,
                        validValues=values.keys(),
                    )
                )
            ]

    @plugin.event(
        stage=plugin.Stages.STAGE_CUSTOMIZATION,
        name=constants.Stages.LDAP_COMMON_CUSTOMIZATION_LATE,
    )
    def _customization_late(self):
        if self.environment[constants.LDAPEnv.USE_DNS] is None:
            self.dialog.note(
                (
                    ' ',
                    _('NOTE:'),
                    _(
                        'It is highly recommended to use DNS resolution for '
                        'LDAP server.'
                    ),
                    _(
                        'If for some reason you intend to use hosts or plain '
                        'address disable DNS usage.'
                    ),
                    ' ',
                )
            )
            self.environment[
                constants.LDAPEnv.USE_DNS
            ] = self.dialog.queryString(
                name='OVAAALDAP_LDAP_USE_DNS',
                note=_(
                    'Use DNS (@VALUES@) [@DEFAULT@]: '
                ),
                prompt=True,
                caseSensitive=False,
                validValues=(_('Yes'), _('No')),
                default=_('Yes'),
            ) != _('No').lower()

        if self.environment[constants.LDAPEnv.SERVERSET] is None:
            serversets = []
            values = {}
            for i, s in enumerate(self._SERVERSETS, start=1):
                serversets.append(
                    '%2s - %s' % (
                        i,
                        s['display']
                    )
                )
                values[i] = s

            self.dialog.note(
                (_('Available policy method:'),) +
                tuple(serversets)
            )

            s = values[
                int(
                    self.dialog.queryString(
                        name='OVAAALDAP_LDAP_SERVERSET',
                        note=_('Please select: '),
                        prompt=True,
                        validValues=values.keys(),
                    )
                )
            ]

            self.environment[constants.LDAPEnv.SERVERSET] = s['serverset']

            while self.environment[s['key']] is None:
                arg = self.dialog.queryString(
                    name='OVAAALDAP_LDAP_SERVERSET',
                    note=_('Please enter {what}: ').format(
                        what=s['prompt'],
                    ),
                    prompt=True,
                )

                if s['validate'](self, arg):
                    self.environment[s['key']] = arg

        if self.environment[constants.LDAPEnv.PROTOCOL] is None:
            self.dialog.note(
                (
                    ' ',
                    _('NOTE:'),
                    _(
                        'It is highly recommended to use secure protocol to '
                        'access the LDAP server.'
                    ),
                    _(
                        'Protocol startTLS is the standard recommended '
                        'method to do so.'
                    ),
                    _(
                        'Only in cases in which the startTLS is not '
                        'supported, fallback to non standard ldaps protocol.'
                    ),
                    (
                        'Use plain for test environments only.'
                    ),
                    ' ',
                )
            )
            self.environment[
                constants.LDAPEnv.PROTOCOL
            ] = self.dialog.queryString(
                name='OVAAALDAP_LDAP_PROTOCOL',
                note=_(
                    'Please select protocol to use (@VALUES@) [@DEFAULT@]: '
                ),
                prompt=True,
                caseSensitive=False,
                validValues=('startTLS', 'ldaps', 'plain'),
                default='startTLS',
            )
            if (
                self.environment[constants.LDAPEnv.PROTOCOL] == 'ldaps' and
                self.environment[constants.LDAPEnv.SERVERSET] == 'srvrecord'
            ):
                self.logger.warning(
                    _(
                        "Ldaps doesn't use any standard SRV record. We preset"
                        " ldaps, so please make sure your DNS SRV records are"
                        " set accordingly."
                    )
                )

        connection = None

        if self.environment[constants.LDAPEnv.PROTOCOL] == 'plain':
            connection = self._connectLDAP()
        else:
            if self.environment[constants.LDAPEnv.CACERT] is not None:
                cacertfile = None
                try:
                    cacert, cacertfile, insecure = self._getCACert()
                    connection = self._connectLDAP(
                        cafile=cacertfile.name if cacert else None,
                        insecure=insecure,
                    )
                finally:
                    if cacertfile is not None:
                        cacertfile.close()
            else:
                while connection is None:
                    cacertfile = None
                    try:
                        cacert, cacertfile, insecure = self._getCACert()
                        connection = self._connectLDAP(
                            cafile=cacertfile.name if cacert else None,
                            insecure=insecure,
                        )
                        self.environment[constants.LDAPEnv.INSECURE] = insecure
                        self.environment[constants.LDAPEnv.CACERT] = cacert
                    except self.SoftRuntimeError as e:
                        self.logger.error('%s', e)
                        self.logger.debug('Exception', exc_info=True)
                    finally:
                        if cacertfile is not None:
                            cacertfile.close()

        if self.environment[constants.LDAPEnv.USER] is not None:
            self._bindLDAP(
                connection,
                self.environment[constants.LDAPEnv.USER],
                self.environment[constants.LDAPEnv.PASSWORD],
            )
        else:
            while self.environment[
                constants.LDAPEnv.USER
            ] is None:
                user = self.dialog.queryString(
                    name='OVAAALDAP_LDAP_USER',
                    note=_(
                        'Enter search user DN (for example uid=username,'
                        'dc=example,dc=com or leave empty for anonymous): '
                    ),
                    prompt=True,
                    default='',
                )
                if not user:
                    password = ''
                else:
                    password = self.dialog.queryString(
                        name='OVAAALDAP_LDAP_PASSWORD',
                        note=_(
                            'Enter search user password: '
                        ),
                        prompt=True,
                        hidden=True
                    )
                try:
                    self._bindLDAP(connection, user, password)
                    self.environment[constants.LDAPEnv.USER] = user
                    self.environment[constants.LDAPEnv.PASSWORD] = password
                except Exception as e:
                    self.logger.error(
                        _(
                            "Cannot authenticate using '{user}': {error}"
                        ).format(
                            user=user,
                            error=e,
                        )
                    )

            self.environment[
                otopicons.CoreEnv.LOG_FILTER
            ].append(self.environment[constants.LDAPEnv.PASSWORD])

        if (
            self.environment[
                constants.LDAPEnv.BASE_DN
            ] is None and
            self.environment[
                constants.LDAPEnv.PROFILE
            ] != constants.PROFILES.AD
        ):
            basedn = [
                v['basedn'] for v in self.environment[
                    constants.LDAPEnv.AVAILABLE_PROFILES
                ] if v['profile'] == self.environment[
                    constants.LDAPEnv.PROFILE
                ]
            ]
            self.logger.debug('Perform search for base DN: %s', basedn)
            result = connection.search_st(
                '',
                ldap.SCOPE_BASE,
                '(objectClass=*)',
                basedn,
                timeout=60,
            )[0][1]
            self.logger.debug('Result: %s', result)
            if result:
                # Convert items from list of bytes to string
                values = [x.decode('utf-8') for x in list(result.values())[0]]
                default = values[0]
                base_dn = None
                while self.environment[
                    constants.LDAPEnv.BASE_DN
                ] is None:
                    base_dn = self.dialog.queryString(
                        name='OVAAALDAP_LDAP_BASE_DN',
                        note=_(
                            'Please enter base DN (%s) [@DEFAULT@]: ' % (
                                ' | '.join(values)
                            )
                        ),
                        default=default,
                        prompt=True,
                    )
                    try:
                        ldap.dn.str2dn(base_dn)
                        self.environment[
                            constants.LDAPEnv.BASE_DN
                        ] = base_dn
                    except ldap.DECODING_ERROR as e:
                        self.logger.error("'%s' is not valid DN", base_dn)

        if self.environment[constants.LDAPEnv.AAA_USE_VM_SSO] is None:
            self.environment[
                constants.LDAPEnv.AAA_USE_VM_SSO
            ] = self.dialog.queryString(
                name='OVAAALDAP_LDAP_AAA_USE_VM_SSO',
                note=_(
                    'Are you going to use Single Sign-On for Virtual Machines'
                    ' (@VALUES@) [@DEFAULT@]: '
                ),
                prompt=True,
                caseSensitive=False,
                validValues=(_('Yes'), _('No')),
                default=_('Yes'),
            ) != _('No').lower()

        if self.environment[constants.LDAPEnv.AAA_USE_VM_SSO]:
            self.dialog.note(
                (
                    ' ',
                    _('NOTE:'),
                    _(
                        'Profile name has to match domain name, otherwise '
                        'Single Sign-On for Virtual Machines will not work.'
                    ),
                    ' ',
                )
            )

        # Default profile name:
        default = ''
        if self.environment[constants.LDAPEnv.SERVERSET] == 'single':
            default = self.environment[constants.LDAPEnv.HOSTS]
        elif self.environment[constants.LDAPEnv.SERVERSET] == 'srvrecord':
            default = self.environment[constants.LDAPEnv.DOMAIN]

        if self.environment[constants.LDAPEnv.AAA_PROFILE_NAME] is None:
            self.environment[
                constants.LDAPEnv.AAA_PROFILE_NAME
            ] = self.dialog.queryString(
                name='OVAAALDAP_LDAP_AAA_PROFILE',
                note=_(
                    'Please specify profile name that will be visible to '
                    'users %s: ' % ('[%s]' % default if default else '')
                ),
                default=default if default else None,
                prompt=True,
            )

# vim: expandtab tabstop=4 shiftwidth=4


text:
package com.sdw.soft.meta;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;
import java.util.Date;

/**
 * Created by shangyd on 2018/4/24.
 */
@Document
public class User implements Serializable{

    @Id
    private String id;
    @Indexed(unique = true)
    private String username;
    private String phone;
    private String email;
    private String name;
    private Date birthday;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", username='" + username + '\'' +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                ", name='" + name + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}


text:
// **********************************************************************************
// The MIT License (MIT)
//
// Copyright (c) 2014 Rob Prouse
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// **********************************************************************************

using AsciiArt.ImageConverters;
using AsciiArt.Interfaces;
using Ninject.Modules;

namespace AsciiArt.Modules
{
    public class AsciiModule : NinjectModule
    {
        /// <summary>
        /// Loads the module into the kernel.
        /// </summary>
        public override void Load()
        {
            Bind<FileConverter>().To<FileConverter>();
            Bind<IAsciiConverter>()
                .To<InvertedAscii>()
                .WithConstructorArgument("pixelsPerCharacter", 8);

            Bind<IImageProvider>().To<ImageProvider>();
        }
    }
}


text:
package sort;

our $VERSION = '2.04';

# The hints for pp_sort are now stored in $^H{sort}; older versions
# of perl used the global variable $sort::hints. -- rjh 2005-12-19

$sort::stable_bit      = 0x00000100;
$sort::unstable_bit    = 0x00000200;

use strict;

sub import {
    shift;
    if (@_ == 0) {
	require Carp;
	Carp::croak("sort pragma requires arguments");
    }
    local $_;
    $^H{sort} //= 0;
    while ($_ = shift(@_)) {
	if ($_ eq 'stable') {
	    $^H{sort} |=  $sort::stable_bit;
	    $^H{sort} &= ~$sort::unstable_bit;
	} elsif ($_ eq 'defaults') {
	    $^H{sort} =   0;
	} else {
	    require Carp;
	    Carp::croak("sort: unknown subpragma '$_'");
	}
    }
}

sub unimport {
    shift;
    if (@_ == 0) {
	require Carp;
	Carp::croak("sort pragma requires arguments");
    }
    local $_;
    no warnings 'uninitialized';	# bitops would warn
    while ($_ = shift(@_)) {
	if ($_ eq 'stable') {
	    $^H{sort} &= ~$sort::stable_bit;
	    $^H{sort} |=  $sort::unstable_bit;
	} else {
	    require Carp;
	    Carp::croak("sort: unknown subpragma '$_'");
	}
    }
}

sub current {
    my @sort;
    if ($^H{sort}) {
	push @sort, 'stable'    if $^H{sort} & $sort::stable_bit;
    }
    join(' ', @sort);
}

1;
__END__

=head1 NAME

sort - perl pragma to control sort() behaviour

=head1 SYNOPSIS

    use sort 'stable';		# guarantee stability
    use sort 'defaults';	# revert to default behavior
    no  sort 'stable';		# stability not important

    my $current;
    BEGIN {
	$current = sort::current();	# identify prevailing pragmata
    }

=head1 DESCRIPTION

With the C<sort> pragma you can control the behaviour of the builtin
C<sort()> function.

A stable sort means that for records that compare equal, the original
input ordering is preserved.
Stability will matter only if elements that compare equal can be
distinguished in some other way.  That means that simple numerical
and lexical sorts do not profit from stability, since equal elements
are indistinguishable.  However, with a comparison such as

   { substr($a, 0, 3) cmp substr($b, 0, 3) }

stability might matter because elements that compare equal on the
first 3 characters may be distinguished based on subsequent characters.

Whether sorting is stable by default is an accident of implementation
that can change (and has changed) between Perl versions.
If stability is important, be sure to
say so with a

  use sort 'stable';

The C<no sort> pragma doesn't
I<forbid> what follows, it just leaves the choice open.  Thus, after

  no sort 'stable';

sorting may happen to be stable anyway.

=head1 CAVEATS

As of Perl 5.10, this pragma is lexically scoped and takes effect
at compile time. In earlier versions its effect was global and took
effect at run-time; the documentation suggested using C<eval()> to
change the behaviour:

  { eval 'no sort "stable"';      # stability not wanted
    print sort::current . "\n";
    @a = sort @b;
    eval 'use sort "defaults"';   # clean up, for others
  }
  { eval 'use sort qw(defaults stable)';     # force stability
    print sort::current . "\n";
    @c = sort @d;
    eval 'use sort "defaults"';   # clean up, for others
  }

Such code no longer has the desired effect, for two reasons.
Firstly, the use of C<eval()> means that the sorting algorithm
is not changed until runtime, by which time it's too late to
have any effect. Secondly, C<sort::current> is also called at
run-time, when in fact the compile-time value of C<sort::current>
is the one that matters.

So now this code would be written:

  { no sort "stable";      # stability not wanted
    my $current;
    BEGIN { $current = sort::current; }
    print "$current\n";
    @a = sort @b;
    # Pragmas go out of scope at the end of the block
  }
  { use sort qw(defaults stable);     # force stability
    my $current;
    BEGIN { $current = sort::current; }
    print "$current\n";
    @c = sort @d;
  }

=cut