imap-commands-util.c revision 24e8feedfc223fcdddb2c9eb4605cbd98afce449
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher#include "imap-common.h"
33396dc46ea52c18f47db1b5d590880806521005Sumit Bose#include "array.h"
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher#include "buffer.h"
33396dc46ea52c18f47db1b5d590880806521005Sumit Bose#include "str.h"
703dc1eb5b050b24235a6640f271d34ea008cf98Jan Engelhardt#include "str-sanitize.h"
703dc1eb5b050b24235a6640f271d34ea008cf98Jan Engelhardt#include "imap-resp-code.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "imap-parser.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "imap-sync.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "imap-util.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "mail-storage.h"
324fb26ba803a999bedc29e93c46c84f27abf5b7Sumit Bose#include "mail-namespace.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#include "imap-commands-util.h"
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher/* Maximum length for mailbox name, including it's path. This isn't fully
d71cd46ede9c57e3947c86c61306a9caddce242cLukas Slebodnik exact since the user can create folder hierarchy with small names, then
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher rename them to larger names. Mail storages should set more strict limits
002f84aea86371aa079b867c0ec39396b97109d3Lukas Slebodnik to them, mbox/maildir currently allow paths only up to PATH_MAX. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher#define MAILBOX_MAX_NAME_LEN 512
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstruct mail_namespace *
87d3b47abba6a40fcf809c85a2b138bc1013d9c5Jakub Hrozekclient_find_namespace(struct client_command_context *cmd, const char *mailbox,
87d3b47abba6a40fcf809c85a2b138bc1013d9c5Jakub Hrozek const char **storage_name_r,
deeadf40db3a1eec64cf030e54afc4cb8612a8d5Lukas Slebodnik enum mailbox_name_status *mailbox_status_r)
ccf340e56364851f2e5b75e52d3d63701b662954Lukas Slebodnik{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_namespace *namespaces = cmd->client->user->namespaces;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_namespace *ns;
f0ea3ed816182fadf77f3e7f7ddb298b287007adLukas Slebodnik const char *storage_name, *p;
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher unsigned int storage_name_len;
cc98edd9479d4622634a1275c98058916c14059aStephen Gallagher
ee359fe1384507fed6c2274e7bfe81d288de4542Stephen Gallagher storage_name = mailbox;
cc98edd9479d4622634a1275c98058916c14059aStephen Gallagher ns = mail_namespace_find(namespaces, &storage_name);
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina if (ns == NULL) {
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina client_send_tagline(cmd, t_strdup_printf(
d3da1c165cdb4c1ec126a8f4b6b544ca415b9d20Pavel Březina "NO Client tried to access nonexistent namespace. "
c481179da5d5b53ce16d8784c0bd2857ffc2f061Lukas Slebodnik "(Mailbox name should probably be prefixed with: %s)",
1183d29d87c5c7439cf2364b7d7324d4a13b6e35Stephen Gallagher mail_namespace_find_inbox(namespaces)->prefix));
002f84aea86371aa079b867c0ec39396b97109d3Lukas Slebodnik return NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (mailbox_status_r == NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *storage_name_r = storage_name;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return ns;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* make sure it even looks valid */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (*storage_name == '\0' && !(*mailbox != '\0' && ns->list)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_send_tagline(cmd, "NO Empty mailbox name.");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return NULL;
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce }
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce
f775337a7d4ca1c0be8eab683d0d753cbaee49e2Lukas Slebodnik storage_name_len = strlen(storage_name);
f775337a7d4ca1c0be8eab683d0d753cbaee49e2Lukas Slebodnik if ((cmd->client->set->parsed_workarounds &
f775337a7d4ca1c0be8eab683d0d753cbaee49e2Lukas Slebodnik WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek storage_name_len > 0 &&
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek storage_name[storage_name_len-1] == ns->real_sep) {
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek /* drop the extra trailing hierarchy separator */
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce storage_name = t_strndup(storage_name, storage_name_len-1);
c89589fa349f38214c9cb8d9389c0fd557e5dca2Simo Sorce }
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek if (strlen(mailbox) == ns->prefix_len) {
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek /* trying to open "ns prefix/" */
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek client_send_tagline(cmd, "NO Invalid mailbox name.");
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek return NULL;
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek }
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek if (ns->real_sep != ns->sep && ns->prefix_len < strlen(mailbox)) {
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek /* make sure there are no real separators used in the mailbox
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek name. */
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek mailbox += ns->prefix_len;
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek for (p = mailbox; *p != '\0'; p++) {
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek if (*p == ns->real_sep) {
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek client_send_tagline(cmd, t_strdup_printf(
48130eef6c5c64a07094b9e8582ba358b2048f24Jakub Hrozek "NO Character not allowed "
1658c567191c35beaddffafdb079abe33248037bLukas Slebodnik "in mailbox name: '%c'",
1658c567191c35beaddffafdb079abe33248037bLukas Slebodnik ns->real_sep));
1658c567191c35beaddffafdb079abe33248037bLukas Slebodnik return NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* make sure two hierarchy separators aren't next to each others */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (p = storage_name+1; *p != '\0'; p++) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (p[0] == ns->real_sep && p[-1] == ns->real_sep) {
d921c1eba437662437847279f251a0a5d8f70127Maxim client_send_tagline(cmd, "NO Invalid mailbox name.");
d921c1eba437662437847279f251a0a5d8f70127Maxim return NULL;
d921c1eba437662437847279f251a0a5d8f70127Maxim }
d921c1eba437662437847279f251a0a5d8f70127Maxim }
d921c1eba437662437847279f251a0a5d8f70127Maxim
d921c1eba437662437847279f251a0a5d8f70127Maxim if (storage_name_len > MAILBOX_MAX_NAME_LEN) {
d921c1eba437662437847279f251a0a5d8f70127Maxim client_send_tagline(cmd, "NO Mailbox name too long.");
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer return NULL;
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer }
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer /* check what our storage thinks of it */
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer if (mailbox_list_get_mailbox_name_status(ns->list, storage_name,
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer mailbox_status_r) < 0) {
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer client_send_list_error(cmd, ns->list);
327127bb7fcc07f882209f029e14026de1b23c94Maxim return NULL;
327127bb7fcc07f882209f029e14026de1b23c94Maxim }
327127bb7fcc07f882209f029e14026de1b23c94Maxim *storage_name_r = storage_name;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return ns;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid client_fail_mailbox_name_status(struct client_command_context *cmd,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *mailbox_name,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *resp_code,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher enum mailbox_name_status status)
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce{
bc9235cfb80bd64a3bfa959e8d26d5ad1be0bdf4Jakub Hrozek switch (status) {
bc9235cfb80bd64a3bfa959e8d26d5ad1be0bdf4Jakub Hrozek case MAILBOX_NAME_EXISTS_MAILBOX:
07d82f79d2970a08628ebf71343441ec55faa6faPavel Březina case MAILBOX_NAME_EXISTS_DIR:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_send_tagline(cmd, t_strconcat(
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "NO [", IMAP_RESP_CODE_ALREADYEXISTS,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "] Mailbox already exists: ",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher NULL));
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagher break;
90fd1bbd6035cdab46faa3a695a2fb2be6508b17Sumit Bose case MAILBOX_NAME_VALID:
03713859dffacc7142393e53c73d8d4cf7dee8d5Pavel Březina if (resp_code == NULL)
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik resp_code = "";
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik else
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke resp_code = t_strconcat("[", resp_code, "] ", NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_send_tagline(cmd, t_strconcat(
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "NO ", resp_code, "Mailbox doesn't exist: ",
64ea4127f463798410a2c20e0261c6b15f60257fJakub Hrozek str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher NULL));
772464c842968d6e544118ae1aa7c49a7cda2ad6Stephen Gallagher break;
32381402a4a9afc003782c9e2301fc59c9bda2a9Yassir Elley case MAILBOX_NAME_INVALID:
068dbee9ca7bf5b37330eff91c94ae10f288d09fJakub Hrozek client_send_tagline(cmd, t_strconcat(
98ce3c3e85a4bb2e1822bf8ab2a1c2ab9e3dd61dJakub Hrozek "NO Invalid mailbox name: ",
be65f065fef1d387281096ef095a2acef39ecc12Jakub Hrozek str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
e124844907ed6973915e4d56f5442ecd07535a12Jakub Hrozek NULL));
f36078af138f052cd9a30360867b0ebd0805af5eJakub Hrozek break;
34c78b745eb349eef2b0f13ef2b722632aebe619Jan Cholasta case MAILBOX_NAME_NOINFERIORS:
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek client_send_tagline(cmd,
e07a94a66985b674c5df11ca466792902164c4e2George McCollister "NO Parent mailbox doesn't allow child mailboxes.");
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek break;
a2e417f38c57ed87c956ddcecf4dafca93842b65Lukas Slebodnik }
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer}
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer
a9c287bda3fc2a1e12cef2135ade96945f11ad01Sumit Bosebool client_verify_open_mailbox(struct client_command_context *cmd)
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (cmd->client->mailbox != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher else {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_send_tagline(cmd, "BAD No mailbox selected.");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherconst char *
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherimap_get_error_string(struct client_command_context *cmd,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *error_string, enum mail_error error)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *resp_code = NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher switch (error) {
a5077712fc8c24e8cad08207b7b5a6603bde6a7cJakub Hrozek case MAIL_ERROR_NONE:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher break;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher case MAIL_ERROR_TEMP:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher resp_code = IMAP_RESP_CODE_SERVERBUG;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher break;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher case MAIL_ERROR_NOTPOSSIBLE:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher case MAIL_ERROR_PARAMS:
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher resp_code = IMAP_RESP_CODE_CANNOT;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher break;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher case MAIL_ERROR_PERM:
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek resp_code = IMAP_RESP_CODE_NOPERM;
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bose break;
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke case MAIL_ERROR_NOSPACE:
6b0a7c72bb841d6885a620c68bd51d55109b66c7Jakub Hrozek resp_code = IMAP_RESP_CODE_OVERQUOTA;
9917c138d9a270deb5820915384fbde751190c2aLukas Slebodnik break;
0e1dcef53d9d8465ce97d31ad11be4445a6e7eb8Lukas Slebodnik case MAIL_ERROR_NOTFOUND:
c3889e5a101a075defe533d81f5296d5e680f639Lukas Slebodnik if ((cmd->cmd_flags & COMMAND_FLAG_USE_NONEXISTENT) != 0)
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina resp_code = IMAP_RESP_CODE_NONEXISTENT;
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer break;
40b2be4f4312470044cdef460b02b66003f5c85fJakub Hrozek case MAIL_ERROR_EXISTS:
bf54fbed126ec3d459af40ea370ffadacd31c76dJakub Hrozek resp_code = IMAP_RESP_CODE_ALREADYEXISTS;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov break;
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina case MAIL_ERROR_EXPUNGED:
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina resp_code = IMAP_RESP_CODE_EXPUNGEISSUED;
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina break;
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina case MAIL_ERROR_INUSE:
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher resp_code = IMAP_RESP_CODE_INUSE;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher break;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher }
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik if (resp_code == NULL || *error_string == '[')
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik return t_strconcat("NO ", error_string, NULL);
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik else
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher return t_strdup_printf("NO [%s] %s", resp_code, error_string);
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik}
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnikvoid client_send_list_error(struct client_command_context *cmd,
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher struct mailbox_list *list)
9dbdf62243f01f6aee41c2b5f2976c56da47f25dLukas Slebodnik{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *error_string;
539b1be3507abdf8ac235b06eeed5011b0b5cde2Ondrej Kos enum mail_error error;
539b1be3507abdf8ac235b06eeed5011b0b5cde2Ondrej Kos
574a1c20f114851071ae74112b34488c3d1aeeb3Ondrej Kos error_string = mailbox_list_get_last_error(list, &error);
574a1c20f114851071ae74112b34488c3d1aeeb3Ondrej Kos client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
574a1c20f114851071ae74112b34488c3d1aeeb3Ondrej Kos error));
574a1c20f114851071ae74112b34488c3d1aeeb3Ondrej Kos}
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozekvoid client_send_storage_error(struct client_command_context *cmd,
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek struct mail_storage *storage)
9542512d7be40f2000298c86d3d2b728f4f0f65aStephen Gallagher{
e6e26182d58c05d896f72f2925426658a6dc70b5Jakub Hrozek const char *error_string;
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek enum mail_error error;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (cmd->client->mailbox != NULL &&
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mailbox_is_inconsistent(cmd->client->mailbox)) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* we can't do forced CLOSE, so have to disconnect */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_disconnect_with_error(cmd->client,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "IMAP session state is inconsistent, please relogin.");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher error_string = mail_storage_get_last_error(storage, &error);
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek error));
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid client_send_untagged_storage_error(struct client *client,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct mail_storage *storage)
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek{
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek const char *error_string;
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek enum mail_error error;
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek if (client->mailbox != NULL &&
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mailbox_is_inconsistent(client->mailbox)) {
6b01dae732eedee808f32a9cdd4b5656a9f839c4Jakub Hrozek /* we can't do forced CLOSE, so have to disconnect */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_disconnect_with_error(client,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "IMAP session state is inconsistent, please relogin.");
b50dffea929ee5cd0c59ba3c4822337cc162ff92Kamil Dudka return;
b50dffea929ee5cd0c59ba3c4822337cc162ff92Kamil Dudka }
b50dffea929ee5cd0c59ba3c4822337cc162ff92Kamil Dudka
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher error_string = mail_storage_get_last_error(storage, &error);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_send_line(client, t_strconcat("* NO ", error_string, NULL));
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek}
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherbool client_parse_mail_flags(struct client_command_context *cmd,
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek const struct imap_arg *args,
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek enum mail_flags *flags_r,
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek const char *const **keywords_r)
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek{
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek const char *atom;
d9378e64499642e86989158f274372187314d5b2Lukas Slebodnik enum mail_flags flag;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek ARRAY_DEFINE(keywords, const char *);
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek *flags_r = 0;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek *keywords_r = NULL;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek p_array_init(&keywords, cmd->pool, 16);
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek while (!IMAP_ARG_IS_EOL(args)) {
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek if (!imap_arg_get_atom(args, &atom)) {
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek client_send_command_error(cmd,
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek "Flags list contains non-atoms.");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
4d81fe27ced3d2e96866aeaf61661a925cb8edf1Jakub Hrozek }
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher
f5b6f977d4144c28e9c66f3f1c9d634d595d1117Marko Myllynen if (*atom == '\\') {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* system flag */
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik atom = t_str_ucase(atom);
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik flag = imap_parse_system_flag(atom);
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik if (flag != 0 && flag != MAIL_RECENT)
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik *flags_r |= flag;
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik else {
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik client_send_tagline(cmd, t_strconcat(
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik "BAD Invalid system flag ",
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik atom, NULL));
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik return FALSE;
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik }
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik } else {
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik /* keyword validity checks are done by lib-storage */
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik array_append(&keywords, &atom, 1);
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik }
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik args++;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik }
dc4c30bae512c0b45ff925d9e998337f8fe97e94Lukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (array_count(&keywords) == 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher *keywords_r = NULL;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik else {
72e60fd4eabcfbcdbfe01e8c38b94052bc6c2067Jakub Hrozek (void)array_append_space(&keywords); /* NULL-terminate */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik *keywords_r = array_idx(&keywords, 0);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik }
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik return TRUE;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik}
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnikstatic const char *get_keywords_string(const ARRAY_TYPE(keywords) *keywords)
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik{
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik string_t *str;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik const char *const *names;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik str = t_str_new(256);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher array_foreach(keywords, names) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *name = *names;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik str_append_c(str, ' ');
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik str_append(str, name);
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik }
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik return str_c(str);
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik}
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik
827dd342494de18099dddd0272c1a85f10703556Lukas Slebodnik#define SYSTEM_FLAGS "\\Answered \\Flagged \\Deleted \\Seen \\Draft"
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnikvoid client_send_mailbox_flags(struct client *client, bool selecting)
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik{
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik unsigned int count = array_count(client->keywords.names);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik const char *str;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (!selecting && count == client->keywords.announce_count) {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* no changes to keywords and we're not selecting a mailbox */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik return;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik }
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik client->keywords.announce_count = count;
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik str = count == 0 ? "" : get_keywords_string(client->keywords.names);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik client_send_line(client,
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik t_strconcat("* FLAGS ("SYSTEM_FLAGS, str, ")", NULL));
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (mailbox_is_readonly(client->mailbox)) {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik client_send_line(client, "* OK [PERMANENTFLAGS ()] "
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik "Read-only mailbox.");
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik } else {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik bool star = mailbox_allow_new_keywords(client->mailbox);
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik client_send_line(client,
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov t_strconcat("* OK [PERMANENTFLAGS ("SYSTEM_FLAGS, str,
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov star ? " \\*" : "",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ")] Flags permitted.", NULL));
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
3d038d2e0dc7af04ec2f7c85ae325accb39f6237Jakub Hrozek}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid client_update_mailbox_flags(struct client *client,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const ARRAY_TYPE(keywords) *keywords)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client->keywords.names = keywords;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client->keywords.announce_count = 0;
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek}
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozek
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozekconst char *const *
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozekclient_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek const ARRAY_TYPE(keyword_indexes) *src)
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek{
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek const unsigned int *kw_indexes;
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek const char *const *all_names;
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozek unsigned int all_count;
e07a94a66985b674c5df11ca466792902164c4e2George McCollister
e07a94a66985b674c5df11ca466792902164c4e2George McCollister client_send_mailbox_flags(client, FALSE);
e07a94a66985b674c5df11ca466792902164c4e2George McCollister
bf01e8179cbb2be476805340636098deda7e1366Sumit Bose /* convert indexes to names */
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose all_names = array_get(client->keywords.names, &all_count);
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose array_clear(dest);
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose array_foreach(src, kw_indexes) {
172c07013d1ea99447a780fd36f49d5c3a76981bJakub Hrozek unsigned int kw_index = *kw_indexes;
9917c138d9a270deb5820915384fbde751190c2aLukas Slebodnik
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(kw_index < all_count);
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce array_append(dest, &all_names[kw_index], 1);
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce }
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce (void)array_append_space(dest);
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce return array_idx(dest, 0);
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce}
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce
336879aabae137f9a81304f147fb0d43001654b0Simo Sorcebool mailbox_equals(const struct mailbox *box1,
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce const struct mail_namespace *ns2, const char *name2)
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce{
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce struct mail_namespace *ns1 = mailbox_get_namespace(box1);
336879aabae137f9a81304f147fb0d43001654b0Simo Sorce const char *name1;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if (ns1 != ns2)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik return FALSE;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik name1 = mailbox_get_name(box1);
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik if (strcmp(name1, name2) == 0)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik return TRUE;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik return strcasecmp(name1, "INBOX") == 0 &&
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik strcasecmp(name2, "INBOX") == 0;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik}
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnikvoid msgset_generator_init(struct msgset_generator_context *ctx, string_t *str)
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik{
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik memset(ctx, 0, sizeof(*ctx));
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik ctx->str = str;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik ctx->last_uid = (uint32_t)-1;
aac071824f6c98003f30d49ab440c15b4b53692cLukas Slebodnik}
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnikvoid msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid)
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik{
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik if (uid != ctx->last_uid+1) {
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik if (ctx->first_uid == 0)
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik ;
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik else if (ctx->first_uid == ctx->last_uid)
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik str_printfa(ctx->str, "%u,", ctx->first_uid);
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik else {
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik str_printfa(ctx->str, "%u:%u,",
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik ctx->first_uid, ctx->last_uid);
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik }
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik ctx->first_uid = uid;
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik }
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik ctx->last_uid = uid;
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik}
356eef72675cde4dc5627c1e2f1a01846ec6eb1dLukas Slebodnik
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid msgset_generator_finish(struct msgset_generator_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ctx->first_uid == ctx->last_uid)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_printfa(ctx->str, "%u", ctx->first_uid);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher else
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_printfa(ctx->str, "%u:%u", ctx->first_uid, ctx->last_uid);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher