Beispiel #1
0
const char *dict_cache_lookup(DICT_CACHE *cp, const char *cache_key)
{
    const char *myname = "dict_cache_lookup";
    const char *cache_val;
    DICT   *db = cp->db;

    /*
     * Search for the cache entry. Don't return an entry that is scheduled
     * for delete-behind.
     */
    if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)
	&& DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) {
	if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	    msg_info("%s: key=%s (pretend not found  - scheduled for deletion)",
		     myname, cache_key);
	DICT_ERR_VAL_RETURN(cp, DICT_ERR_NONE, (char *) 0);
    } else {
	cache_val = dict_get(db, cache_key);
	if (cache_val == 0 && db->error != 0)
	    msg_rate_delay(&cp->get_log_stamp, cp->log_delay, msg_warn,
			   "%s: cache lookup for '%s' failed due to error",
			   cp->name, cache_key);
	if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	    msg_info("%s: key=%s value=%s", myname, cache_key,
		     cache_val ? cache_val : db->error ?
		     "error" : "(not found)");
	DICT_ERR_VAL_RETURN(cp, db->error, cache_val);
    }
}
Beispiel #2
0
int     dict_cache_delete(DICT_CACHE *cp, const char *cache_key)
{
    const char *myname = "dict_cache_delete";
    int     del_res;
    DICT   *db = cp->db;

    /*
     * Delete the entry, unless we would delete the current first/next entry.
     * In that case, schedule the "current" entry for delete-behind to avoid
     * mis-behavior by some databases.
     */
    if (DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) {
	DC_SCHEDULE_FOR_DELETE_BEHIND(cp);
	if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	    msg_info("%s: key=%s (current entry - schedule for delete-behind)",
		     myname, cache_key);
	DICT_ERR_VAL_RETURN(cp, DICT_ERR_NONE, DICT_STAT_SUCCESS);
    } else {
	del_res = dict_del(db, cache_key);
	if (del_res != 0)
	    msg_rate_delay(&cp->del_log_stamp, cp->log_delay, msg_warn,
		  "%s: could not delete entry for %s", cp->name, cache_key);
	if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	    msg_info("%s: key=%s (%s)", myname, cache_key,
		     del_res == 0 ? "found" :
		     db->error ? "error" : "not found");
	DICT_ERR_VAL_RETURN(cp, db->error, del_res);
    }
}
Beispiel #3
0
static const char *dict_random_lookup(DICT *dict, const char *unused_query)
{
    DICT_RANDOM *dict_random = (DICT_RANDOM *) dict;

    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
	 dict_random->replies->argv[myrand() % dict_random->replies->argc]);
}
Beispiel #4
0
static const char *dict_union_lookup(DICT *dict, const char *query)
{
    static const char myname[] = "dict_union_lookup";
    DICT_UNION *dict_union = (DICT_UNION *) dict;
    DICT   *map;
    char  **cpp;
    char   *dict_type_name;
    const char *result = 0;

    /*
     * After Roel van Meer, postfix-users mailing list, Sept 2014.
     */
    VSTRING_RESET(dict_union->re_buf);
    for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) {
	if ((map = dict_handle(dict_type_name)) == 0)
	    msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name);
	if ((result = dict_get(map, query)) == 0)
	    continue;
	if (VSTRING_LEN(dict_union->re_buf) > 0)
	    VSTRING_ADDCH(dict_union->re_buf, ',');
	vstring_strcat(dict_union->re_buf, result);
    }
    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE,
			VSTRING_LEN(dict_union->re_buf) > 0 ?
			STR(dict_union->re_buf) : 0);
}
Beispiel #5
0
static int dict_ht_sequence(DICT *dict, int how, const char **name,
			            const char **value)
{
    DICT_HT *dict_ht = (DICT_HT *) dict;
    HTABLE_INFO *ht;

    ht = htable_sequence(dict_ht->table,
			 how == DICT_SEQ_FUN_FIRST ? HTABLE_SEQ_FIRST :
			 how == DICT_SEQ_FUN_NEXT ? HTABLE_SEQ_NEXT :
			 HTABLE_SEQ_STOP);
    if (ht != 0) {
	*name = ht->key;
	*value = ht->value;
	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
    } else {
	*name = 0;
	*value = 0;
	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
    }
}
Beispiel #6
0
static const char *dict_ht_lookup(DICT *dict, const char *name)
{
    DICT_HT *dict_ht = (DICT_HT *) dict;

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }
    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, htable_find(dict_ht->table, name));
}
Beispiel #7
0
static int dict_thash_sequence(DICT *dict, int function,
			               const char **key, const char **value)
{
    const char *myname = "dict_thash_sequence";
    DICT_THASH *dict_thash = (DICT_THASH *) dict;

    /*
     * Determine and execute the seek function.
     */
    switch (function) {
    case DICT_SEQ_FUN_FIRST:
	if (dict_thash->info == 0)
	    dict_thash->info = htable_list(dict_thash->table);
	dict_thash->cursor = dict_thash->info;
	break;
    case DICT_SEQ_FUN_NEXT:
	if (dict_thash->cursor[0])
	    dict_thash->cursor += 1;
	break;
    default:
	msg_panic("%s: invalid function: %d", myname, function);
    }

    /*
     * Return the entry under the cursor.
     */
    if (dict_thash->cursor[0]) {
	*key = dict_thash->cursor[0]->key;
	*value = dict_thash->cursor[0]->value;
	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
    } else {
	*key = 0;
	*value = 0;
	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
    }
}
Beispiel #8
0
static const char *dict_thash_lookup(DICT *dict, const char *name)
{
    DICT_THASH *dict_thash = (DICT_THASH *) dict;
    const char *result = 0;

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }

    /*
     * Look up the value.
     */
    result = htable_find(dict_thash->table, name);

    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result);
}
Beispiel #9
0
static int dict_ht_update(DICT *dict, const char *name, const char *value)
{
    DICT_HT *dict_ht = (DICT_HT *) dict;
    HTABLE_INFO *ht;
    char   *saved_value = mystrdup(value);

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }
    if ((ht = htable_locate(dict_ht->table, name)) != 0) {
	myfree(ht->value);
    } else {
	ht = htable_enter(dict_ht->table, name, (char *) 0);
    }
    ht->value = saved_value;
    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS);
}
Beispiel #10
0
int     dict_cache_update(DICT_CACHE *cp, const char *cache_key,
			          const char *cache_val)
{
    const char *myname = "dict_cache_update";
    DICT   *db = cp->db;
    int     put_res;

    /*
     * Store the cache entry and cancel the delete-behind operation.
     */
    if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)
	&& DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) {
	if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	    msg_info("%s: cancel delete-behind for key=%s", myname, cache_key);
	DC_CANCEL_DELETE_BEHIND(cp);
    }
    if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	msg_info("%s: key=%s value=%s", myname, cache_key, cache_val);
    put_res = dict_put(db, cache_key, cache_val);
    if (put_res != 0)
	msg_rate_delay(&cp->upd_log_stamp, cp->log_delay, msg_warn,
		  "%s: could not update entry for %s", cp->name, cache_key);
    DICT_ERR_VAL_RETURN(cp, db->error, put_res);
}
Beispiel #11
0
int     dict_cache_sequence(DICT_CACHE *cp, int first_next,
			            const char **cache_key,
			            const char **cache_val)
{
    const char *myname = "dict_cache_sequence";
    int     seq_res;
    const char *raw_cache_key;
    const char *raw_cache_val;
    char   *previous_curr_key;
    char   *previous_curr_val;
    DICT   *db = cp->db;

    /*
     * Find the first or next database entry. Hide the record with the cache
     * cleanup completion time stamp.
     */
    seq_res = dict_seq(db, first_next, &raw_cache_key, &raw_cache_val);
    if (seq_res == 0
	&& strcmp(raw_cache_key, DC_LAST_CACHE_CLEANUP_COMPLETED) == 0)
	seq_res =
	    dict_seq(db, DICT_SEQ_FUN_NEXT, &raw_cache_key, &raw_cache_val);
    if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	msg_info("%s: key=%s value=%s", myname,
		 seq_res == 0 ? raw_cache_key : db->error ?
		 "(error)" : "(not found)",
		 seq_res == 0 ? raw_cache_val : db->error ?
		 "(error)" : "(not found)");
    if (db->error)
	msg_rate_delay(&cp->seq_log_stamp, cp->log_delay, msg_warn,
		       "%s: sequence error", cp->name);

    /*
     * Save the current cache_key and cache_val before they are clobbered by
     * our own delete operation below. This also prevents surprises when the
     * application accesses the database after this function returns.
     * 
     * We also use the saved cache_key to protect the current entry against
     * application delete requests.
     */
    previous_curr_key = cp->saved_curr_key;
    previous_curr_val = cp->saved_curr_val;
    if (seq_res == 0) {
	cp->saved_curr_key = mystrdup(raw_cache_key);
	cp->saved_curr_val = mystrdup(raw_cache_val);
    } else {
	cp->saved_curr_key = 0;
	cp->saved_curr_val = 0;
    }

    /*
     * Delete behind.
     */
    if (db->error == 0 && DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)) {
	DC_CANCEL_DELETE_BEHIND(cp);
	if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
	    msg_info("%s: delete-behind key=%s value=%s",
		     myname, previous_curr_key, previous_curr_val);
	if (dict_del(db, previous_curr_key) != 0)
	    msg_rate_delay(&cp->del_log_stamp, cp->log_delay, msg_warn,
			   "%s: could not delete entry for %s",
			   cp->name, previous_curr_key);
    }

    /*
     * Clean up previous iteration key and value.
     */
    if (previous_curr_key)
	myfree(previous_curr_key);
    if (previous_curr_val)
	myfree(previous_curr_val);

    /*
     * Return the result.
     */
    *cache_key = (cp)->saved_curr_key;
    *cache_val = (cp)->saved_curr_val;
    DICT_ERR_VAL_RETURN(cp, db->error, seq_res);
}
Beispiel #12
0
static const char *dict_sqlite_lookup(DICT *dict, const char *name)
{
    const char *myname = "dict_sqlite_lookup";
    DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict;
    sqlite3_stmt *sql_stmt;
    const char *query_remainder;
    static VSTRING *query;
    static VSTRING *result;
    const char *retval;
    int     expansion = 0;
    int     status;
    int     domain_rc;

    /*
     * In case of return without lookup (skipped key, etc.).
     */
    dict->error = 0;

    /*
     * Don't frustrate future attempts to make Postfix UTF-8 transparent.
     */
    if (!valid_utf_8(name, strlen(name))) {
	if (msg_verbose)
	    msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
		     myname, dict_sqlite->parser->name, name);
	return (0);
    }

    /*
     * Optionally fold the key. Folding may be enabled on on-the-fly.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(100);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }

    /*
     * Apply the optional domain filter for email address lookups.
     */
    if ((domain_rc = db_common_check_domain(dict_sqlite->ctx, name)) == 0) {
	if (msg_verbose)
	    msg_info("%s: %s: Skipping lookup of '%s'",
		     myname, dict_sqlite->parser->name, name);
	return (0);
    }
    if (domain_rc < 0)
	DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);

    /*
     * Expand the query and query the database.
     */
