"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/lib-dict/dict.c" between
dovecot-2.3.16.tar.gz and dovecot-2.3.17.tar.gz

About: Dovecot is an IMAP and POP3 server, written with security primarily in mind.

dict.c  (dovecot-2.3.16):dict.c  (dovecot-2.3.17)
skipping to change at line 18 skipping to change at line 18
#include "str.h" #include "str.h"
#include "ioloop.h" #include "ioloop.h"
#include "dict-private.h" #include "dict-private.h"
struct dict_commit_callback_ctx { struct dict_commit_callback_ctx {
pool_t pool; pool_t pool;
struct dict_commit_callback_ctx *prev, *next; struct dict_commit_callback_ctx *prev, *next;
struct dict *dict; struct dict *dict;
struct event *event; struct event *event;
dict_transaction_commit_callback_t *callback; dict_transaction_commit_callback_t *callback;
struct dict_op_settings_private set;
struct timeout *to; struct timeout *to;
void *context; void *context;
struct dict_commit_result result; struct dict_commit_result result;
bool delayed_callback:1; bool delayed_callback:1;
}; };
struct dict_lookup_callback_ctx { struct dict_lookup_callback_ctx {
struct dict *dict; struct dict *dict;
struct event *event; struct event *event;
dict_lookup_callback_t *callback; dict_lookup_callback_t *callback;
skipping to change at line 95 skipping to change at line 96
array_free(&dict_drivers); array_free(&dict_drivers);
} }
int dict_init(const char *uri, const struct dict_settings *set, int dict_init(const char *uri, const struct dict_settings *set,
struct dict **dict_r, const char **error_r) struct dict **dict_r, const char **error_r)
{ {
struct dict_settings set_dup = *set; struct dict_settings set_dup = *set;
struct dict *dict; struct dict *dict;
const char *p, *name, *error; const char *p, *name, *error;
i_assert(set->username != NULL);
p = strchr(uri, ':'); p = strchr(uri, ':');
if (p == NULL) { if (p == NULL) {
*error_r = t_strdup_printf("Dictionary URI is missing ':': %s", *error_r = t_strdup_printf("Dictionary URI is missing ':': %s",
uri); uri);
return -1; return -1;
} }
name = t_strdup_until(uri, p); name = t_strdup_until(uri, p);
dict = dict_driver_lookup(name); dict = dict_driver_lookup(name);
if (dict == NULL) { if (dict == NULL) {
*error_r = t_strdup_printf("Unknown dict module: %s", name); *error_r = t_strdup_printf("Unknown dict module: %s", name);
return -1; return -1;
} }
struct event *event = event_create(set->event_parent); struct event *event = event_create(set->event_parent);
event_add_category(event, &event_category_dict); event_add_category(event, &event_category_dict);
event_add_str(event, "driver", dict->name); event_add_str(event, "driver", dict->name);
if (set->username[0] != '\0') event_set_append_log_prefix(event, t_strdup_printf("dict(%s): ",
event_add_str(event, "user", set->username); dict->name));
event_set_append_log_prefix(event, t_strdup_printf("dict(%s)<%s>: ",
dict->name, set->username));
set_dup.event_parent = event; set_dup.event_parent = event;
if (dict->v.init(dict, p+1, &set_dup, dict_r, &error) < 0) { if (dict->v.init(dict, p+1, &set_dup, dict_r, &error) < 0) {
*error_r = t_strdup_printf("dict %s: %s", name, error); *error_r = t_strdup_printf("dict %s: %s", name, error);
event_unref(&event); event_unref(&event);
return -1; return -1;
} }
i_assert(*dict_r != NULL); i_assert(*dict_r != NULL);
(*dict_r)->refcount++; (*dict_r)->refcount++;
(*dict_r)->event = event; (*dict_r)->event = event;
e_debug(event_create_passthrough(event)->set_name("dict_created")->event(
),
"dict created (uri=%s, base_dir=%s)", uri, set->base_dir);
return 0; return 0;
} }
static void dict_ref(struct dict *dict) static void dict_ref(struct dict *dict)
{ {
i_assert(dict->refcount > 0); i_assert(dict->refcount > 0);
dict->refcount++; dict->refcount++;
} }
skipping to change at line 147 skipping to change at line 146
static void dict_unref(struct dict **_dict) static void dict_unref(struct dict **_dict)
{ {
struct dict *dict = *_dict; struct dict *dict = *_dict;
*_dict = NULL; *_dict = NULL;
if (dict == NULL) if (dict == NULL)
return; return;
struct event *event = dict->event; struct event *event = dict->event;
i_assert(dict->refcount > 0); i_assert(dict->refcount > 0);
if (--dict->refcount == 0) { if (--dict->refcount == 0) {
dict->v.deinit(dict); dict->v.deinit(dict);
e_debug(event_create_passthrough(event)->
set_name("dict_destroyed")->event(), "dict destroyed");
event_unref(&event); event_unref(&event);
} }
} }
void dict_deinit(struct dict **_dict) void dict_deinit(struct dict **_dict)
{ {
struct dict *dict = *_dict; struct dict *dict = *_dict;
*_dict = NULL; *_dict = NULL;
skipping to change at line 193 skipping to change at line 194
commit->to = io_loop_move_timeout(&commit->to); commit->to = io_loop_move_timeout(&commit->to);
ret = TRUE; ret = TRUE;
} }
if (dict->v.switch_ioloop != NULL) { if (dict->v.switch_ioloop != NULL) {
if (dict->v.switch_ioloop(dict)) if (dict->v.switch_ioloop(dict))
return TRUE; return TRUE;
} }
return ret; return ret;
} }
static bool dict_key_prefix_is_valid(const char *key) static bool dict_key_prefix_is_valid(const char *key, const char *username)
{ {
return str_begins(key, DICT_PATH_SHARED) || if (str_begins(key, DICT_PATH_SHARED))
str_begins(key, DICT_PATH_PRIVATE); return TRUE;
if (str_begins(key, DICT_PATH_PRIVATE)) {
i_assert(username != NULL && username[0] != '\0');
return TRUE;
}
return FALSE;
} }
void dict_pre_api_callback(struct dict *dict) void dict_pre_api_callback(struct dict *dict)
{ {
if (dict->prev_ioloop != NULL) { if (dict->prev_ioloop != NULL) {
/* Don't let callback see that we've created our /* Don't let callback see that we've created our
internal ioloop in case it wants to add some ios internal ioloop in case it wants to add some ios
or timeouts. */ or timeouts. */
io_loop_set_current(dict->prev_ioloop); io_loop_set_current(dict->prev_ioloop);
} }
skipping to change at line 277 skipping to change at line 284
DLLIST_REMOVE(&ctx->dict->commits, ctx); DLLIST_REMOVE(&ctx->dict->commits, ctx);
timeout_remove(&ctx->to); timeout_remove(&ctx->to);
dict_pre_api_callback(ctx->dict); dict_pre_api_callback(ctx->dict);
if (ctx->callback != NULL) if (ctx->callback != NULL)
ctx->callback(&ctx->result, ctx->context); ctx->callback(&ctx->result, ctx->context);
else if (ctx->result.ret < 0) else if (ctx->result.ret < 0)
e_error(ctx->event, "Commit failed: %s", ctx->result.error); e_error(ctx->event, "Commit failed: %s", ctx->result.error);
dict_post_api_callback(ctx->dict); dict_post_api_callback(ctx->dict);
dict_transaction_finished(ctx->event, ctx->result.ret, FALSE, ctx->result .error); dict_transaction_finished(ctx->event, ctx->result.ret, FALSE, ctx->result .error);
dict_op_settings_private_free(&ctx->set);
event_unref(&ctx->event); event_unref(&ctx->event);
dict_unref(&ctx->dict); dict_unref(&ctx->dict);
pool_unref(&ctx->pool); pool_unref(&ctx->pool);
} }
static void dict_commit_callback(const struct dict_commit_result *result, static void dict_commit_callback(const struct dict_commit_result *result,
void *context) void *context)
{ {
struct dict_commit_callback_ctx *ctx = context; struct dict_commit_callback_ctx *ctx = context;
i_assert(result->ret >= 0 || result->error != NULL); i_assert(result->ret >= 0 || result->error != NULL);
ctx->result = *result; ctx->result = *result;
if (ctx->delayed_callback) { if (ctx->delayed_callback) {
ctx->result.error = p_strdup(ctx->pool, ctx->result.error); ctx->result.error = p_strdup(ctx->pool, ctx->result.error);
ctx->to = timeout_add_short(0, dict_commit_async_timeout, ctx); ctx->to = timeout_add_short(0, dict_commit_async_timeout, ctx);
} else { } else {
dict_commit_async_timeout(ctx); dict_commit_async_timeout(ctx);
} }
} }
int dict_lookup(struct dict *dict, pool_t pool, const char *key, static struct event *
const char **value_r, const char **error_r) dict_event_create(struct dict *dict, const struct dict_op_settings *set)
{ {
struct event *event = event_create(dict->event); struct event *event = event_create(dict->event);
if (set->username != NULL)
event_add_str(event, "user", set->username);
return event;
}
int dict_lookup(struct dict *dict, const struct dict_op_settings *set,
pool_t pool, const char *key,
const char **value_r, const char **error_r)
{
struct event *event = dict_event_create(dict, set);
int ret; int ret;
i_assert(dict_key_prefix_is_valid(key)); i_assert(dict_key_prefix_is_valid(key, set->username));
e_debug(event, "Looking up '%s'", key); e_debug(event, "Looking up '%s'", key);
event_add_str(event, "key", key); event_add_str(event, "key", key);
ret = dict->v.lookup(dict, pool, key, value_r, error_r); ret = dict->v.lookup(dict, set, pool, key, value_r, error_r);
dict_lookup_finished(event, ret, *error_r); dict_lookup_finished(event, ret, *error_r);
event_unref(&event); event_unref(&event);
return ret; return ret;
} }
#undef dict_lookup_async #undef dict_lookup_async
void dict_lookup_async(struct dict *dict, const char *key, void dict_lookup_async(struct dict *dict, const struct dict_op_settings *set,
dict_lookup_callback_t *callback, void *context) const char *key, dict_lookup_callback_t *callback,
void *context)
{ {
if (dict->v.lookup_async == NULL) { if (dict->v.lookup_async == NULL) {
struct dict_lookup_result result; struct dict_lookup_result result;
i_zero(&result); i_zero(&result);
/* event is going to be sent by dict_lookup */ /* event is going to be sent by dict_lookup */
result.ret = dict_lookup(dict, pool_datastack_create(), result.ret = dict_lookup(dict, set, pool_datastack_create(),
key, &result.value, &result.error); key, &result.value, &result.error);
const char *const values[] = { result.value, NULL }; const char *const values[] = { result.value, NULL };
result.values = values; result.values = values;
callback(&result, context); callback(&result, context);
return; return;
} }
struct dict_lookup_callback_ctx *lctx = struct dict_lookup_callback_ctx *lctx =
i_new(struct dict_lookup_callback_ctx, 1); i_new(struct dict_lookup_callback_ctx, 1);
lctx->dict = dict; lctx->dict = dict;
dict_ref(lctx->dict); dict_ref(lctx->dict);
lctx->callback = callback; lctx->callback = callback;
lctx->context = context; lctx->context = context;
lctx->event = event_create(dict->event); lctx->event = dict_event_create(dict, set);
event_add_str(lctx->event, "key", key); event_add_str(lctx->event, "key", key);
e_debug(lctx->event, "Looking up (async) '%s'", key); e_debug(lctx->event, "Looking up (async) '%s'", key);
dict->v.lookup_async(dict, key, dict_lookup_callback, lctx); dict->v.lookup_async(dict, set, key, dict_lookup_callback, lctx);
} }
struct dict_iterate_context * struct dict_iterate_context *
dict_iterate_init(struct dict *dict, const char *path, dict_iterate_init(struct dict *dict, const struct dict_op_settings *set,
enum dict_iterate_flags flags) const char *path, enum dict_iterate_flags flags)
{
const char *paths[2];
paths[0] = path;
paths[1] = NULL;
return dict_iterate_init_multiple(dict, paths, flags);
}
struct dict_iterate_context *
dict_iterate_init_multiple(struct dict *dict, const char *const *paths,
enum dict_iterate_flags flags)
{ {
struct dict_iterate_context *ctx; struct dict_iterate_context *ctx;
unsigned int i;
i_assert(paths[0] != NULL); i_assert(path != NULL);
for (i = 0; paths[i] != NULL; i++) i_assert(dict_key_prefix_is_valid(path, set->username));
i_assert(dict_key_prefix_is_valid(paths[i]));
if (dict->v.iterate_init == NULL) { if (dict->v.iterate_init == NULL) {
/* not supported by backend */ /* not supported by backend */
ctx = &dict_iter_unsupported; ctx = &dict_iter_unsupported;
} else { } else {
ctx = dict->v.iterate_init(dict, paths, flags); ctx = dict->v.iterate_init(dict, set, path, flags);
} }
/* the dict in context can differ from the dict /* the dict in context can differ from the dict
passed as parameter, e.g. it can be dict-fail when passed as parameter, e.g. it can be dict-fail when
iteration is not supported. */ iteration is not supported. */
ctx->event = event_create(dict->event); ctx->event = dict_event_create(dict, set);
ctx->flags = flags; ctx->flags = flags;
dict_op_settings_dup(set, &ctx->set);
event_add_str(ctx->event, "key", paths[0]); event_add_str(ctx->event, "key", path);
event_set_name(ctx->event, "dict_iteration_started"); event_set_name(ctx->event, "dict_iteration_started");
e_debug(ctx->event, "Iterating prefix %s", paths[0]); e_debug(ctx->event, "Iterating prefix %s", path);
ctx->dict->iter_count++; ctx->dict->iter_count++;
return ctx; return ctx;
} }
bool dict_iterate(struct dict_iterate_context *ctx, bool dict_iterate(struct dict_iterate_context *ctx,
const char **key_r, const char **value_r) const char **key_r, const char **value_r)
{ {
const char *const *values; const char *const *values;
if (!dict_iterate_values(ctx, key_r, &values)) if (!dict_iterate_values(ctx, key_r, &values))
skipping to change at line 454 skipping to change at line 461
struct event *event = ctx->event; struct event *event = ctx->event;
int ret; int ret;
uint64_t rows; uint64_t rows;
i_assert(ctx->dict->iter_count > 0); i_assert(ctx->dict->iter_count > 0);
ctx->dict->iter_count--; ctx->dict->iter_count--;
*_ctx = NULL; *_ctx = NULL;
rows = ctx->row_count; rows = ctx->row_count;
struct dict_op_settings_private set_copy = ctx->set;
ret = ctx->dict->v.iterate_deinit(ctx, error_r); ret = ctx->dict->v.iterate_deinit(ctx, error_r);
dict_op_settings_private_free(&set_copy);
event_add_int(event, "rows", rows); event_add_int(event, "rows", rows);
event_set_name(event, "dict_iteration_finished"); event_set_name(event, "dict_iteration_finished");
if (ret < 0) { if (ret < 0) {
event_add_str(event, "error", *error_r); event_add_str(event, "error", *error_r);
e_debug(event, "Iteration finished: %s", *error_r); e_debug(event, "Iteration finished: %s", *error_r);
} else { } else {
if (rows == 0) if (rows == 0)
event_add_str(event, "key_not_found", "yes"); event_add_str(event, "key_not_found", "yes");
e_debug(event, "Iteration finished, got %"PRIu64" rows", rows); e_debug(event, "Iteration finished, got %"PRIu64" rows", rows);
} }
event_unref(&event); event_unref(&event);
return ret; return ret;
} }
struct dict_transaction_context *dict_transaction_begin(struct dict *dict) struct dict_transaction_context *
dict_transaction_begin(struct dict *dict, const struct dict_op_settings *set)
{ {
struct dict_transaction_context *ctx; struct dict_transaction_context *ctx;
guid_128_t guid; guid_128_t guid;
if (dict->v.transaction_init == NULL) if (dict->v.transaction_init == NULL)
ctx = &dict_transaction_unsupported; ctx = &dict_transaction_unsupported;
else else
ctx = dict->v.transaction_init(dict); ctx = dict->v.transaction_init(dict);
/* the dict in context can differ from the dict /* the dict in context can differ from the dict
passed as parameter, e.g. it can be dict-fail when passed as parameter, e.g. it can be dict-fail when
transactions are not supported. */ transactions are not supported. */
ctx->dict->transaction_count++; ctx->dict->transaction_count++;
DLLIST_PREPEND(&ctx->dict->transactions, ctx); DLLIST_PREPEND(&ctx->dict->transactions, ctx);
ctx->event = event_create(dict->event); ctx->event = dict_event_create(dict, set);
dict_op_settings_dup(set, &ctx->set);
guid_128_generate(guid); guid_128_generate(guid);
event_add_str(ctx->event, "txid", guid_128_to_string(guid)); event_add_str(ctx->event, "txid", guid_128_to_string(guid));
event_set_name(ctx->event, "dict_transaction_started"); event_set_name(ctx->event, "dict_transaction_started");
e_debug(ctx->event, "Starting transaction"); e_debug(ctx->event, "Starting transaction");
return ctx; return ctx;
} }
void dict_transaction_no_slowness_warning(struct dict_transaction_context *ctx) void dict_transaction_no_slowness_warning(struct dict_transaction_context *ctx)
{ {
ctx->no_slowness_warning = TRUE; ctx->no_slowness_warning = TRUE;
skipping to change at line 555 skipping to change at line 566
i_zero(&result); i_zero(&result);
i_assert(ctx->dict->transaction_count > 0); i_assert(ctx->dict->transaction_count > 0);
ctx->dict->transaction_count--; ctx->dict->transaction_count--;
DLLIST_REMOVE(&ctx->dict->transactions, ctx); DLLIST_REMOVE(&ctx->dict->transactions, ctx);
DLLIST_PREPEND(&ctx->dict->commits, cctx); DLLIST_PREPEND(&ctx->dict->commits, cctx);
cctx->dict = ctx->dict; cctx->dict = ctx->dict;
dict_ref(cctx->dict); dict_ref(cctx->dict);
cctx->callback = dict_transaction_commit_sync_callback; cctx->callback = dict_transaction_commit_sync_callback;
cctx->context = &result; cctx->context = &result;
cctx->event = ctx->event; cctx->event = ctx->event;
cctx->set = ctx->set;
ctx->dict->v.transaction_commit(ctx, FALSE, dict_commit_callback, cctx); ctx->dict->v.transaction_commit(ctx, FALSE, dict_commit_callback, cctx);
*error_r = t_strdup(result.error); *error_r = t_strdup(result.error);
i_free(result.error); i_free(result.error);
return result.ret; return result.ret;
} }
#undef dict_transaction_commit_async #undef dict_transaction_commit_async
void dict_transaction_commit_async(struct dict_transaction_context **_ctx, void dict_transaction_commit_async(struct dict_transaction_context **_ctx,
dict_transaction_commit_callback_t *callback, dict_transaction_commit_callback_t *callback,
skipping to change at line 585 skipping to change at line 597
DLLIST_REMOVE(&ctx->dict->transactions, ctx); DLLIST_REMOVE(&ctx->dict->transactions, ctx);
DLLIST_PREPEND(&ctx->dict->commits, cctx); DLLIST_PREPEND(&ctx->dict->commits, cctx);
if (callback == NULL) if (callback == NULL)
callback = dict_transaction_commit_async_noop_callback; callback = dict_transaction_commit_async_noop_callback;
cctx->pool = pool; cctx->pool = pool;
cctx->dict = ctx->dict; cctx->dict = ctx->dict;
dict_ref(cctx->dict); dict_ref(cctx->dict);
cctx->callback = callback; cctx->callback = callback;
cctx->context = context; cctx->context = context;
cctx->event = ctx->event; cctx->event = ctx->event;
cctx->set = ctx->set;
cctx->delayed_callback = TRUE; cctx->delayed_callback = TRUE;
ctx->dict->v.transaction_commit(ctx, TRUE, dict_commit_callback, cctx); ctx->dict->v.transaction_commit(ctx, TRUE, dict_commit_callback, cctx);
cctx->delayed_callback = FALSE; cctx->delayed_callback = FALSE;
} }
void dict_transaction_commit_async_nocallback( void dict_transaction_commit_async_nocallback(
struct dict_transaction_context **ctx) struct dict_transaction_context **ctx)
{ {
dict_transaction_commit_async(ctx, NULL, NULL); dict_transaction_commit_async(ctx, NULL, NULL);
} }
skipping to change at line 609 skipping to change at line 622
if (ctx == NULL) if (ctx == NULL)
return; return;
struct event *event = ctx->event; struct event *event = ctx->event;
*_ctx = NULL; *_ctx = NULL;
i_assert(ctx->dict->transaction_count > 0); i_assert(ctx->dict->transaction_count > 0);
ctx->dict->transaction_count--; ctx->dict->transaction_count--;
DLLIST_REMOVE(&ctx->dict->transactions, ctx); DLLIST_REMOVE(&ctx->dict->transactions, ctx);
struct dict_op_settings_private set_copy = ctx->set;
ctx->dict->v.transaction_rollback(ctx); ctx->dict->v.transaction_rollback(ctx);
dict_transaction_finished(event, DICT_COMMIT_RET_OK, TRUE, NULL); dict_transaction_finished(event, DICT_COMMIT_RET_OK, TRUE, NULL);
dict_op_settings_private_free(&set_copy);
event_unref(&event); event_unref(&event);
} }
void dict_set(struct dict_transaction_context *ctx, void dict_set(struct dict_transaction_context *ctx,
const char *key, const char *value) const char *key, const char *value)
{ {
i_assert(dict_key_prefix_is_valid(key)); i_assert(dict_key_prefix_is_valid(key, ctx->set.username));
struct event_passthrough *e = event_create_passthrough(ctx->event)-> struct event_passthrough *e = event_create_passthrough(ctx->event)->
set_name("dict_set_key")-> set_name("dict_set_key")->
add_str("key", key); add_str("key", key);
e_debug(e->event(), "Setting '%s' to '%s'", key, value); e_debug(e->event(), "Setting '%s' to '%s'", key, value);
T_BEGIN { T_BEGIN {
ctx->dict->v.set(ctx, key, value); ctx->dict->v.set(ctx, key, value);
} T_END; } T_END;
ctx->changed = TRUE; ctx->changed = TRUE;
} }
void dict_unset(struct dict_transaction_context *ctx, void dict_unset(struct dict_transaction_context *ctx,
const char *key) const char *key)
{ {
i_assert(dict_key_prefix_is_valid(key)); i_assert(dict_key_prefix_is_valid(key, ctx->set.username));
struct event_passthrough *e = event_create_passthrough(ctx->event)-> struct event_passthrough *e = event_create_passthrough(ctx->event)->
set_name("dict_unset_key")-> set_name("dict_unset_key")->
add_str("key", key); add_str("key", key);
e_debug(e->event(), "Unsetting '%s'", key); e_debug(e->event(), "Unsetting '%s'", key);
T_BEGIN { T_BEGIN {
ctx->dict->v.unset(ctx, key); ctx->dict->v.unset(ctx, key);
} T_END; } T_END;
ctx->changed = TRUE; ctx->changed = TRUE;
} }
void dict_atomic_inc(struct dict_transaction_context *ctx, void dict_atomic_inc(struct dict_transaction_context *ctx,
const char *key, long long diff) const char *key, long long diff)
{ {
i_assert(dict_key_prefix_is_valid(key)); i_assert(dict_key_prefix_is_valid(key, ctx->set.username));
struct event_passthrough *e = event_create_passthrough(ctx->event)-> struct event_passthrough *e = event_create_passthrough(ctx->event)->
set_name("dict_increment_key")-> set_name("dict_increment_key")->
add_str("key", key); add_str("key", key);
e_debug(e->event(), "Incrementing '%s' with %lld", key, diff); e_debug(e->event(), "Incrementing '%s' with %lld", key, diff);
if (diff != 0) T_BEGIN { if (diff != 0) T_BEGIN {
ctx->dict->v.atomic_inc(ctx, key, diff); ctx->dict->v.atomic_inc(ctx, key, diff);
ctx->changed = TRUE; ctx->changed = TRUE;
} T_END; } T_END;
skipping to change at line 730 skipping to change at line 745
if (*++p == '|') if (*++p == '|')
str_append_c(ret, '/'); str_append_c(ret, '/');
else if (*p == '\0') else if (*p == '\0')
break; break;
else else
str_append_c(ret, *p); str_append_c(ret, *p);
} }
} }
return str_c(ret); return str_c(ret);
} }
void dict_op_settings_dup(const struct dict_op_settings *source,
struct dict_op_settings_private *dest_r)
{
i_zero(dest_r);
dest_r->username = i_strdup(source->username);
dest_r->home_dir = i_strdup(source->home_dir);
}
void dict_op_settings_private_free(struct dict_op_settings_private *set)
{
i_free(set->username);
i_free(set->home_dir);
}
 End of changes. 36 change blocks. 
44 lines changed or deleted 60 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)