Commit c96bc2c0 c96bc2c092dda2d3c9fe3f1498857c7d4afb56dc by Sergey Poznyakoff

imap client: implement status command

* include/mailutils/imap.h (mu_imap_status): New proto.
(_mu_imap_status_name_table): New declaration.
* include/mailutils/sys/imap.h (mu_imap_client_state)
<MU_IMAP_STATUS_RX>: New state.
* libproto/imap/status.c: New file.
* libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Add status.c
* mu/imap.c: Implement "status"
1 parent ebbbc397
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
21 #include <mailutils/iterator.h> 21 #include <mailutils/iterator.h>
22 #include <mailutils/debug.h> 22 #include <mailutils/debug.h>
23 #include <mailutils/stream.h> 23 #include <mailutils/stream.h>
24 #include <mailutils/kwd.h>
24 25
25 #ifdef __cplusplus 26 #ifdef __cplusplus
26 extern "C" { 27 extern "C" {
...@@ -95,6 +96,10 @@ struct mu_imap_stat ...@@ -95,6 +96,10 @@ struct mu_imap_stat
95 int mu_imap_select (mu_imap_t imap, const char *mbox, int writable, 96 int mu_imap_select (mu_imap_t imap, const char *mbox, int writable,
96 struct mu_imap_stat *ps); 97 struct mu_imap_stat *ps);
97 98
99 int mu_imap_status (mu_imap_t imap, const char *mbox, struct mu_imap_stat *ps);
100
101 extern struct mu_kwd _mu_imap_status_name_table[];
102
98 #ifdef __cplusplus 103 #ifdef __cplusplus
99 } 104 }
100 #endif 105 #endif
......
...@@ -44,7 +44,8 @@ enum mu_imap_client_state ...@@ -44,7 +44,8 @@ enum mu_imap_client_state
44 MU_IMAP_LOGIN_RX, 44 MU_IMAP_LOGIN_RX,
45 MU_IMAP_LOGOUT_RX, 45 MU_IMAP_LOGOUT_RX,
46 MU_IMAP_ID_RX, 46 MU_IMAP_ID_RX,
47 MU_IMAP_SELECT_RX 47 MU_IMAP_SELECT_RX,
48 MU_IMAP_STATUS_RX
48 }; 49 };
49 50
50 enum mu_imap_response 51 enum mu_imap_response
......
...@@ -43,6 +43,7 @@ libmu_imap_la_SOURCES = \ ...@@ -43,6 +43,7 @@ libmu_imap_la_SOURCES = \
43 response.c\ 43 response.c\
44 select.c\ 44 select.c\
45 state.c\ 45 state.c\
46 status.c\
46 tag.c\ 47 tag.c\
47 trace.c 48 trace.c
48 49
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2011 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_FIRST_UNSEEN },
43 { NULL }
44 };
45
46 static int
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 static int
91 _parse_status_response (mu_imap_t imap, const char *mboxname,
92 struct mu_imap_stat *ps)
93 {
94 struct imap_list_element *response, *elt;
95 size_t count;
96 int rc;
97
98 rc = mu_list_get (imap->untagged_resp, 0, (void*) &response);
99 if (rc)
100 return rc;
101
102 mu_list_count (response->v.list, &count);
103 if (count != 3)
104 return MU_ERR_PARSE;
105 rc = mu_list_get (response->v.list, 0, (void*) &elt);
106 if (rc)
107 return rc;
108 if (!_mu_imap_list_element_is_string (elt, "STATUS"))
109 return MU_ERR_NOENT;
110
111 rc = mu_list_get (response->v.list, 1, (void*) &elt);
112 if (rc)
113 return rc;
114 if (!_mu_imap_list_element_is_string (elt, mboxname))
115 return MU_ERR_NOENT;
116
117 rc = mu_list_get (response->v.list, 2, (void*) &elt);
118 if (rc)
119 return rc;
120 if (elt->type != imap_eltype_list)
121 return MU_ERR_PARSE;
122
123 ps->flags = 0;
124 return mu_list_gmap (elt->v.list, _status_mapper, 2, ps);
125 }
126
127 int
128 mu_imap_status (mu_imap_t imap, const char *mboxname, struct mu_imap_stat *ps)
129 {
130 int status;
131 char *p;
132 int delim = 0;
133 int i;
134
135 if (imap == NULL)
136 return EINVAL;
137 if (!imap->io)
138 return MU_ERR_NO_TRANSPORT;
139 if (imap->state != MU_IMAP_CONNECTED)
140 return MU_ERR_SEQ;
141 if (imap->imap_state != MU_IMAP_STATE_AUTH &&
142 imap->imap_state != MU_IMAP_STATE_SELECTED)
143 return MU_ERR_SEQ;
144 if (!ps)
145 return MU_ERR_OUT_PTR_NULL;
146 if ((ps->flags & STATUS_FLAG_MASK) == 0)
147 return EINVAL;
148
149 if (!mboxname)
150 {
151 if (imap->imap_state == MU_IMAP_STATE_SELECTED)
152 {
153 if (ps)
154 *ps = imap->mbox_stat;
155 return 0;
156 }
157 return EINVAL;
158 }
159
160 if (imap->mbox_name && strcmp (imap->mbox_name, mboxname) == 0)
161 {
162 if (ps)
163 *ps = imap->mbox_stat;
164 return 0;
165 }
166
167 switch (imap->state)
168 {
169 case MU_IMAP_CONNECTED:
170 status = _mu_imap_tag_next (imap);
171 MU_IMAP_CHECK_EAGAIN (imap, status);
172 status = mu_imapio_printf (imap->io, "%s STATUS %s (",
173 imap->tag_str, mboxname);
174 MU_IMAP_CHECK_ERROR (imap, status);
175 delim = 0;
176 for (i = 0; status == 0 && _mu_imap_status_name_table[i].name; i++)
177 {
178 if (ps->flags & _mu_imap_status_name_table[i].tok)
179 {
180 if (delim)
181 status = mu_imapio_send (imap->io, " ", 1);
182 if (status == 0)
183 status = mu_imapio_printf (imap->io, "%s",
184 _mu_imap_status_name_table[i].name);
185 }
186 delim = 1;
187 }
188 if (status == 0)
189 status = mu_imapio_send (imap->io, ")\r\n", 3);
190 MU_IMAP_CHECK_ERROR (imap, status);
191 MU_IMAP_FCLR (imap, MU_IMAP_RESP);
192 imap->state = MU_IMAP_STATUS_RX;
193
194 case MU_IMAP_STATUS_RX:
195 status = _mu_imap_response (imap);
196 MU_IMAP_CHECK_EAGAIN (imap, status);
197 switch (imap->resp_code)
198 {
199 case MU_IMAP_OK:
200 memset (&imap->mbox_stat, 0, sizeof (imap->mbox_stat));
201 status = _parse_status_response (imap, mboxname, ps);
202 break;
203
204 case MU_IMAP_NO:
205 status = EACCES;
206 break;
207
208 case MU_IMAP_BAD:
209 status = MU_ERR_BADREPLY;
210 if (mu_imapio_reply_string (imap->io, 2, &p) == 0)
211 {
212 _mu_imap_seterrstr (imap, p, strlen (p));
213 free (p);
214 }
215 break;
216 }
217 imap->state = MU_IMAP_CONNECTED;
218 break;
219
220 default:
221 status = EINPROGRESS;
222 }
223 return status;
224 }
...@@ -520,6 +520,40 @@ com_examine (int argc, char **argv) ...@@ -520,6 +520,40 @@ com_examine (int argc, char **argv)
520 { 520 {
521 return select_mbox (argc, argv, 0); 521 return select_mbox (argc, argv, 0);
522 } 522 }
523
524 static int
525 com_status (int argc, char **argv)
526 {
527 struct mu_imap_stat st;
528 int i, flag;
529 int status;
530
531 st.flags = 0;
532 for (i = 2; i < argc; i++)
533 {
534 if (mu_kwd_xlat_name_ci (_mu_imap_status_name_table, argv[i], &flag))
535 {
536 mu_error (_("unknown data item: %s"), argv[i]);
537 return 0;
538 }
539 st.flags |= flag;
540 }
541
542 status = mu_imap_status (imap, argv[1], &st);
543 if (status == 0)
544 {
545 print_imap_stats (&st);
546 }
547 else
548 {
549 const char *str;
550
551 mu_error ("status failed: %s", mu_strerror (status));
552 if (mu_imap_strerror (imap, &str) == 0)
553 mu_error ("server reply: %s", str);
554 }
555 return 0;
556 }
523 557
524 558
525 struct mutool_command imap_comtab[] = { 559 struct mutool_command imap_comtab[] = {
...@@ -548,11 +582,14 @@ struct mutool_command imap_comtab[] = { ...@@ -548,11 +582,14 @@ struct mutool_command imap_comtab[] = {
548 N_("[-test KW] [ARG [ARG...]]"), 582 N_("[-test KW] [ARG [ARG...]]"),
549 N_("send ID command") }, 583 N_("send ID command") },
550 { "select", 1, 2, com_select, 584 { "select", 1, 2, com_select,
551 N_("MBOX"), 585 N_("[MBOX]"),
552 N_("select a mailbox") }, 586 N_("select a mailbox") },
553 { "examine", 1, 2, com_examine, 587 { "examine", 1, 2, com_examine,
554 N_("MBOX"), 588 N_("[MBOX]"),
555 N_("examine a mailbox") }, 589 N_("examine a mailbox") },
590 { "status", 3, -1, com_status,
591 N_("MBOX KW [KW...]"),
592 N_("get mailbox status") },
556 { "quit", 1, 1, com_logout, 593 { "quit", 1, 1, com_logout,
557 NULL, 594 NULL,
558 N_("same as `logout'") }, 595 N_("same as `logout'") },
......