#define INIT_VSTR(buf, len) do { \
	if (buf == 0) \
		buf = vstring_alloc(len); \
	VSTRING_RESET(buf); \
	VSTRING_TERMINATE(buf); \
    } while (0)

    INIT_VSTR(query, 10);

    if (!db_common_expand(dict_sqlite->ctx, dict_sqlite->query,
			  name, 0, query, dict_sqlite_quote))
	return (0);

    if (msg_verbose)
	msg_info("%s: %s: Searching with query %s",
		 myname, dict_sqlite->parser->name, vstring_str(query));

    if (sqlite3_prepare_v2(dict_sqlite->db, vstring_str(query), -1,
			   &sql_stmt, &query_remainder) != SQLITE_OK)
	msg_fatal("%s: %s: SQL prepare failed: %s\n",
		  myname, dict_sqlite->parser->name,
		  sqlite3_errmsg(dict_sqlite->db));

    if (*query_remainder && msg_verbose)
	msg_info("%s: %s: Ignoring text at end of query: %s",
		 myname, dict_sqlite->parser->name, query_remainder);

    /*
     * Retrieve and expand the result(s).
     */
    INIT_VSTR(result, 10);
    while ((status = sqlite3_step(sql_stmt)) != SQLITE_DONE) {
	if (status == SQLITE_ROW) {
	    if (db_common_expand(dict_sqlite->ctx, dict_sqlite->result_format,
				 (char *) sqlite3_column_text(sql_stmt, 0),
				 name, result, 0)
		&& dict_sqlite->expansion_limit > 0
		&& ++expansion > dict_sqlite->expansion_limit) {
		msg_warn("%s: %s: Expansion limit exceeded for key '%s'",
			 myname, dict_sqlite->parser->name, name);
		dict->error = DICT_ERR_RETRY;
		break;
	    }
	}
	/* Fix 20100616 */
	else {
	    msg_warn("%s: %s: SQL step failed for query '%s': %s\n",
		     myname, dict_sqlite->parser->name,
		     vstring_str(query), sqlite3_errmsg(dict_sqlite->db));
	    dict->error = DICT_ERR_RETRY;
	    break;
	}
    }

    /*
     * Clean up.
     */
    if (sqlite3_finalize(sql_stmt))
	msg_fatal("%s: %s: SQL finalize failed for query '%s': %s\n",
		  myname, dict_sqlite->parser->name,
		  vstring_str(query), sqlite3_errmsg(dict_sqlite->db));

    return ((dict->error == 0 && *(retval = vstring_str(result)) != 0) ?
	    retval : 0);
}
Beispiel #13
0
static const char *dict_static_lookup(DICT *dict, const char *unused_name)
{
    DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, dict->name);
}
Beispiel #14
0
static const char *dict_mysql_lookup(DICT *dict, const char *name)
{
    const char *myname = "dict_mysql_lookup";
    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
    MYSQL_RES *query_res;
    MYSQL_ROW row;
    static VSTRING *result;
    static VSTRING *query;
    int     i;
    int     j;
    int     numrows;
    int     expansion;
    const char *r;
    db_quote_callback_t quote_func = dict_mysql_quote;
    int     domain_rc;

    dict->error = 0;

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }

    /*
     * If there is a domain list for this map, then only search for addresses
     * in domains on the list. This can significantly reduce the load on the
     * server.
     */
    if ((domain_rc = db_common_check_domain(dict_mysql->ctx, name)) == 0) {
	if (msg_verbose)
	    msg_info("%s: Skipping lookup of '%s'", myname, name);
	return (0);
    }
    if (domain_rc < 0)
	DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);

