예제 #1
0
파일: cmd-search.c 프로젝트: aosm/dovecot
bool cmd_search(struct client_command_context *cmd)
{
	struct imap_search_context *ctx;
	struct mail_search_args *sargs;
	const struct imap_arg *args;
	const char *charset;
	int ret;

	if (!client_read_args(cmd, 0, 0, &args))
		return FALSE;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	ctx = p_new(cmd->pool, struct imap_search_context, 1);
	ctx->cmd = cmd;

	if ((ret = cmd_search_parse_return_if_found(ctx, &args)) <= 0) {
		/* error / waiting for unambiguity */
		return ret < 0;
	}

	if (imap_arg_atom_equals(args, "CHARSET")) {
		/* CHARSET specified */
		if (!imap_arg_get_astring(&args[1], &charset)) {
			client_send_command_error(cmd,
				"Invalid charset argument.");
			return TRUE;
		}
		args += 2;
	} else {
		charset = "UTF-8";
	}

	ret = imap_search_args_build(cmd, args, charset, &sargs);
	if (ret <= 0)
		return ret < 0;

	return imap_search_start(ctx, sargs, NULL);
}
예제 #2
0
bool cmd_uid_expunge(struct client_command_context *cmd)
{
	const struct imap_arg *args;
	struct mail_search_args *search_args;
	const char *uidset;
	int ret;

	if (!client_read_args(cmd, 1, 0, &args))
		return FALSE;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	if (!imap_arg_get_astring(&args[0], &uidset)) {
		client_send_command_error(cmd, "Invalid arguments.");
		return TRUE;
	}

	ret = imap_search_get_seqset(cmd, uidset, TRUE, &search_args);
	if (ret <= 0)
		return ret < 0;
	return cmd_expunge_finish(cmd, search_args);
}
예제 #3
0
bool cmd_fetch(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct imap_fetch_context *ctx;
	const struct imap_arg *args, *next_arg, *list_arg;
	struct mail_search_args *search_args;
	struct imap_fetch_qresync_args qresync_args;
	const char *messageset;
	bool send_vanished = FALSE;
	int ret;

	if (!client_read_args(cmd, 0, 0, &args))
		return FALSE;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	/* <messageset> <field(s)> [(modifiers)] */
	if (!imap_arg_get_atom(&args[0], &messageset) ||
	    (args[1].type != IMAP_ARG_LIST && args[1].type != IMAP_ARG_ATOM) ||
	    (!IMAP_ARG_IS_EOL(&args[2]) && args[2].type != IMAP_ARG_LIST)) {
		client_send_command_error(cmd, "Invalid arguments.");
		return TRUE;
	}

	/* UID FETCH VANISHED needs the uidset, so convert it to
	   sequence set later */
	ret = imap_search_get_anyset(cmd, messageset, cmd->uid, &search_args);
	if (ret <= 0)
		return ret < 0;

	ctx = imap_fetch_alloc(client, cmd->pool);

	if (!fetch_parse_args(ctx, cmd, &args[1], &next_arg) ||
	    (imap_arg_get_list(next_arg, &list_arg) &&
	     !fetch_parse_modifiers(ctx, cmd, search_args, list_arg,
				    &send_vanished))) {
		imap_fetch_free(&ctx);
		mail_search_args_unref(&search_args);
		return TRUE;
	}

	if (send_vanished) {
		memset(&qresync_args, 0, sizeof(qresync_args));
		if (imap_fetch_send_vanished(client, client->mailbox,
					     search_args, &qresync_args) < 0) {
			mail_search_args_unref(&search_args);
			return cmd_fetch_finish(ctx, cmd);
		}
	}

	imap_fetch_begin(ctx, client->mailbox, search_args);
	mail_search_args_unref(&search_args);

	if (imap_fetch_more(ctx, cmd) == 0) {
		/* unfinished */
		cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;

		cmd->func = cmd_fetch_continue;
		cmd->context = ctx;
		return FALSE;
	}
	return cmd_fetch_finish(ctx, cmd);
}
예제 #4
0
bool cmd_store(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	const struct imap_arg *args;
	struct mail_search_args *search_args;
	struct mail_search_context *search_ctx;
        struct mailbox_transaction_context *t;
	struct mail *mail;
	struct imap_store_context ctx;
	ARRAY_TYPE(seq_range) modified_set, uids;
	enum mailbox_transaction_flags flags = 0;
	enum imap_sync_flags imap_sync_flags = 0;
	const char *set, *reply, *tagged_reply;
	string_t *str;
	int ret;

	if (!client_read_args(cmd, 0, 0, &args))
		return FALSE;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	if (!imap_arg_get_atom(args, &set)) {
		client_send_command_error(cmd, "Invalid arguments.");
		return TRUE;
	}
	ret = imap_search_get_seqset(cmd, set, cmd->uid, &search_args);
	if (ret <= 0)
		return ret < 0;

	memset(&ctx, 0, sizeof(ctx));
	ctx.cmd = cmd;
	if (!store_parse_args(&ctx, ++args)) {
		mail_search_args_unref(&search_args);
		return TRUE;
	}

	if (client->mailbox_examined) {
		mail_search_args_unref(&search_args);
		if (ctx.max_modseq < (uint64_t)-1)
			reply = "NO CONDSTORE failed: Mailbox is read-only.";
		else
			reply = "OK Store ignored with read-only mailbox.";
		return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST |
				(cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
				0, reply);
	}

	if (ctx.silent)
		flags |= MAILBOX_TRANSACTION_FLAG_HIDE;
	if (ctx.max_modseq < (uint64_t)-1) {
		/* update modseqs so we can check them early */
		flags |= MAILBOX_TRANSACTION_FLAG_REFRESH;
	}

	t = mailbox_transaction_begin(client->mailbox, flags);
	search_ctx = mailbox_search_init(t, search_args, NULL,
					 MAIL_FETCH_FLAGS, NULL);
	mail_search_args_unref(&search_args);

	i_array_init(&modified_set, 64);
	if (ctx.max_modseq < (uint32_t)-1) {
		/* STORE UNCHANGEDSINCE is being used */
		mailbox_transaction_set_max_modseq(t, ctx.max_modseq,
						   &modified_set);
	}

	while (mailbox_search_next(search_ctx, &mail)) {
		if (ctx.max_modseq < (uint64_t)-1) {
			/* check early so there's less work for transaction
			   commit if something has to be cancelled */
			if (mail_get_modseq(mail) > ctx.max_modseq) {
				seq_range_array_add(&modified_set, mail->seq);
				continue;
			}
		}
		if (ctx.modify_type == MODIFY_REPLACE || ctx.flags != 0)
			mail_update_flags(mail, ctx.modify_type, ctx.flags);
		if (ctx.modify_type == MODIFY_REPLACE || ctx.keywords != NULL) {
			mail_update_keywords(mail, ctx.modify_type,
					     ctx.keywords);
		}
	}

	if (ctx.keywords != NULL)
		mailbox_keywords_unref(&ctx.keywords);

	ret = mailbox_search_deinit(&search_ctx);
	if (ret < 0)
		mailbox_transaction_rollback(&t);
	 else
		ret = mailbox_transaction_commit(&t);
	if (ret < 0) {
		array_free(&modified_set);
		client_send_box_error(cmd, client->mailbox);
		return TRUE;
	}

	if (array_count(&modified_set) == 0)
		tagged_reply = "OK Store completed.";
	else {
		if (cmd->uid) {
			i_array_init(&uids, array_count(&modified_set)*2);
			mailbox_get_uid_range(client->mailbox, &modified_set,
					      &uids);
			array_free(&modified_set);
			modified_set = uids;
		}
		str = str_new(cmd->pool, 256);
		str_append(str, "OK [MODIFIED ");
		imap_write_seq_range(str, &modified_set);
		str_append(str, "] Conditional store failed.");
		tagged_reply = str_c(str);
	}
	array_free(&modified_set);

	/* With UID STORE we have to return UID for the flags as well.
	   Unfortunately we don't have the ability to separate those
	   flag changes that were caused by UID STORE and those that
	   came externally, so we'll just send the UID for all flag
	   changes that we see. */
	if (cmd->uid && (!ctx.silent || (client->enabled_features &
					 MAILBOX_FEATURE_CONDSTORE) != 0))
		imap_sync_flags |= IMAP_SYNC_FLAG_SEND_UID;

	return cmd_sync(cmd, (cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
			imap_sync_flags, tagged_reply);
}
static bool cmd_compress(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct zlib_client *zclient = IMAP_ZLIB_IMAP_CONTEXT(client);
	const struct compression_handler *handler;
	const struct imap_arg *args;
	struct istream *old_input;
	struct ostream *old_output;
	const char *mechanism, *value;
	unsigned int level;

	/* <mechanism> */
	if (!client_read_args(cmd, 0, 0, &args))
		return FALSE;

	if (!imap_arg_get_atom(args, &mechanism) ||
	    !IMAP_ARG_IS_EOL(&args[1])) {
		client_send_command_error(cmd, "Invalid arguments.");
		return TRUE;
	}
	if (zclient->handler != NULL) {
		client_send_tagline(cmd, t_strdup_printf(
			"NO [COMPRESSIONACTIVE] COMPRESSION=%s already enabled.",
			t_str_ucase(zclient->handler->name)));
		return TRUE;
	}
	if (client->tls_compression) {
		client_send_tagline(cmd,
			"NO [COMPRESSIONACTIVE] TLS compression already enabled.");
		return TRUE;
	}

	handler = compression_lookup_handler(t_str_lcase(mechanism));
	if (handler == NULL || handler->create_istream == NULL) {
		client_send_tagline(cmd, "NO Unknown compression mechanism.");
		return TRUE;
	}

	imap_zlib_client_skip_line(client);
	client_send_tagline(cmd, "OK Begin compression.");

	value = mail_user_plugin_getenv(client->user,
					"imap_zlib_compress_level");
	if (value == NULL || str_to_uint(value, &level) < 0 ||
	    level <= 0 || level > 9)
		level = IMAP_COMPRESS_DEFAULT_LEVEL;

	old_input = client->input;
	old_output = client->output;
	client->input = handler->create_istream(old_input, FALSE);
	client->output = handler->create_ostream(old_output, level);
	/* preserve output offset so that the bytes out counter in logout
	   message doesn't get reset here */
	client->output->offset = old_output->offset;
	i_stream_unref(&old_input);
	o_stream_unref(&old_output);

	client_update_imap_parser_streams(client);
	zclient->handler = handler;
	return TRUE;
}
static bool cmd_x_apple_push_service(struct client_command_context *cmd)
{
	const struct imap_arg *args;
	
	struct mail_user *user = cmd->client->user;
	const char *aps_topic = mail_user_plugin_getenv(user, "push_notify_aps_topic");

	if (!client_read_args(cmd, 0, 0, &args))
		return FALSE;

	const char *aps_ver=NULL;
	const char *aps_acct_id=NULL;
	const char *aps_dev_token=NULL;
	const char *aps_sub_topic=NULL;
	const struct imap_arg *mailbox_list=NULL;
	unsigned int mailbox_count=NULL;
	bool mailbox_subscriptions=FALSE;

	const char *key, *value;

	/* must have a topic */
	if (aps_topic == NULL || *aps_topic == '\0')
		return FALSE;

	/* scarf off the aps keys/values */
	while (imap_arg_get_astring(&args[0], &key)) {
		if (imap_arg_get_astring(&args[1], &value)) {
			if (strcasecmp(key, "aps-version") == 0)
				aps_ver = t_strdup(value);
			else if (strcasecmp(key, "aps-account-id") == 0)
				aps_acct_id = t_strdup(value);
			else if (strcasecmp(key, "aps-device-token") == 0)
				aps_dev_token = t_strdup(value);
			else if (strcasecmp(key, "aps-subtopic") == 0)
				aps_sub_topic = t_strdup(value);
			else 
				return FALSE;
		}
		else if (strcasecmp(key, "mailboxes") == 0) {
			mailbox_subscriptions = imap_arg_get_list_full(&args[1], &mailbox_list, &mailbox_count);
		}
		args += 2;
	}

	/* save notification settings */
	if ( aps_ver && aps_acct_id && aps_dev_token && aps_sub_topic ) {
		msg_data_t msg_data;
		
		/* subscribe to notification node */
		memset(&msg_data, 0, sizeof(struct msg_data_s));
		msg_data.msg = 2;
		i_strocpy(msg_data.d1, user->username, sizeof(msg_data.d1));
		i_strocpy(msg_data.d2, aps_acct_id, sizeof(msg_data.d2));
		i_strocpy(msg_data.d3, aps_dev_token, sizeof(msg_data.d3));
		i_strocpy(msg_data.d4, aps_sub_topic, sizeof(msg_data.d4));
		send_msg_data(&msg_data);

		if(mailbox_subscriptions) {
			const char *mailbox;
			while (imap_arg_get_astring(&mailbox_list[0], &mailbox)) {
				T_BEGIN;
				string_t *mailbox_str = t_str_new(256);
				imap_append_quoted( mailbox_str, "mailbox");
				str_append_c( mailbox_str, ' ');
				imap_append_quoted( mailbox_str, mailbox);
				send_response(cmd->client, mailbox_str);

				msg_data.msg = 4;
				i_strocpy(msg_data.d4, mailbox, sizeof(msg_data.d4));
				send_msg_data(&msg_data);
				T_END;
				mailbox_list += 1;
			}
		}

		/* generate aps response */
		string_t *str = t_str_new(256);
		imap_append_quoted( str, "aps-version" );
		str_append_c(str, ' ');
		imap_append_quoted( str, APS_VERSION );
		str_append_c(str, ' ');
		imap_append_quoted( str, "aps-topic" );
		str_append_c(str, ' ');
		imap_append_quoted( str, aps_topic );
		send_response(cmd->client, str);
	}

	client_send_tagline(cmd, "OK Provisioned");

	return TRUE;
}
예제 #7
0
static bool cmd_xapplepushservice(struct client_command_context *cmd)
{
  /*
   * Parse arguments. We expect four key value pairs. We only take
   * those that we understand for version 2 of this extension.
   *
   * TODO: We are ignoring the mailboxes parameter for now and just
   * default to INBOX always.
   */

  const struct imap_arg *args;
  const char *arg_key, *arg_val;
  const char *aps_version = NULL, *aps_account_id = NULL, *aps_device_token = NULL, *aps_subtopic = NULL;

  if (!client_read_args(cmd, 0, 0, &args)) {
    client_send_command_error(cmd, "Invalid arguments.");
    return FALSE;
  }

  for (int i = 0; i < 4; i++) {
    if (!imap_arg_get_astring(&args[i*2+0], &arg_key)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }

    if (!imap_arg_get_astring(&args[i*2+1], &arg_val)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }

    if (strcasecmp(arg_key, "aps-version") == 0) {
      aps_version = arg_val;
    } else if (strcasecmp(arg_key, "aps-account-id") == 0) {
      aps_account_id = arg_val;
    } else if (strcasecmp(arg_key, "aps-device-token") == 0) {
      aps_device_token = arg_val;
    } else if (strcasecmp(arg_key, "aps-subtopic") == 0) {
      aps_subtopic = arg_val;
    }
  }

  /*
   * Check if this is a version we expect
   */

  if (!aps_version || (strcmp(aps_version, "1") != 0 && strcmp(aps_version, "2") != 0)) {
    client_send_command_error(cmd, "Unknown aps-version.");
    return FALSE;
  }

  /*
   * If this is version 2 then also need to grab the mailboxes, which
   * is a list of mailbox names.
   */

  const struct imap_arg *mailboxes = NULL;

  if (strcmp(aps_version, "2") == 0) {
    if (!imap_arg_get_astring(&args[8], &arg_key)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }

    if (strcmp(arg_key, "mailboxes") != 0) {
      client_send_command_error(cmd, "Invalid arguments. (Expected mailboxes)");
      return FALSE;
    }

    if (!imap_arg_get_list(&args[9], &mailboxes)) {
      client_send_command_error(cmd, "Invalid arguments.");
      return FALSE;
    }
  }

  /*
   * Check if all of the parameters are there.
   */

  if (!aps_account_id || strlen(aps_account_id) == 0) {
    client_send_command_error(cmd, "Incomplete or empty aps-account-id parameter.");
    return FALSE;
  }

  if (!aps_device_token || strlen(aps_device_token) == 0) {
    client_send_command_error(cmd, "Incomplete or empty aps-device-token parameter.");
    return FALSE;
  }

  if (!aps_subtopic || strlen(aps_subtopic) == 0) {
    client_send_command_error(cmd, "Incomplete or empty aps-subtopic parameter.");
    return FALSE;
  }

  /*
   * Forward to the helper daemon. The helper will return the
   * aps-topic, which in reality is the subject of the certificate. I
   * think it is only used to make sure that the binding between the
   * client and the APS server and the IMAP server stays current.
   */

  struct client *client = cmd->client;
  struct mail_user *user = client->user;

  const char *aps_topic = mail_user_plugin_getenv(user, "xaps_topic");

  if (xaps_register(aps_account_id, aps_device_token, aps_subtopic, user->username, mailboxes) != 0) {
    client_send_command_error(cmd, "Registration failed.");
i_info("Registration failed.");
    return FALSE;
  }

  /*
   * Return success. We assume that aps_version and aps_topic do not
   * contain anything that needs to be escaped.
   */
  client_send_line(cmd->client,
    t_strdup_printf("* XAPPLEPUSHSERVICE aps-version \"%s\" aps-topic \"%s\"", aps_version, aps_topic));
  client_send_tagline(cmd, "OK XAPPLEPUSHSERVICE Registration successful.");


  return TRUE;
}