Commit 441dda65 441dda65dd5aa0a97bc26ea7398d38de43dbee06 by Sergey Poznyakoff

Implement IMAP SEARCH. New function mu_mailbox_access_time.

* include/mailutils/imap.h (mu_imap_search): New proto.
* libproto/imap/search.c: New file.
* include/mailutils/sys/imap.h (MU_IMAP_CLIENT_SEARCH_RX): New state.
* libproto/imap/fetch.c: Fix debugging categories
* libproto/imap/mbox.c (_imap_messages_recent)
(_imap_message_unseen): Use search if information is not
readily available.
* libproto/imap/status.c (_mu_imap_status_name_table): Comment
out UNSEEN: its semantics is entirely different from what we
need.
* libproto/imap/Makefile.am: Add new file.
* mu/imap.c: Implement search.

* include/mailutils/sys/mailbox.h (_mu_mailbox) <_get_atime>: New method.
* include/mailutils/mailbox.h (mu_mailbox_access_time): New proto.
* libproto/mbox/mbox.c (_mailbox_mbox_init): Initialize
>_get_atime.
* libmailutils/mailbox/mailbox.c (mu_mailbox_access_time): New function.

* libmailutils/datetime/utcoff.c (mu_utc_offset): Rewrite using tzset.

* mh/msgchk.c: Implement --date.
Check for new mail using both recent and unseen messages.
1 parent b8dc1293
...@@ -143,6 +143,9 @@ int mu_imap_session_state_str (int state, const char **pstr); ...@@ -143,6 +143,9 @@ int mu_imap_session_state_str (int state, const char **pstr);
143 143
144 int mu_imap_tag (mu_imap_t imap, const char **pseq); 144 int mu_imap_tag (mu_imap_t imap, const char **pseq);
145 145
146 int mu_imap_search (mu_imap_t imap, int uid, const char *expr,
147 mu_msgset_t *msgset);
148
146 149
147 #define MU_IMAP_STAT_DEFINED_FLAGS 0x01 150 #define MU_IMAP_STAT_DEFINED_FLAGS 0x01
148 #define MU_IMAP_STAT_PERMANENT_FLAGS 0x02 151 #define MU_IMAP_STAT_PERMANENT_FLAGS 0x02
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
20 #define _MAILUTILS_MAILBOX_H 20 #define _MAILUTILS_MAILBOX_H
21 21
22 #include <sys/types.h> 22 #include <sys/types.h>
23 23 #include <time.h>
24 #include <mailutils/types.h> 24 #include <mailutils/types.h>
25 25
26 #ifdef __cplusplus 26 #ifdef __cplusplus
...@@ -56,6 +56,7 @@ extern int mu_mailbox_get_folder (mu_mailbox_t, mu_folder_t *); ...@@ -56,6 +56,7 @@ extern int mu_mailbox_get_folder (mu_mailbox_t, mu_folder_t *);
56 extern int mu_mailbox_set_folder (mu_mailbox_t, mu_folder_t); 56 extern int mu_mailbox_set_folder (mu_mailbox_t, mu_folder_t);
57 extern int mu_mailbox_uidvalidity (mu_mailbox_t, unsigned long *); 57 extern int mu_mailbox_uidvalidity (mu_mailbox_t, unsigned long *);
58 extern int mu_mailbox_uidnext (mu_mailbox_t, size_t *); 58 extern int mu_mailbox_uidnext (mu_mailbox_t, size_t *);
59 extern int mu_mailbox_access_time (mu_mailbox_t mbox, time_t *return_time);
59 60
60 /* Messages. */ 61 /* Messages. */
61 extern int mu_mailbox_get_message (mu_mailbox_t, size_t msgno, 62 extern int mu_mailbox_get_message (mu_mailbox_t, size_t msgno,
......
...@@ -71,6 +71,7 @@ enum mu_imap_client_state ...@@ -71,6 +71,7 @@ enum mu_imap_client_state
71 MU_IMAP_CLIENT_UNSUBSCRIBE_RX, 71 MU_IMAP_CLIENT_UNSUBSCRIBE_RX,
72 MU_IMAP_CLIENT_LSUB_RX, 72 MU_IMAP_CLIENT_LSUB_RX,
73 MU_IMAP_CLIENT_STARTTLS_RX, 73 MU_IMAP_CLIENT_STARTTLS_RX,
74 MU_IMAP_CLIENT_SEARCH_RX,
74 MU_IMAP_CLIENT_CLOSING 75 MU_IMAP_CLIENT_CLOSING
75 }; 76 };
76 77
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
20 # define _MAILUTILS_SYS_MAILBOX_H 20 # define _MAILUTILS_SYS_MAILBOX_H
21 21
22 # include <sys/types.h> 22 # include <sys/types.h>
23 # include <time.h>
23 # include <stdio.h> 24 # include <stdio.h>
24 25
25 # include <mailutils/monitor.h> 26 # include <mailutils/monitor.h>
...@@ -76,6 +77,7 @@ struct _mu_mailbox ...@@ -76,6 +77,7 @@ struct _mu_mailbox
76 77
77 int (*_translate) (mu_mailbox_t, int cmd, size_t, size_t *); 78 int (*_translate) (mu_mailbox_t, int cmd, size_t, size_t *);
78 int (*_copy) (mu_mailbox_t, mu_msgset_t, const char *, int); 79 int (*_copy) (mu_mailbox_t, mu_msgset_t, const char *, int);
80 int (*_get_atime) (mu_mailbox_t, time_t *);
79 }; 81 };
80 82
81 # ifdef __cplusplus 83 # ifdef __cplusplus
......
...@@ -20,13 +20,10 @@ ...@@ -20,13 +20,10 @@
20 #endif 20 #endif
21 #include <time.h> 21 #include <time.h>
22 22
23 /* Convert time 0 at UTC to our localtime, that tells us the offset 23 /* Returns the offset of our timezone from UTC, in seconds. */
24 of our current timezone from UTC. */
25 int 24 int
26 mu_utc_offset (void) 25 mu_utc_offset (void)
27 { 26 {
28 time_t t = 0; 27 tzset ();
29 struct tm *tm = gmtime (&t); 28 return - timezone;
30
31 return - mktime (tm);
32 } 29 }
......
...@@ -918,3 +918,12 @@ mu_mailbox_translate (mu_mailbox_t mbox, int cmd, size_t from, size_t *to) ...@@ -918,3 +918,12 @@ mu_mailbox_translate (mu_mailbox_t mbox, int cmd, size_t from, size_t *to)
918 return rc; 918 return rc;
919 } 919 }
920 920
921 int
922 mu_mailbox_access_time (mu_mailbox_t mbox, time_t *return_time)
923 {
924 _MBOX_CHECK_Q (mbox, _get_atime);
925 if (!return_time)
926 return MU_ERR_OUT_PTR_NULL;
927 return mbox->_get_atime (mbox, return_time);
928 }
929
......
...@@ -55,6 +55,7 @@ libmu_imap_la_SOURCES = \ ...@@ -55,6 +55,7 @@ libmu_imap_la_SOURCES = \
55 resplist.c\ 55 resplist.c\
56 response.c\ 56 response.c\
57 resproc.c\ 57 resproc.c\
58 search.c\
58 select.c\ 59 select.c\
59 state.c\ 60 state.c\
60 status.c\ 61 status.c\
......
...@@ -1004,7 +1004,7 @@ _fetch_fold (void *item, void *data) ...@@ -1004,7 +1004,7 @@ _fetch_fold (void *item, void *data)
1004 1004
1005 if (!mt->name) 1005 if (!mt->name)
1006 { 1006 {
1007 mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE9, 1007 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9,
1008 ("ignoring unknown FETCH item '%s'", kw)); 1008 ("ignoring unknown FETCH item '%s'", kw));
1009 env->state = resp_skip; 1009 env->state = resp_skip;
1010 return 0; 1010 return 0;
...@@ -1112,7 +1112,7 @@ _mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list) ...@@ -1112,7 +1112,7 @@ _mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list)
1112 status = mu_list_create (&result); 1112 status = mu_list_create (&result);
1113 if (status) 1113 if (status)
1114 { 1114 {
1115 mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, 1115 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
1116 ("mu_list_create: %s", mu_strerror (status))); 1116 ("mu_list_create: %s", mu_strerror (status)));
1117 return 1; 1117 return 1;
1118 } 1118 }
......
...@@ -812,7 +812,7 @@ _imap_messages_count (mu_mailbox_t mbox, size_t *pcount) ...@@ -812,7 +812,7 @@ _imap_messages_count (mu_mailbox_t mbox, size_t *pcount)
812 return MU_ERR_INFO_UNAVAILABLE; 812 return MU_ERR_INFO_UNAVAILABLE;
813 return 0; 813 return 0;
814 } 814 }
815 815
816 static int 816 static int
817 _imap_messages_recent (mu_mailbox_t mbox, size_t *pcount) 817 _imap_messages_recent (mu_mailbox_t mbox, size_t *pcount)
818 { 818 {
...@@ -820,7 +820,20 @@ _imap_messages_recent (mu_mailbox_t mbox, size_t *pcount) ...@@ -820,7 +820,20 @@ _imap_messages_recent (mu_mailbox_t mbox, size_t *pcount)
820 if (imbx->stats.flags & MU_IMAP_STAT_RECENT_COUNT) 820 if (imbx->stats.flags & MU_IMAP_STAT_RECENT_COUNT)
821 *pcount = imbx->stats.recent_count; 821 *pcount = imbx->stats.recent_count;
822 else 822 else
823 return MU_ERR_INFO_UNAVAILABLE; 823 {
824 int rc;
825 mu_folder_t folder = mbox->folder;
826 mu_imap_t imap = folder->data;
827 mu_msgset_t msgset;
828
829 rc = mu_imap_search (imap, 0, "RECENT", &msgset);
830 if (rc)
831 return rc;
832
833 rc = mu_msgset_count (msgset, pcount);
834 mu_msgset_free (msgset);
835 return rc;
836 }
824 return 0; 837 return 0;
825 } 838 }
826 839
...@@ -840,9 +853,36 @@ _imap_message_unseen (mu_mailbox_t mbox, size_t *pn) ...@@ -840,9 +853,36 @@ _imap_message_unseen (mu_mailbox_t mbox, size_t *pn)
840 { 853 {
841 struct _mu_imap_mailbox *imbx = mbox->data; 854 struct _mu_imap_mailbox *imbx = mbox->data;
842 if (imbx->stats.flags & MU_IMAP_STAT_FIRST_UNSEEN) 855 if (imbx->stats.flags & MU_IMAP_STAT_FIRST_UNSEEN)
843 *pn = imbx->stats.uidnext; 856 *pn = imbx->stats.first_unseen;
844 else 857 else
845 return MU_ERR_INFO_UNAVAILABLE; 858 {
859 int rc;
860 mu_folder_t folder = mbox->folder;
861 mu_imap_t imap = folder->data;
862 mu_msgset_t msgset;
863 mu_list_t list;
864
865 rc = mu_imap_search (imap, 0, "UNSEEN", &msgset);
866 if (rc)
867 return rc;
868
869 if (mu_msgset_is_empty (msgset))
870 {
871 mu_msgset_free (msgset);
872 return MU_ERR_NOENT;
873 }
874
875 rc = mu_msgset_get_list (msgset, &list);
876 if (rc == 0)
877 {
878 struct mu_msgrange r;
879 rc = mu_list_head (list, (void **) &r);
880 if (rc == 0)
881 *pn = r.msg_beg;
882 }
883 mu_msgset_free (msgset);
884 return rc;
885 }
846 return 0; 886 return 0;
847 } 887 }
848 888
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2011-2013 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 #include <config.h>
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <mailutils/errno.h>
23 #include <mailutils/msgset.h>
24 #include <mailutils/nls.h>
25 #include <mailutils/imap.h>
26 #include <mailutils/sys/imap.h>
27
28 struct search_closure
29 {
30 mu_msgset_t msgset;
31 int retcode;
32 };
33
34 static int
35 add_msgno (void *item, void *data)
36 {
37 int rc;
38 struct imap_list_element *elt = item;
39 struct search_closure *scp = data;
40 char *p;
41 unsigned long num;
42
43 if (elt->type != imap_eltype_string)
44 {
45 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
46 (_("unexpected list element in untagged response from SEARCH")));
47 scp->retcode = MU_ERR_BADREPLY;
48 return 1;
49 }
50
51 if (!scp->msgset)
52 {
53 /* First element (SEARCH): create the message set and return */
54 rc = mu_msgset_create (&scp->msgset, NULL, MU_MSGSET_NUM);
55 if (rc)
56 {
57 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
58 (_("cannot create msgset: %s"),
59 mu_strerror (rc)));
60 scp->retcode = rc;
61 return rc;
62 }
63 return 0;
64 }
65
66 num = strtoul (elt->v.string, &p, 10);
67 if (*p)
68 {
69 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
70 (_("not a number in untagged response from SEARCH: %s"),
71 elt->v.string));
72 scp->retcode = MU_ERR_BADREPLY;
73 return 1;
74 }
75
76 rc = mu_msgset_add_range (scp->msgset, num, num, MU_MSGSET_NUM);
77 if (rc)
78 {
79 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
80 ("mu_msgset_add_range: %s", mu_strerror (rc)));
81 scp->retcode = rc;
82 return 1;
83 }
84 return 0;
85 }
86
87 static void
88 search_handler (mu_imap_t imap, mu_list_t resp, void *data)
89 {
90 mu_list_foreach (resp, add_msgno, data);
91 }
92
93 int
94 mu_imap_search (mu_imap_t imap, int uid, const char *expr, mu_msgset_t *msgset)
95 {
96 char const *argv[3];
97 int i, rc;
98 static struct imap_command com;
99 struct search_closure clos;
100
101 if (!expr)
102 return EINVAL;
103 if (!msgset)
104 return MU_ERR_OUT_PTR_NULL;
105 i = 0;
106 if (uid)
107 argv[i++] = "UID";
108 argv[i++] = "SEARCH";
109
110 clos.msgset = NULL;
111 clos.retcode = 0;
112
113 com.session_state = MU_IMAP_SESSION_SELECTED;
114 com.capa = NULL;
115 com.rx_state = MU_IMAP_CLIENT_SEARCH_RX;
116 com.argc = i;
117 com.argv = argv;
118 com.extra = expr;
119 com.msgset = NULL;
120 com.tagged_handler = NULL;
121 com.untagged_handler = search_handler;
122 com.untagged_handler_data = &clos;
123 rc = mu_imap_gencom (imap, &com);
124 if (rc)
125 mu_msgset_free (clos.msgset);
126 else if (clos.retcode)
127 {
128 mu_msgset_free (clos.msgset);
129 rc = clos.retcode;
130 }
131 else
132 *msgset = clos.msgset;
133 return rc;
134 }
...@@ -39,7 +39,7 @@ struct mu_kwd _mu_imap_status_name_table[] = { ...@@ -39,7 +39,7 @@ struct mu_kwd _mu_imap_status_name_table[] = {
39 { "RECENT", MU_IMAP_STAT_RECENT_COUNT }, 39 { "RECENT", MU_IMAP_STAT_RECENT_COUNT },
40 { "UIDNEXT", MU_IMAP_STAT_UIDNEXT }, 40 { "UIDNEXT", MU_IMAP_STAT_UIDNEXT },
41 { "UIDVALIDITY", MU_IMAP_STAT_UIDVALIDITY }, 41 { "UIDVALIDITY", MU_IMAP_STAT_UIDVALIDITY },
42 { "UNSEEN", MU_IMAP_STAT_FIRST_UNSEEN }, 42 /* { "UNSEEN", MU_IMAP_STAT_NUM_UNSEEN }, */
43 { NULL } 43 { NULL }
44 }; 44 };
45 45
......
...@@ -1436,6 +1436,20 @@ mbox_expunge0 (mu_mailbox_t mailbox, int remove_deleted) ...@@ -1436,6 +1436,20 @@ mbox_expunge0 (mu_mailbox_t mailbox, int remove_deleted)
1436 return status; 1436 return status;
1437 } 1437 }
1438 1438
1439 static int
1440 mbox_get_atime (mu_mailbox_t mailbox, time_t *return_time)
1441 {
1442 mbox_data_t mud = mailbox->data;
1443 struct stat st;
1444
1445 if (mud == NULL)
1446 return EINVAL;
1447 if (stat (mud->name, &st))
1448 return errno;
1449 *return_time = st.st_atime;
1450 return 0;
1451 }
1452
1439 1453
1440 /* Allocate the mbox_data_t struct(concrete mailbox), but don't do any 1454 /* Allocate the mbox_data_t struct(concrete mailbox), but don't do any
1441 parsing on the name or even test for existence. However we do strip any 1455 parsing on the name or even test for existence. However we do strip any
...@@ -1498,6 +1512,8 @@ _mailbox_mbox_init (mu_mailbox_t mailbox) ...@@ -1498,6 +1512,8 @@ _mailbox_mbox_init (mu_mailbox_t mailbox)
1498 1512
1499 mailbox->_get_size = mbox_get_size; 1513 mailbox->_get_size = mbox_get_size;
1500 1514
1515 mailbox->_get_atime = mbox_get_atime;
1516
1501 /* Set our properties. */ 1517 /* Set our properties. */
1502 { 1518 {
1503 mu_property_t property = NULL; 1519 mu_property_t property = NULL;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
18 #include "mailutils/kwd.h" 18 #include "mailutils/kwd.h"
19 #include "mailutils/folder.h" 19 #include "mailutils/folder.h"
20 #include "mailutils/auth.h" 20 #include "mailutils/auth.h"
21 #include "mailutils/datetime.h"
21 #include <pwd.h> 22 #include <pwd.h>
22 23
23 static char doc[] = N_("GNU MH msgchk")"\v" 24 static char doc[] = N_("GNU MH msgchk")"\v"
...@@ -295,11 +296,20 @@ checkmail (const char *username, int personal) ...@@ -295,11 +296,20 @@ checkmail (const char *username, int personal)
295 { 296 {
296 mu_off_t mbsiz = 0; 297 mu_off_t mbsiz = 0;
297 298
298 rc = mu_mailbox_messages_recent (mbox, &recent); 299 rc = mu_mailbox_message_unseen (mbox, &recent);
299 if (rc) 300 if (rc)
300 { 301 {
301 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_recent", 302 if (rc != ENOSYS && rc != MU_ERR_INFO_UNAVAILABLE)
302 mu_url_to_string (url), rc); 303 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_unseen",
304 mu_url_to_string (url), rc);
305 rc = mu_mailbox_messages_recent (mbox, &recent);
306 }
307
308 if (rc)
309 {
310 if (rc != ENOSYS && rc != MU_ERR_INFO_UNAVAILABLE)
311 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_recent",
312 mu_url_to_string (url), rc);
303 313
304 mu_mailbox_get_size (mbox, &mbsiz); 314 mu_mailbox_get_size (mbox, &mbsiz);
305 if (personal) 315 if (personal)
...@@ -327,7 +337,14 @@ checkmail (const char *username, int personal) ...@@ -327,7 +337,14 @@ checkmail (const char *username, int personal)
327 } 337 }
328 338
329 if (date_option) 339 if (date_option)
330 /*FIXME*/; 340 {
341 time_t t;
342
343 if (mu_mailbox_access_time (mbox, &t) == 0)
344 mu_c_streamftime (mu_strout,
345 _("; last read on %a, %d %b %Y %H:%M:%S %z"),
346 localtime (&t), NULL);
347 }
331 348
332 mu_printf ("\n"); 349 mu_printf ("\n");
333 } 350 }
......
...@@ -1122,6 +1122,35 @@ com_copy (int argc, char **argv) ...@@ -1122,6 +1122,35 @@ com_copy (int argc, char **argv)
1122 } 1122 }
1123 1123
1124 static int 1124 static int
1125 com_search (int argc, char **argv)
1126 {
1127 mu_msgset_t mset;
1128 size_t count;
1129 int rc;
1130
1131 rc = mu_imap_search (imap, uid_mode, argv[1], &mset);
1132 if (rc)
1133 {
1134 report_failure ("search", rc);
1135 return 0;
1136 }
1137
1138 rc = mu_msgset_count (mset, &count);
1139 if (rc == EINVAL || count == 0)
1140 {
1141 mu_printf (_("no matches"));
1142 return 0;
1143 }
1144 mu_printf ("%lu matches:", (unsigned long) count);
1145 mu_msgset_print (mu_strout, mset);
1146 mu_printf ("\n");
1147 mu_msgset_free (mset);
1148
1149 return 0;
1150 }
1151
1152
1153 static int
1125 print_list_item (void *item, void *data) 1154 print_list_item (void *item, void *data)
1126 { 1155 {
1127 struct mu_list_response *resp = item; 1156 struct mu_list_response *resp = item;
...@@ -1293,6 +1322,10 @@ struct mutool_command imap_comtab[] = { ...@@ -1293,6 +1322,10 @@ struct mutool_command imap_comtab[] = {
1293 com_uid, 1322 com_uid,
1294 N_("[on|off]"), 1323 N_("[on|off]"),
1295 N_("control UID mode") }, 1324 N_("control UID mode") },
1325 { "search", 2, 2, CMD_COALESCE_EXTRA_ARGS,
1326 com_search,
1327 N_("args..."),
1328 N_("search the mailbox") },
1296 { "quit", 1, 1, 0, 1329 { "quit", 1, 1, 0,
1297 com_logout, 1330 com_logout,
1298 NULL, 1331 NULL,
......