#define INIT_VSTR(buf, len) do { \
	if (buf == 0) \
	    buf = vstring_alloc(len); \
	VSTRING_RESET(buf); \
	VSTRING_TERMINATE(buf); \
    } while (0)

    INIT_VSTR(query, 10);

    /*
     * Suppress the lookup if the query expansion is empty
     * 
     * This initial expansion is outside the context of any specific host
     * connection, we just want to check the key pre-requisites, so when
     * quoting happens separately for each connection, we don't bother with
     * quoting...
     */
#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
    quote_func = 0;
#endif
    if (!db_common_expand(dict_mysql->ctx, dict_mysql->query,
			  name, 0, query, quote_func))
	return (0);

    /* do the query - set dict->error & cleanup if there's an error */
    if ((query_res = plmysql_query(dict_mysql, name, query)) == 0) {
	dict->error = DICT_ERR_RETRY;
	return (0);
    }
    numrows = mysql_num_rows(query_res);
    if (msg_verbose)
	msg_info("%s: retrieved %d rows", myname, numrows);
    if (numrows == 0) {
	mysql_free_result(query_res);
	return 0;
    }
    INIT_VSTR(result, 10);

    for (expansion = i = 0; i < numrows && dict->error == 0; i++) {
	row = mysql_fetch_row(query_res);
	for (j = 0; j < mysql_num_fields(query_res); j++) {
	    if (db_common_expand(dict_mysql->ctx, dict_mysql->result_format,
				 row[j], name, result, 0)
		&& dict_mysql->expansion_limit > 0
		&& ++expansion > dict_mysql->expansion_limit) {
		msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
			 myname, dict_mysql->parser->name, name);
		dict->error = DICT_ERR_RETRY;
		break;
	    }
	}
    }
    mysql_free_result(query_res);
    r = vstring_str(result);
    return ((dict->error == 0 && *r) ? r : 0);
}
Beispiel #15
0
static const char *dict_pgsql_lookup(DICT *dict, const char *name)
{
    const char *myname = "dict_pgsql_lookup";
    PGSQL_RES *query_res;
    DICT_PGSQL *dict_pgsql;
    PLPGSQL *pldb;
    static VSTRING *query;
    static VSTRING *result;
    int     i;
    int     j;
    int     numrows;
    int     numcols;
    int     expansion;
    const char *r;
    int     domain_rc;

    dict_pgsql = (DICT_PGSQL *) dict;
    pldb = dict_pgsql->pldb;

#define INIT_VSTR(buf, len) do { \
	if (buf == 0) \
	    buf = vstring_alloc(len); \
	VSTRING_RESET(buf); \
	VSTRING_TERMINATE(buf); \
    } while (0)

    INIT_VSTR(query, 10);
    INIT_VSTR(result, 10);

    dict->error = 0;

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_FIX) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, name);
	name = lowercase(vstring_str(dict->fold_buf));
    }

    /*
     * If there is a domain list for this map, then only search for addresses
     * in domains on the list. This can significantly reduce the load on the
     * server.
     */
    if ((domain_rc = db_common_check_domain(dict_pgsql->ctx, name)) == 0) {
	if (msg_verbose)
	    msg_info("%s: Skipping lookup of '%s'", myname, name);
	return (0);
    }
    if (domain_rc < 0)
	DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);

    /*
     * Suppress the actual lookup if the expansion is empty.
     * 
     * This initial expansion is outside the context of any specific host
     * connection, we just want to check the key pre-requisites, so when
     * quoting happens separately for each connection, we don't bother with
     * quoting...
     */
    if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
			  name, 0, query, 0))
	return (0);

    /* do the query - set dict->error & cleanup if there's an error */
    if ((query_res = plpgsql_query(dict_pgsql, name, query,
				   dict_pgsql->dbname,
				   dict_pgsql->username,
				   dict_pgsql->password)) == 0) {
	dict->error = DICT_ERR_RETRY;
	return 0;
    }
    numrows = PQntuples(query_res);
    if (msg_verbose)
	msg_info("%s: retrieved %d rows", myname, numrows);
    if (numrows == 0) {
	PQclear(query_res);
	return 0;
    }
    numcols = PQnfields(query_res);

    for (expansion = i = 0; i < numrows && dict->error == 0; i++) {
	for (j = 0; j < numcols; j++) {
	    r = PQgetvalue(query_res, i, j);
	    if (db_common_expand(dict_pgsql->ctx, dict_pgsql->result_format,
				 r, name, result, 0)
		&& dict_pgsql->expansion_limit > 0
		&& ++expansion > dict_pgsql->expansion_limit) {
		msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
			 myname, dict_pgsql->parser->name, name);
		dict->error = DICT_ERR_RETRY;
		break;
	    }
	}
    }
    PQclear(query_res);
    r = vstring_str(result);
    return ((dict->error == 0 && *r) ? r : 0);
}