Esempio n. 1
0
static void stats_user_created(struct mail_user *user)
{
	struct ioloop_context *ioloop_ctx =
		io_loop_get_current_context(current_ioloop);
	struct stats_user *suser;
	struct mail_user_vfuncs *v = user->vlast;
	const char *path, *str, *error;
	unsigned int refresh_secs;

	if (ioloop_ctx == NULL) {
		/* we're probably running some test program, or at least
		   mail-storage-service wasn't used to create this user.
		   disable stats tracking. */
		return;
	}
	if (user->autocreated) {
		/* lda / shared user. we're not tracking this one. */
		return;
	}

	/* get refresh time */
	str = mail_user_plugin_getenv(user, "stats_refresh");
	if (str == NULL)
		return;
	if (settings_get_time(str, &refresh_secs, &error) < 0) {
		i_error("stats: Invalid stats_refresh setting: %s", error);
		return;
	}
	if (refresh_secs == 0)
		return;
	if (refresh_secs > SESSION_STATS_FORCE_REFRESH_SECS) {
		i_warning("stats: stats_refresh too large, changing to %u",
			  SESSION_STATS_FORCE_REFRESH_SECS);
		refresh_secs = SESSION_STATS_FORCE_REFRESH_SECS;
	}

	if (global_stats_conn == NULL) {
		path = t_strconcat(user->set->base_dir,
				   "/"MAIL_STATS_SOCKET_NAME, NULL);
		global_stats_conn = stats_connection_create(path);
	}
	stats_connection_ref(global_stats_conn);

	if (stats_user_count == 0) {
		/* first user connection */
		stats_global_user = user;
	} else if (stats_user_count == 1) {
		/* second user connection. we'll need to start doing
		   per-io callback tracking now. (we might have been doing it
		   also previously but just temporarily quickly dropped to
		   having 1 user, in which case stats_global_user=NULL) */
		if (stats_global_user != NULL) {
			stats_add_session(stats_global_user);
			stats_global_user = NULL;
		}
	}
	stats_user_count++;

	suser = p_new(user->pool, struct stats_user, 1);
	suser->module_ctx.super = *v;
	user->vlast = &suser->module_ctx.super;
	v->deinit = stats_user_deinit;
	v->stats_fill = stats_user_stats_fill;

	suser->refresh_secs = refresh_secs;
	str = mail_user_plugin_getenv(user, "stats_track_cmds");
	if (str != NULL && strcmp(str, "yes") == 0)
		suser->track_commands = TRUE;

	suser->stats_conn = global_stats_conn;
	if (user->session_id != NULL && user->session_id[0] != '\0')
		suser->stats_session_id = user->session_id;
	else {
		guid_128_t guid;

		guid_128_generate(guid);
		suser->stats_session_id =
			p_strdup(user->pool, guid_128_to_string(guid));
	}
	suser->last_session_update = time(NULL);
	user->stats_enabled = TRUE;

	suser->ioloop_ctx = ioloop_ctx;
	io_loop_context_add_callbacks(ioloop_ctx,
				      stats_io_activate,
				      stats_io_deactivate, user);

	suser->pre_io_stats = stats_alloc(user->pool);
	suser->session_stats = stats_alloc(user->pool);
	suser->last_sent_session_stats = stats_alloc(user->pool);

	MODULE_CONTEXT_SET(user, stats_user_module, suser);
	mail_stats_connection_connect(suser->stats_conn, user);
	suser->to_stats_timeout =
		timeout_add(suser->refresh_secs*1000,
			    session_stats_refresh_timeout, user);
	/* fill the initial values. this is necessary for the process-global
	   values (e.g. getrusage()) if the process is reused for multiple
	   users. otherwise the next user will start with the previous one's
	   last values. */
	mail_user_stats_fill(user, suser->pre_io_stats);
}
static int
sync_create_box(struct dsync_brain *brain, struct mailbox *box,
		const guid_128_t mailbox_guid, uint32_t uid_validity,
		enum mail_error *error_r)
{
	struct mailbox_metadata metadata;
	struct mailbox_update update;
	enum mail_error error;
	const char *errstr;
	int ret;

