1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2010-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <mailutils/cctype.h>
25 #include <mailutils/cstr.h>
26 #include <mailutils/stream.h>
27 #include <mailutils/errno.h>
28 #include <mailutils/sys/imap.h>
29
30 static void
_imap_list_free(void * ptr)31 _imap_list_free (void *ptr)
32 {
33 struct imap_list_element *elt = ptr;
34
35 switch (elt->type)
36 {
37 case imap_eltype_string:
38 free (elt->v.string);
39 break;
40
41 case imap_eltype_list:
42 mu_list_destroy (&elt->v.list);
43 }
44 free (ptr);
45 }
46
47 static int
_mu_imap_response_list_create(mu_imap_t imap,mu_list_t * plist)48 _mu_imap_response_list_create (mu_imap_t imap, mu_list_t *plist)
49 {
50 mu_list_t list;
51 int status = mu_list_create (&list);
52 MU_IMAP_CHECK_ERROR (imap, status);
53 mu_list_set_destroy_item (list, _imap_list_free);
54 *plist = list;
55 return 0;
56 }
57
58 #define IS_LBRACE(p) ((p)[0] == '(' && !(p)[1])
59 #define IS_RBRACE(p) ((p)[0] == ')' && !(p)[1])
60 #define IS_NIL(p) (strcmp (p, "NIL") == 0)
61
62 static struct imap_list_element *
_new_imap_list_element(mu_imap_t imap,enum imap_eltype type)63 _new_imap_list_element (mu_imap_t imap, enum imap_eltype type)
64 {
65 struct imap_list_element *elt = calloc (1, sizeof (*elt));
66 if (!elt)
67 {
68 imap->client_state = MU_IMAP_CLIENT_ERROR;
69 }
70 else
71 elt->type = type;
72 return elt;
73 }
74
75 struct parsebuf
76 {
77 mu_imap_t pb_imap;
78 size_t pb_count;
79 char **pb_arr;
80 int pb_err;
81 int pb_inlist;
82 };
83
84 static void
parsebuf_init(struct parsebuf * pb,mu_imap_t imap)85 parsebuf_init (struct parsebuf *pb, mu_imap_t imap)
86 {
87 memset (pb, 0, sizeof *pb);
88 pb->pb_imap = imap;
89 }
90
91 static int
parsebuf_advance(struct parsebuf * pb)92 parsebuf_advance (struct parsebuf *pb)
93 {
94 if (pb->pb_count == 0)
95 return MU_ERR_NOENT;
96 pb->pb_count--;
97 pb->pb_arr++;
98 return 0;
99 }
100
101 static char *
parsebuf_gettok(struct parsebuf * pb)102 parsebuf_gettok (struct parsebuf *pb)
103 {
104 char *p;
105
106 if (pb->pb_count == 0)
107 return NULL;
108 p = *pb->pb_arr;
109 parsebuf_advance (pb);
110 return p;
111 }
112
113 static char *
parsebuf_peek(struct parsebuf * pb)114 parsebuf_peek (struct parsebuf *pb)
115 {
116 if (pb->pb_count == 0)
117 return NULL;
118 return *pb->pb_arr;
119 }
120
121 static void
parsebuf_seterr(struct parsebuf * pb,int err)122 parsebuf_seterr (struct parsebuf *pb, int err)
123 {
124 pb->pb_err = err;
125 }
126
127 static struct imap_list_element *_parse_element (struct parsebuf *pb);
128
129 static struct imap_list_element *
_parse_list(struct parsebuf * pb)130 _parse_list (struct parsebuf *pb)
131 {
132 int rc;
133 struct imap_list_element *elt, *list_elt;
134
135 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_list);
136 if (!elt)
137 {
138 parsebuf_seterr (pb, ENOMEM);
139 return NULL;
140 }
141
142 rc = _mu_imap_response_list_create (pb->pb_imap, &elt->v.list);
143 if (rc)
144 {
145 free (elt);
146 parsebuf_seterr (pb, rc);
147 return NULL;
148 }
149
150 while ((list_elt = _parse_element (pb)))
151 mu_list_append (elt->v.list, list_elt);
152
153 return elt;
154 }
155
156 static struct imap_list_element *
_parse_element(struct parsebuf * pb)157 _parse_element (struct parsebuf *pb)
158 {
159 struct imap_list_element *elt;
160 char *tok;
161
162 if (pb->pb_err)
163 return NULL;
164
165 tok = parsebuf_gettok (pb);
166
167 if (!tok)
168 {
169 if (pb->pb_inlist)
170 parsebuf_seterr (pb, MU_ERR_PARSE);
171 return NULL;
172 }
173
174 if (IS_LBRACE (tok))
175 {
176 tok = parsebuf_peek (pb);
177 if (!tok)
178 {
179 parsebuf_seterr (pb, MU_ERR_PARSE);
180 return NULL;
181 }
182
183 if (IS_RBRACE (tok))
184 {
185 parsebuf_gettok (pb);
186 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_list);
187 if (!elt)
188 {
189 parsebuf_seterr (pb, ENOMEM);
190 return NULL;
191 }
192 elt->v.list = NULL;
193 }
194 else
195 {
196 pb->pb_inlist++;
197 elt = _parse_list (pb);
198 }
199 }
200 else if (IS_RBRACE (tok))
201 {
202 if (pb->pb_inlist)
203 pb->pb_inlist--;
204 else
205 parsebuf_seterr (pb, MU_ERR_PARSE);
206 return NULL;
207 }
208 else if (IS_NIL (tok))
209 {
210 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_list);
211 if (!elt)
212 {
213 parsebuf_seterr (pb, ENOMEM);
214 return NULL;
215 }
216 elt->v.list = NULL;
217 }
218 else
219 {
220 char *s;
221 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_string);
222 if (!elt)
223 {
224 parsebuf_seterr (pb, ENOMEM);
225 return NULL;
226 }
227 s = strdup (tok);
228 if (!s)
229 {
230 free (elt);
231 parsebuf_seterr (pb, ENOMEM);
232 return NULL;
233 }
234 elt->v.string = s;
235 }
236 return elt;
237 }
238
239 int
_mu_imap_untagged_response_to_list(mu_imap_t imap,mu_list_t * plist)240 _mu_imap_untagged_response_to_list (mu_imap_t imap, mu_list_t *plist)
241 {
242 struct imap_list_element *elt;
243 struct parsebuf pb;
244
245 parsebuf_init (&pb, imap);
246 mu_imapio_get_words (imap->io, &pb.pb_count, &pb.pb_arr);
247 parsebuf_advance (&pb); /* Skip initial '*' */
248 elt = _parse_list (&pb);
249 if (pb.pb_err)
250 {
251 if (elt)
252 _imap_list_free (elt);
253 imap->client_state = MU_IMAP_CLIENT_ERROR;
254 return pb.pb_err;
255 }
256 *plist = elt->v.list;
257 free (elt);
258 return 0;
259 }
260
261 int
_mu_imap_list_element_is_string(struct imap_list_element * elt,const char * str)262 _mu_imap_list_element_is_string (struct imap_list_element *elt,
263 const char *str)
264 {
265 if (elt->type != imap_eltype_string)
266 return 0;
267 return strcmp (elt->v.string, str) == 0;
268 }
269
270 int
_mu_imap_list_element_is_nil(struct imap_list_element * elt)271 _mu_imap_list_element_is_nil (struct imap_list_element *elt)
272 {
273 return elt->type == imap_eltype_list && mu_list_is_empty (elt->v.list);
274 }
275
276 struct imap_list_element *
_mu_imap_list_at(mu_list_t list,int idx)277 _mu_imap_list_at (mu_list_t list, int idx)
278 {
279 struct imap_list_element *arg;
280 int rc = mu_list_get (list, idx, (void*) &arg);
281 if (rc)
282 {
283 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
284 ("cannot get list element: %s", mu_strerror (rc)));
285 return NULL;
286 }
287 return arg;
288 }
289
290 int
_mu_imap_list_nth_element_is_string(mu_list_t list,size_t n,const char * str)291 _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
292 const char *str)
293 {
294 struct imap_list_element *elt = _mu_imap_list_at (list, n);
295 return elt && elt->type == imap_eltype_string &&
296 strcmp (elt->v.string, str) == 0;
297 }
298
299 int
_mu_imap_list_nth_element_is_string_ci(mu_list_t list,size_t n,const char * str)300 _mu_imap_list_nth_element_is_string_ci (mu_list_t list, size_t n,
301 const char *str)
302 {
303 struct imap_list_element *elt = _mu_imap_list_at (list, n);
304 return elt && elt->type == imap_eltype_string &&
305 mu_c_strcasecmp (elt->v.string, str) == 0;
306 }
307
308
309