1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2011-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/sys/imap.h>
29 
30 #define STATUS_FLAG_MASK \
31   (MU_IMAP_STAT_MESSAGE_COUNT|			\
32    MU_IMAP_STAT_RECENT_COUNT|			\
33    MU_IMAP_STAT_UIDNEXT|			\
34    MU_IMAP_STAT_UIDVALIDITY|			\
35    MU_IMAP_STAT_FIRST_UNSEEN)
36 
37 struct mu_kwd _mu_imap_status_name_table[] = {
38   { "MESSAGES",    MU_IMAP_STAT_MESSAGE_COUNT },
39   { "RECENT",      MU_IMAP_STAT_RECENT_COUNT  },
40   { "UIDNEXT",     MU_IMAP_STAT_UIDNEXT       },
41   { "UIDVALIDITY", MU_IMAP_STAT_UIDVALIDITY   },
42   /*  { "UNSEEN",      MU_IMAP_STAT_NUM_UNSEEN  }, */
43   { NULL }
44 };
45 
46 static int
_status_mapper(void ** itmv,size_t itmc,void * call_data)47 _status_mapper (void **itmv, size_t itmc, void *call_data)
48 {
49   struct mu_imap_stat *ps = call_data;
50   struct imap_list_element *kw = itmv[0], *val = itmv[1];
51   size_t value;
52   char *p;
53   int flag;
54 
55   if (kw->type != imap_eltype_string || val->type != imap_eltype_string)
56     return MU_ERR_PARSE;
57   if (mu_kwd_xlat_name_ci (_mu_imap_status_name_table, kw->v.string, &flag))
58     return MU_ERR_PARSE;
59 
60   value = strtoul (val->v.string, &p, 10);
61   if (*p)
62     return MU_ERR_PARSE;
63 
64   ps->flags |= flag;
65 
66   switch (flag)
67     {
68     case MU_IMAP_STAT_MESSAGE_COUNT:
69       ps->message_count = value;
70       break;
71 
72     case MU_IMAP_STAT_RECENT_COUNT:
73       ps->recent_count = value;
74       break;
75 
76     case MU_IMAP_STAT_UIDNEXT:
77       ps->uidnext = value;
78       break;
79 
80     case MU_IMAP_STAT_UIDVALIDITY:
81       ps->uidvalidity = value;
82       break;
83 
84     case MU_IMAP_STAT_FIRST_UNSEEN:
85       ps->first_unseen = value;
86     }
87   return 0;
88 }
89 
90 struct status_data
91 {
92   const char *mboxname;
93   struct mu_imap_stat *ps;
94 };
95 
96 static void
_status_response_action(mu_imap_t imap,mu_list_t response,void * data)97 _status_response_action (mu_imap_t imap, mu_list_t response, void *data)
98 {
99   struct status_data *sd = data;
100   struct imap_list_element *elt;
101 
102   elt = _mu_imap_list_at (response, 0);
103   if (elt && _mu_imap_list_element_is_string (elt, "STATUS"))
104     {
105       elt = _mu_imap_list_at (response, 1);
106       if (elt && _mu_imap_list_element_is_string (elt, sd->mboxname))
107 	{
108 	  elt = _mu_imap_list_at (response, 2);
109 	  if (elt && elt->type == imap_eltype_list)
110 	    {
111 	      sd->ps->flags = 0;
112 	      mu_list_gmap (elt->v.list, _status_mapper, 2, sd->ps);
113 	    }
114 	}
115     }
116 }
117 
118 int
mu_imap_status(mu_imap_t imap,const char * mboxname,struct mu_imap_stat * ps)119 mu_imap_status (mu_imap_t imap, const char *mboxname, struct mu_imap_stat *ps)
120 {
121   int status;
122   int delim = 0;
123   int i;
124 
125   if (imap == NULL)
126     return EINVAL;
127   if (!imap->io)
128     return MU_ERR_NO_TRANSPORT;
129   if (imap->session_state != MU_IMAP_SESSION_AUTH &&
130       imap->session_state != MU_IMAP_SESSION_SELECTED)
131     return MU_ERR_SEQ;
132   if (!ps)
133     return MU_ERR_OUT_PTR_NULL;
134   if ((ps->flags & STATUS_FLAG_MASK) == 0)
135     return EINVAL;
136 
137   if (!mboxname)
138     {
139       if (imap->session_state == MU_IMAP_SESSION_SELECTED)
140 	{
141 	  if (ps)
142 	    *ps = imap->mbox_stat;
143 	  return 0;
144 	}
145       return EINVAL;
146     }
147 
148   if (imap->mbox_name && strcmp (imap->mbox_name, mboxname) == 0)
149     {
150       if (ps)
151 	*ps = imap->mbox_stat;
152       return 0;
153     }
154 
155   switch (imap->client_state)
156     {
157     case MU_IMAP_CLIENT_READY:
158       status = _mu_imap_tag_next (imap);
159       MU_IMAP_CHECK_EAGAIN (imap, status);
160       status = mu_imapio_printf (imap->io, "%s STATUS %s (",
161 				 imap->tag_str, mboxname);
162       MU_IMAP_CHECK_ERROR (imap, status);
163       delim = 0;
164       for (i = 0; status == 0 && _mu_imap_status_name_table[i].name; i++)
165 	{
166 	  if (ps->flags & _mu_imap_status_name_table[i].tok)
167 	    {
168 	      if (delim)
169 		status = mu_imapio_send (imap->io, " ", 1);
170 	      if (status == 0)
171 		status = mu_imapio_printf (imap->io, "%s",
172 					   _mu_imap_status_name_table[i].name);
173 	    }
174 	  delim = 1;
175 	}
176       if (status == 0)
177 	status = mu_imapio_send (imap->io, ")\r\n", 3);
178       MU_IMAP_CHECK_ERROR (imap, status);
179       MU_IMAP_FCLR (imap, MU_IMAP_RESP);
180       imap->client_state = MU_IMAP_CLIENT_STATUS_RX;
181 
182     case MU_IMAP_CLIENT_STATUS_RX:
183       {
184 	struct status_data sd = { mboxname, ps };
185 
186 	status = _mu_imap_response (imap, _status_response_action, &sd);
187 	MU_IMAP_CHECK_EAGAIN (imap, status);
188 	switch (imap->response)
189 	  {
190 	  case MU_IMAP_OK:
191 	    break;
192 
193 	  case MU_IMAP_NO:
194 	    status = EACCES;
195 	    break;
196 
197 	  case MU_IMAP_BAD:
198 	    status = MU_ERR_BADREPLY;
199 	    break;
200 	  }
201 	imap->client_state = MU_IMAP_CLIENT_READY;
202       }
203       break;
204 
205     default:
206       status = EINPROGRESS;
207     }
208   return status;
209 }
210