	memset(&update, 0, sizeof(update));
	memcpy(update.mailbox_guid, mailbox_guid, sizeof(update.mailbox_guid));
	update.uid_validity = uid_validity;

	if (mailbox_create(box, &update, FALSE) < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error != MAIL_ERROR_EXISTS) {
			i_error("Can't create mailbox %s: %s",
				mailbox_get_vname(box), errstr);
			*error_r = error;
			return -1;
		}
	}
	if (brain->no_mail_sync) {
		/* trust that create worked, we can't actually open it
		   and verify. */
		return 0;
	}
	/* sync the mailbox so we can look up its latest status */
	if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
		i_error("Can't sync mailbox %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, error_r));
		return -1;
	}

	/* verify that the GUID is what we wanted. if it's not, it probably
	   means that the mailbox had already been created. then we'll use the
	   GUID that is higher.

	   mismatching UIDVALIDITY is handled later, because we choose it by
	   checking which mailbox has more messages */
	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
		i_error("Can't get mailbox GUID %s: %s",
			mailbox_get_vname(box),
			mailbox_get_last_error(box, error_r));
		return -1;
	}

	ret = memcmp(mailbox_guid, metadata.guid, sizeof(metadata.guid));
	if (ret > 0) {
		if (brain->debug) {
			i_debug("brain %c: Changing mailbox %s GUID %s -> %s",
				brain->master_brain ? 'M' : 'S',
				mailbox_get_vname(box),
				guid_128_to_string(metadata.guid),
				guid_128_to_string(mailbox_guid));
		}
		memset(&update, 0, sizeof(update));
		memcpy(update.mailbox_guid, mailbox_guid,
		       sizeof(update.mailbox_guid));
		if (mailbox_update(box, &update) < 0) {
			i_error("Can't update mailbox GUID %s: %s",
				mailbox_get_vname(box),
				mailbox_get_last_error(box, error_r));
			return -1;
		}
		/* verify that the update worked */
		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
					 &metadata) < 0) {
			i_error("Can't get mailbox GUID %s: %s",
				mailbox_get_vname(box),
				mailbox_get_last_error(box, error_r));
			return -1;
		}
		if (memcmp(mailbox_guid, metadata.guid,
			   sizeof(metadata.guid)) != 0) {
			i_error("Backend didn't update mailbox %s GUID",
				mailbox_get_vname(box));
			*error_r = MAIL_ERROR_TEMP;
			return -1;
		}
	} else if (ret < 0) {
		if (brain->debug) {
			i_debug("brain %c: Other brain should change mailbox "
				"%s GUID %s -> %s",
				brain->master_brain ? 'M' : 'S',
				mailbox_get_vname(box),
				guid_128_to_string(mailbox_guid),
				guid_128_to_string(metadata.guid));
		}
	}
	return 0;
}
int dsync_brain_mailbox_tree_sync_change(struct dsync_brain *brain,
			const struct dsync_mailbox_tree_sync_change *change,
			enum mail_error *error_r)
{
	struct mailbox *box = NULL, *destbox;
	const char *errstr, *func_name = NULL, *storage_name;
	enum mail_error error;
	int ret = -1;

	if (brain->backup_send) {
		i_assert(brain->no_backup_overwrite);
		return 0;
	}

