static void postmap_seq(const char *map_type, const char *map_name, int dict_flags) { DICT *dict; const char *key; const char *value; int func; if (strcmp(map_type, DICT_TYPE_PROXY) == 0) msg_fatal("can't sequence maps via the proxy service"); dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags); for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) { if (dict_seq(dict, func, &key, &value) != 0) break; if (*key == 0) { msg_warn("table %s:%s: empty lookup key value is not allowed", map_type, map_name); } else if (*value == 0) { msg_warn("table %s:%s: key %s: empty string result is not allowed", map_type, map_name, key); msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND", map_type, map_name); } vstream_printf("%s %s\n", key, value); } if (dict->error) msg_fatal("table %s:%s: sequence error: %m", dict->type, dict->name); vstream_fflush(VSTREAM_OUT); dict_close(dict); }
static void proxymap_sequence_service(VSTREAM *client_stream) { int request_flags; DICT *dict; int request_func; const char *reply_key; const char *reply_value; int dict_status; int reply_status; /* * Process the request. */ if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, MAIL_ATTR_TABLE, request_map, ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request_flags, ATTR_TYPE_INT, MAIL_ATTR_FUNC, &request_func, ATTR_TYPE_END) != 3 || (request_func != DICT_SEQ_FUN_FIRST && request_func != DICT_SEQ_FUN_NEXT)) { reply_status = PROXY_STAT_BAD; reply_key = reply_value = ""; } else if ((dict = proxy_map_find(STR(request_map), request_flags, &reply_status)) == 0) { reply_key = reply_value = ""; } else { dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK) | (request_flags & DICT_FLAG_RQST_MASK)); dict_status = dict_seq(dict, request_func, &reply_key, &reply_value); if (dict_status == 0) { reply_status = PROXY_STAT_OK; } else if (dict->error == 0) { reply_status = PROXY_STAT_NOKEY; reply_key = reply_value = ""; } else { reply_status = PROXY_STAT_RETRY; reply_key = reply_value = ""; } } /* * Respond to the client. */ attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, reply_status, ATTR_TYPE_STR, MAIL_ATTR_KEY, reply_key, ATTR_TYPE_STR, MAIL_ATTR_VALUE, reply_value, ATTR_TYPE_END); }
void dict_test(int argc, char **argv) { VSTRING *keybuf = vstring_alloc(1); VSTRING *inbuf = vstring_alloc(1); DICT *dict; char *dict_name; int open_flags; char *bufp; char *cmd; const char *key; const char *value; int ch; int dict_flags = 0; int n; int rc; #define USAGE "verbose|del key|get key|put key=value|first|next|masks|flags" signal(SIGPIPE, SIG_IGN); msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "v")) > 0) { switch (ch) { default: usage(argv[0]); case 'v': msg_verbose++; break; } } optind = OPTIND; if (argc - optind < 2) usage(argv[0]); if (strcasecmp(argv[optind + 1], "create") == 0) open_flags = O_CREAT | O_RDWR | O_TRUNC; else if (strcasecmp(argv[optind + 1], "write") == 0) open_flags = O_RDWR; else if (strcasecmp(argv[optind + 1], "read") == 0) open_flags = O_RDONLY; else msg_fatal("unknown access mode: %s", argv[2]); for (n = 2; argv[optind + n]; n++) dict_flags |= dict_flags_mask(argv[optind + 2]); if ((dict_flags & DICT_FLAG_OPEN_LOCK) == 0) dict_flags |= DICT_FLAG_LOCK; if ((dict_flags & (DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE)) == 0) dict_flags |= DICT_FLAG_DUP_REPLACE; vstream_fflush(VSTREAM_OUT); dict_name = argv[optind]; dict_allow_surrogate = 1; dict = dict_open(dict_name, open_flags, dict_flags); dict_register(dict_name, dict); while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { bufp = vstring_str(inbuf); if (!isatty(0)) { vstream_printf("> %s\n", bufp); vstream_fflush(VSTREAM_OUT); } if (*bufp == '#') continue; if ((cmd = mystrtok(&bufp, " ")) == 0) { vstream_printf("usage: %s\n", USAGE); vstream_fflush(VSTREAM_OUT); continue; } if (dict_changed_name()) msg_warn("dictionary has changed"); key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0; value = mystrtok(&bufp, " ="); if (strcmp(cmd, "verbose") == 0 && !key) { msg_verbose++; } else if (strcmp(cmd, "del") == 0 && key && !value) { if ((rc = dict_del(dict, key)) > 0) vstream_printf("%s: not found\n", key); else if (rc < 0) vstream_printf("%s: error\n", key); else vstream_printf("%s: deleted\n", key); } else if (strcmp(cmd, "get") == 0 && key && !value) { if ((value = dict_get(dict, key)) == 0) { vstream_printf("%s: %s\n", key, dict->error ? "error" : "not found"); } else { vstream_printf("%s=%s\n", key, value); } } else if (strcmp(cmd, "put") == 0 && key && value) { if (dict_put(dict, key, value) != 0) vstream_printf("%s: %s\n", key, dict->error ? "error" : "not updated"); else vstream_printf("%s=%s\n", key, value); } else if (strcmp(cmd, "first") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict->error ? "error" : "not found"); } else if (strcmp(cmd, "next") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict->error ? "error" : "not found"); } else if (strcmp(cmd, "flags") == 0 && !key && !value) { vstream_printf("dict flags %s\n", dict_flags_str(dict->flags)); } else if (strcmp(cmd, "masks") == 0 && !key && !value) { vstream_printf("DICT_FLAG_IMPL_MASK %s\n", dict_flags_str(DICT_FLAG_IMPL_MASK)); vstream_printf("DICT_FLAG_PARANOID %s\n", dict_flags_str(DICT_FLAG_PARANOID)); vstream_printf("DICT_FLAG_RQST_MASK %s\n", dict_flags_str(DICT_FLAG_RQST_MASK)); vstream_printf("DICT_FLAG_INST_MASK %s\n", dict_flags_str(DICT_FLAG_INST_MASK)); } else { vstream_printf("usage: %s\n", USAGE); } vstream_fflush(VSTREAM_OUT); } vstring_free(keybuf); vstring_free(inbuf); dict_close(dict); }
int main(int argc, char **argv) { VSTRING *keybuf = vstring_alloc(1); VSTRING *inbuf = vstring_alloc(1); DICT *dict; char *dict_name; int open_flags; char *bufp; char *cmd; const char *key; const char *value; int ch; int dict_flags = DICT_FLAG_LOCK | DICT_FLAG_DUP_REPLACE; int n; signal(SIGPIPE, SIG_IGN); msg_vstream_init(argv[0], VSTREAM_ERR); while ((ch = GETOPT(argc, argv, "v")) > 0) { switch (ch) { default: usage(argv[0]); case 'v': msg_verbose++; break; } } optind = OPTIND; if (argc - optind < 2) usage(argv[0]); if (strcasecmp(argv[optind + 1], "create") == 0) open_flags = O_CREAT | O_RDWR | O_TRUNC; else if (strcasecmp(argv[optind + 1], "write") == 0) open_flags = O_RDWR; else if (strcasecmp(argv[optind + 1], "read") == 0) open_flags = O_RDONLY; else msg_fatal("unknown access mode: %s", argv[2]); for (n = 2; argv[optind + n]; n++) { if (strcasecmp(argv[optind + 2], "fold") == 0) dict_flags |= DICT_FLAG_FOLD_ANY; else if (strcasecmp(argv[optind + 2], "sync") == 0) dict_flags |= DICT_FLAG_SYNC_UPDATE; else usage(argv[0]); } dict_name = argv[optind]; dict = dict_open(dict_name, open_flags, dict_flags); dict_register(dict_name, dict); while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { bufp = vstring_str(inbuf); if (!isatty(0)) { vstream_printf("> %s\n", bufp); vstream_fflush(VSTREAM_OUT); } if (*bufp == '#') continue; if ((cmd = mystrtok(&bufp, " ")) == 0) { vstream_printf("usage: del key|get key|put key=value|first|next\n"); vstream_fflush(VSTREAM_OUT); continue; } if (dict_changed_name()) msg_warn("dictionary has changed"); key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0; value = mystrtok(&bufp, " ="); if (strcmp(cmd, "del") == 0 && key && !value) { if (dict_del(dict, key)) vstream_printf("%s: not found\n", key); else vstream_printf("%s: deleted\n", key); } else if (strcmp(cmd, "get") == 0 && key && !value) { if ((value = dict_get(dict, key)) == 0) { vstream_printf("%s: %s\n", key, dict_errno == DICT_ERR_RETRY ? "soft error" : "not found"); } else { vstream_printf("%s=%s\n", key, value); } } else if (strcmp(cmd, "put") == 0 && key && value) { dict_put(dict, key, value); vstream_printf("%s=%s\n", key, value); } else if (strcmp(cmd, "first") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict_errno == DICT_ERR_RETRY ? "soft error" : "not found"); } else if (strcmp(cmd, "next") == 0 && !key && !value) { if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0) vstream_printf("%s=%s\n", key, value); else vstream_printf("%s\n", dict_errno == DICT_ERR_RETRY ? "soft error" : "not found"); } else { vstream_printf("usage: del key|get key|put key=value|first|next\n"); } vstream_fflush(VSTREAM_OUT); } vstring_free(keybuf); vstring_free(inbuf); dict_close(dict); return (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); }