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