	switch (change->type) {
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
		/* make sure we're deleting the correct mailbox */
		ret = dsync_brain_mailbox_alloc(brain, change->mailbox_guid,
						&box, &errstr, error_r);
		if (ret < 0) {
			i_error("Mailbox sync: Couldn't allocate mailbox GUID %s: %s",
				guid_128_to_string(change->mailbox_guid), errstr);
			return -1;
		}
		if (ret == 0) {
			if (brain->debug) {
				i_debug("brain %c: Change during sync: "
					"Mailbox GUID %s deletion conflict: %s",
					brain->master_brain ? 'M' : 'S',
					guid_128_to_string(change->mailbox_guid), errstr);
			}
			brain->changes_during_sync = TRUE;
			return 0;
		}
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
		storage_name = mailbox_list_get_storage_name(change->ns->list,
							     change->full_name);
		if (mailbox_list_delete_dir(change->ns->list, storage_name) == 0)
			return 0;

		errstr = mailbox_list_get_last_error(change->ns->list, &error);
		if (error == MAIL_ERROR_NOTFOUND ||
		    error == MAIL_ERROR_EXISTS) {
			if (brain->debug) {
				i_debug("brain %c: Change during sync: "
					"Mailbox %s mailbox_list_delete_dir conflict: %s",
					brain->master_brain ? 'M' : 'S',
					change->full_name, errstr);
			}
			brain->changes_during_sync = TRUE;
			return 0;
		} else {
			i_error("Mailbox sync: mailbox_list_delete_dir failed: %s",
				errstr);
			*error_r = error;
			return -1;
		}
	default:
		box = mailbox_alloc(change->ns->list, change->full_name, 0);
		break;
	}
	switch (change->type) {
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_BOX:
		ret = sync_create_box(brain, box, change->mailbox_guid,
				      change->uid_validity, error_r);
		mailbox_free(&box);
		return ret;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_CREATE_DIR:
		ret = mailbox_create(box, NULL, TRUE);
		if (ret < 0 &&
		    mailbox_get_last_mail_error(box) == MAIL_ERROR_EXISTS) {
			/* it doesn't matter if somebody else created this
			   directory or we automatically did while creating its
			   child mailbox. it's there now anyway and we don't
			   gain anything by treating this failure any
			   differently from success. */
			ret = 0;
		}
		func_name = "mailbox_create";
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_BOX:
		ret = mailbox_delete(box);
		func_name = "mailbox_delete";
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_DELETE_DIR:
		i_unreached();
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_RENAME:
		destbox = mailbox_alloc(change->ns->list,
					change->rename_dest_name, 0);
		ret = mailbox_rename(box, destbox);
		func_name = "mailbox_rename";
		mailbox_free(&destbox);
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_SUBSCRIBE:
		ret = mailbox_set_subscribed(box, TRUE);
		func_name = "mailbox_set_subscribed";
		break;
	case DSYNC_MAILBOX_TREE_SYNC_TYPE_UNSUBSCRIBE:
		ret = mailbox_set_subscribed(box, FALSE);
		func_name = "mailbox_set_subscribed";
		break;
	}
	if (ret < 0) {
		errstr = mailbox_get_last_error(box, &error);
		if (error == MAIL_ERROR_EXISTS ||
		    error == MAIL_ERROR_NOTFOUND) {
			/* mailbox was already created or was already deleted.
			   let the next sync figure out what to do */
			if (brain->debug) {
				i_debug("brain %c: Change during sync: "
					"Mailbox %s %s conflict: %s",
					brain->master_brain ? 'M' : 'S',
					mailbox_get_vname(box),
					func_name, errstr);
			}
			brain->changes_during_sync = TRUE;
			ret = 0;
		} else {
			i_error("Mailbox %s sync: %s failed: %s",
				mailbox_get_vname(box), func_name, errstr);
			*error_r = error;
		}
	}
	mailbox_free(&box);
	return ret;
}
Esempio n. 4
0
static void
dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname_str,
		       char alt_char)
{
	const char *old_vname;
	char *vname, list_sep = mailbox_list_get_hierarchy_sep(ns->list);
	guid_128_t guid;
	unsigned int i, start_pos;

	vname = str_c_modifiable(vname_str);
	if (strncmp(vname, ns->prefix, ns->prefix_len) == 0)
		start_pos = ns->prefix_len;
	else
		start_pos = 0;

	/* replace control chars */
	for (i = start_pos; vname[i] != '\0'; i++) {
		if ((unsigned char)vname[i] < ' ')
			vname[i] = alt_char;
	}
	/* make it valid UTF8 */
	if (!uni_utf8_str_is_valid(vname)) {
		old_vname = t_strdup(vname + start_pos);
		str_truncate(vname_str, start_pos);
		if (uni_utf8_get_valid_data((const void *)old_vname,
					    strlen(old_vname), vname_str))
			i_unreached();
		vname = str_c_modifiable(vname_str);
	}
	if (dsync_is_valid_name(ns, vname))
		return;

	/* 1) change any real separators to alt separators (this wouldn't
	   be necessary with listescape, but don't bother detecting it) */
	if (list_sep != mail_namespace_get_sep(ns)) {
		for (i = start_pos; vname[i] != '\0'; i++) {
			if (vname[i] == list_sep)
				vname[i] = alt_char;
		}
		if (dsync_is_valid_name(ns, vname))
			return;
	}
	/* 2) '/' characters aren't valid without listescape */
	if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') {
		for (i = start_pos; vname[i] != '\0'; i++) {
			if (vname[i] == '/')
				vname[i] = alt_char;
		}
		if (dsync_is_valid_name(ns, vname))
			return;
	}
	/* 3) probably some reserved name (e.g. dbox-Mails) */
	str_insert(vname_str, ns->prefix_len, "_");
	if (dsync_is_valid_name(ns, str_c(vname_str)))
		return;

	/* 4) name is too long? just give up and generate a unique name */
	guid_128_generate(guid);
	str_truncate(vname_str, 0);
	str_append(vname_str, ns->prefix);
	str_append(vname_str, guid_128_to_string(guid));
	i_assert(dsync_is_valid_name(ns, str_c(vname_str)));
}
Esempio n. 5
0
void dbox_save_write_metadata(struct mail_save_context *_ctx,
			      struct ostream *output, uoff_t output_msg_size,
			      const char *orig_mailbox_name,
			      guid_128_t guid_128)
{
	struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
	struct mail_save_data *mdata = &ctx->ctx.data;
	struct dbox_metadata_header metadata_hdr;
	const char *guid;
	string_t *str;
	uoff_t vsize;

	memset(&metadata_hdr, 0, sizeof(metadata_hdr));
	memcpy(metadata_hdr.magic_post, DBOX_MAGIC_POST,
	       sizeof(metadata_hdr.magic_post));
	o_stream_nsend(output, &metadata_hdr, sizeof(metadata_hdr));

	str = t_str_new(256);
	if (output_msg_size != ctx->input->v_offset) {
		/* a plugin changed the data written to disk, so the
		   "message size" dbox header doesn't contain the actual
		   "physical" message size. we need to save it as a
		   separate metadata header. */
		str_printfa(str, "%c%llx\n", DBOX_METADATA_PHYSICAL_SIZE,
			    (unsigned long long)ctx->input->v_offset);
	}
	str_printfa(str, "%c%lx\n", DBOX_METADATA_RECEIVED_TIME,
		    (unsigned long)mdata->received_date);
	if (mail_get_virtual_size(_ctx->dest_mail, &vsize) < 0)
		i_unreached();
	str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE,
		    (unsigned long long)vsize);
	if (mdata->pop3_uidl != NULL) {
		i_assert(strchr(mdata->pop3_uidl, '\n') == NULL);
		str_printfa(str, "%c%s\n", DBOX_METADATA_POP3_UIDL,
			    mdata->pop3_uidl);
	}

	guid = mdata->guid;
	if (guid != NULL)
		mail_generate_guid_128_hash(guid, guid_128);
	else {
		guid_128_generate(guid_128);
		guid = guid_128_to_string(guid_128);
	}
	str_printfa(str, "%c%s\n", DBOX_METADATA_GUID, guid);

	if (orig_mailbox_name != NULL &&
	    strchr(orig_mailbox_name, '\r') == NULL &&
	    strchr(orig_mailbox_name, '\n') == NULL) {
		/* save the original mailbox name so if mailbox indexes get
		   corrupted we can place at least some (hopefully most) of
		   the messages to correct mailboxes. */
		str_printfa(str, "%c%s\n", DBOX_METADATA_ORIG_MAILBOX,
			    orig_mailbox_name);
	}

	dbox_attachment_save_write_metadata(_ctx, str);

	str_append_c(str, '\n');
	o_stream_nsend(output, str_data(str), str_len(str));
}
Esempio n. 6
0
void test_guid(void)
{
	static const guid_128_t test_guid =
	{ 0x01, 0x23, 0x45, 0x67, 0x89,
	  0xab, 0xcd, 0xef,
	  0xAB, 0xCD, 0xEF,
	  0x00, 0x00, 0x00, 0x00, 0x00 };
	guid_128_t guid1, guid2, guid3;
	const char *str;
	char guidbuf[GUID_128_SIZE*2 + 2];
	unsigned int i;

	test_begin("guid_128_generate()");
	guid_128_generate(guid1);
	guid_128_generate(guid2);
	test_assert(!guid_128_equals(guid1, guid2));
	test_assert(guid_128_cmp(guid1, guid2) != 0);
	test_end();

	test_begin("guid_128_is_empty()");
	test_assert(!guid_128_is_empty(guid1));
	test_assert(!guid_128_is_empty(guid2));
	guid_128_generate(guid3);
	guid_128_empty(guid3);
	test_assert(guid_128_is_empty(guid3));
	test_end();

	test_begin("guid_128_copy()");
	guid_128_copy(guid3, guid1);
	test_assert(guid_128_equals(guid3, guid1));
	test_assert(!guid_128_equals(guid3, guid2));
	guid_128_copy(guid3, guid2);
	test_assert(!guid_128_equals(guid3, guid1));
	test_assert(guid_128_equals(guid3, guid2));
	test_end();

	test_begin("guid_128_to_string()");
	str = guid_128_to_string(guid1);
	test_assert(guid_128_from_string(str, guid3) == 0);
	test_assert(guid_128_equals(guid3, guid1));
	test_end();

	test_begin("guid_128_from_string()");
	/* empty */
	memset(guidbuf, '0', GUID_128_SIZE*2);
	guidbuf[GUID_128_SIZE*2] = '\0';
	guidbuf[GUID_128_SIZE*2+1] = '\0';
	test_assert(guid_128_from_string(guidbuf, guid3) == 0);
	test_assert(guid_128_is_empty(guid3));
	/* too large */
	guidbuf[GUID_128_SIZE*2] = '0';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	/* too small */
	guidbuf[GUID_128_SIZE*2-1] = '\0';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	/* reset to normal */
	guidbuf[GUID_128_SIZE*2-1] = '0';
	guidbuf[GUID_128_SIZE*2] = '\0';
	test_assert(guid_128_from_string(guidbuf, guid3) == 0);
	/* upper + lowercase hex chars */
	i_assert(GUID_128_SIZE*2 > 16 + 6);
	for (i = 0; i < 10; i++)
		guidbuf[i] = '0' + i;
	for (i = 0; i < 6; i++)
		guidbuf[10 + i] = 'a' + i;
	for (i = 0; i < 6; i++)
		guidbuf[16 + i] = 'A' + i;
	test_assert(guid_128_from_string(guidbuf, guid3) == 0);
	test_assert(guid_128_equals(guid3, test_guid));
	/* non-hex chars */
	guidbuf[0] = 'g';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	guidbuf[0] = ' ';
	test_assert(guid_128_from_string(guidbuf, guid3) < 0);
	test_end();
}