1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "imap-arg.h"
5 #include "mail-storage-private.h"
6 
7 static struct mail_keywords *
mailbox_keywords_create_skip(struct mailbox * box,const char * const keywords[])8 mailbox_keywords_create_skip(struct mailbox *box,
9 			     const char *const keywords[])
10 {
11 	struct mail_keywords *kw;
12 
13 	T_BEGIN {
14 		ARRAY(const char *) valid_keywords;
15 		const char *error;
16 
17 		t_array_init(&valid_keywords, 32);
18 		for (; *keywords != NULL; keywords++) {
19 			if (mailbox_keyword_is_valid(box, *keywords, &error))
20 				array_push_back(&valid_keywords, keywords);
21 		}
22 		array_append_zero(&valid_keywords); /* NULL-terminate */
23 		kw = mail_index_keywords_create(box->index, keywords);
24 	} T_END;
25 	return kw;
26 }
27 
28 static bool
mailbox_keywords_are_valid(struct mailbox * box,const char * const keywords[],const char ** error_r)29 mailbox_keywords_are_valid(struct mailbox *box, const char *const keywords[],
30 			   const char **error_r)
31 {
32 	unsigned int i;
33 
34 	for (i = 0; keywords[i] != NULL; i++) {
35 		if (!mailbox_keyword_is_valid(box, keywords[i], error_r))
36 			return FALSE;
37 	}
38 	return TRUE;
39 }
40 
mailbox_keywords_create(struct mailbox * box,const char * const keywords[],struct mail_keywords ** keywords_r)41 int mailbox_keywords_create(struct mailbox *box, const char *const keywords[],
42 			    struct mail_keywords **keywords_r)
43 {
44 	const char *error, *empty_keyword_list = NULL;
45 
46 	i_assert(box->opened);
47 
48 	if (keywords == NULL)
49 		keywords = &empty_keyword_list;
50 	if (!mailbox_keywords_are_valid(box, keywords, &error)) {
51 		mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, error);
52 		return -1;
53 	}
54 
55 	*keywords_r = mail_index_keywords_create(box->index, keywords);
56 	return 0;
57 }
58 
59 struct mail_keywords *
mailbox_keywords_create_valid(struct mailbox * box,const char * const keywords[])60 mailbox_keywords_create_valid(struct mailbox *box,
61 			      const char *const keywords[])
62 {
63 	const char *empty_keyword_list = NULL;
64 	const char *error;
65 
66 	i_assert(box->opened);
67 
68 	if (keywords == NULL)
69 		keywords = &empty_keyword_list;
70 	if (mailbox_keywords_are_valid(box, keywords, &error))
71 		return mail_index_keywords_create(box->index, keywords);
72 	else {
73 		/* found invalid keywords, do this the slow way */
74 		return mailbox_keywords_create_skip(box, keywords);
75 	}
76 }
77 
78 struct mail_keywords *
mailbox_keywords_create_from_indexes(struct mailbox * box,const ARRAY_TYPE (keyword_indexes)* idx)79 mailbox_keywords_create_from_indexes(struct mailbox *box,
80 				     const ARRAY_TYPE(keyword_indexes) *idx)
81 {
82 	i_assert(box->opened);
83 
84 	return mail_index_keywords_create_from_indexes(box->index, idx);
85 }
86 
mailbox_keywords_merge(struct mail_keywords * keywords1,struct mail_keywords * keywords2)87 struct mail_keywords *mailbox_keywords_merge(struct mail_keywords *keywords1,
88 					     struct mail_keywords *keywords2)
89 {
90 	ARRAY_TYPE(keyword_indexes) keywords_merged;
91 
92 	i_assert(keywords1->index == keywords2->index);
93 
94 	t_array_init(&keywords_merged, keywords1->count + keywords2->count);
95 	/* duplicates are dropped by mail_index_keywords_create() */
96 	array_append(&keywords_merged, keywords1->idx, keywords1->count);
97 	array_append(&keywords_merged, keywords2->idx, keywords2->count);
98 	return mail_index_keywords_create_from_indexes(keywords1->index,
99 						       &keywords_merged);
100 }
101 
mailbox_keywords_ref(struct mail_keywords * keywords)102 void mailbox_keywords_ref(struct mail_keywords *keywords)
103 {
104 	mail_index_keywords_ref(keywords);
105 }
106 
mailbox_keywords_unref(struct mail_keywords ** keywords)107 void mailbox_keywords_unref(struct mail_keywords **keywords)
108 {
109 	mail_index_keywords_unref(keywords);
110 }
111 
mailbox_keyword_is_valid(struct mailbox * box,const char * keyword,const char ** error_r)112 bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword,
113 			      const char **error_r)
114 {
115 	unsigned int i, idx;
116 
117 	i_assert(box->opened);
118 
119 	/* if it already exists, skip validity checks */
120 	if (mail_index_keyword_lookup(box->index, keyword, &idx))
121 		return TRUE;
122 
123 	if (*keyword == '\0') {
124 		*error_r = "Empty keywords not allowed";
125 		return FALSE;
126 	}
127 	if (box->disallow_new_keywords) {
128 		*error_r = "Can't create new keywords";
129 		return FALSE;
130 	}
131 
132 	/* these are IMAP-specific restrictions, but for now IMAP is all we
133 	   care about */
134 	for (i = 0; keyword[i] != '\0'; i++) {
135 		if (!IS_ATOM_CHAR(keyword[i])) {
136 			if ((unsigned char)keyword[i] < 0x80)
137 				*error_r = "Invalid characters in keyword";
138 			else
139 				*error_r = "8bit characters in keyword";
140 			return FALSE;
141 		}
142 	}
143 	if (i > box->storage->set->mail_max_keyword_length) {
144 		*error_r = "Keyword length too long";
145 		return FALSE;
146 	}
147 	return TRUE;
148 }
149