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