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 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <mailutils/errno.h>
25 #include <mailutils/assoc.h>
26 #include <mailutils/stream.h>
27 #include <mailutils/imap.h>
28 #include <mailutils/imaputil.h>
29 #include <mailutils/sys/imap.h>
30 
31 static int
_collect_flags(void * item,void * data)32 _collect_flags (void *item, void *data)
33 {
34   struct imap_list_element *elt = item;
35   int *args = data;
36 
37   if (elt->type == imap_eltype_string)
38     mu_imap_flag_to_attribute (elt->v.string, args);
39   return 0;
40 }
41 
42 int
_mu_imap_collect_flags(struct imap_list_element * arg,int * res)43 _mu_imap_collect_flags (struct imap_list_element *arg, int *res)
44 {
45   if (arg->type != imap_eltype_list)
46     return EINVAL;
47   *res = 0;
48   mu_list_foreach (arg->v.list, _collect_flags, res);
49   return 0;
50 }
51 
52 static void
_select_response_action(mu_imap_t imap,mu_list_t response,void * data)53 _select_response_action (mu_imap_t imap, mu_list_t response, void *data)
54 {
55   struct imap_list_element *elt;
56 
57   elt = _mu_imap_list_at (response, 0);
58   if (elt && _mu_imap_list_element_is_string (elt, "FLAGS"))
59     {
60       struct imap_list_element *arg = _mu_imap_list_at (response, 1);
61       if (arg &&
62 	  _mu_imap_collect_flags (arg, &imap->mbox_stat.defined_flags) == 0)
63 	imap->mbox_stat.flags |= MU_IMAP_STAT_DEFINED_FLAGS;
64     }
65 }
66 
67 int
mu_imap_select(mu_imap_t imap,const char * mbox,int writable,struct mu_imap_stat * ps)68 mu_imap_select (mu_imap_t imap, const char *mbox, int writable,
69 		struct mu_imap_stat *ps)
70 {
71   int status;
72 
73   if (imap == NULL)
74     return EINVAL;
75   if (!imap->io)
76     return MU_ERR_NO_TRANSPORT;
77   if (imap->session_state != MU_IMAP_SESSION_AUTH &&
78       imap->session_state != MU_IMAP_SESSION_SELECTED)
79     return MU_ERR_SEQ;
80 
81   if (!mbox)
82     {
83       if (imap->session_state == MU_IMAP_SESSION_SELECTED)
84 	{
85 	  if (ps)
86 	    *ps = imap->mbox_stat;
87 	  return 0;
88 	}
89       return MU_ERR_INFO_UNAVAILABLE;
90     }
91 
92   if (imap->mbox_name && strcmp (imap->mbox_name, mbox) == 0
93       && writable == imap->mbox_writable)
94     {
95       if (ps)
96 	*ps = imap->mbox_stat;
97       return 0;
98     }
99 
100   switch (imap->client_state)
101     {
102     case MU_IMAP_CLIENT_READY:
103       status = _mu_imap_tag_next (imap);
104       MU_IMAP_CHECK_EAGAIN (imap, status);
105       status = mu_imapio_send_command (imap->io, imap->tag_str, NULL,
106 				       writable ? "SELECT" : "EXAMINE",
107 				       mbox, NULL);
108       MU_IMAP_CHECK_ERROR (imap, status);
109       MU_IMAP_FCLR (imap, MU_IMAP_RESP);
110       imap->client_state = MU_IMAP_CLIENT_SELECT_RX;
111 
112     case MU_IMAP_CLIENT_SELECT_RX:
113       memset (&imap->mbox_stat, 0, sizeof (imap->mbox_stat));
114       status = _mu_imap_response (imap, _select_response_action, NULL);
115       MU_IMAP_CHECK_EAGAIN (imap, status);
116       switch (imap->response)
117 	{
118 	case MU_IMAP_OK:
119 	  imap->session_state = MU_IMAP_SESSION_SELECTED;
120 	  free (imap->mbox_name);
121 	  imap->mbox_name = strdup (mbox);
122 	  if (!imap->mbox_name)
123 	    {
124 	      imap->client_state = MU_IMAP_CLIENT_ERROR;
125 	      return errno;
126 	    }
127 	  imap->mbox_writable = writable;
128 	  if (ps)
129 	    *ps = imap->mbox_stat;
130 	  break;
131 
132 	case MU_IMAP_NO:
133 	  status = EACCES;
134 	  break;
135 
136 	case MU_IMAP_BAD:
137 	  status = MU_ERR_BADREPLY;
138 	  break;
139 	}
140       imap->client_state = MU_IMAP_CLIENT_READY;
141       break;
142 
143     default:
144       status = EINPROGRESS;
145     }
146   return status;
147 }
148