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);
int mu_imap_tag (mu_imap_t imap, const char **pseq);
int mu_imap_search (mu_imap_t imap, int uid, const char *expr,
mu_msgset_t *msgset);
#define MU_IMAP_STAT_DEFINED_FLAGS 0x01
#define MU_IMAP_STAT_PERMANENT_FLAGS 0x02
......
......@@ -20,7 +20,7 @@
#define _MAILUTILS_MAILBOX_H
#include <sys/types.h>
#include <time.h>
#include <mailutils/types.h>
#ifdef __cplusplus
......@@ -56,6 +56,7 @@ extern int mu_mailbox_get_folder (mu_mailbox_t, mu_folder_t *);
extern int mu_mailbox_set_folder (mu_mailbox_t, mu_folder_t);
extern int mu_mailbox_uidvalidity (mu_mailbox_t, unsigned long *);
extern int mu_mailbox_uidnext (mu_mailbox_t, size_t *);
extern int mu_mailbox_access_time (mu_mailbox_t mbox, time_t *return_time);
/* Messages. */
extern int mu_mailbox_get_message (mu_mailbox_t, size_t msgno,
......
......@@ -71,6 +71,7 @@ enum mu_imap_client_state
MU_IMAP_CLIENT_UNSUBSCRIBE_RX,
MU_IMAP_CLIENT_LSUB_RX,
MU_IMAP_CLIENT_STARTTLS_RX,
MU_IMAP_CLIENT_SEARCH_RX,
MU_IMAP_CLIENT_CLOSING
};
......
......@@ -20,6 +20,7 @@
# define _MAILUTILS_SYS_MAILBOX_H
# include <sys/types.h>
# include <time.h>
# include <stdio.h>
# include <mailutils/monitor.h>
......@@ -76,6 +77,7 @@ struct _mu_mailbox
int (*_translate) (mu_mailbox_t, int cmd, size_t, size_t *);
int (*_copy) (mu_mailbox_t, mu_msgset_t, const char *, int);
int (*_get_atime) (mu_mailbox_t, time_t *);
};
# ifdef __cplusplus
......
......@@ -20,13 +20,10 @@
#endif
#include <time.h>
/* Convert time 0 at UTC to our localtime, that tells us the offset
of our current timezone from UTC. */
/* Returns the offset of our timezone from UTC, in seconds. */
int
mu_utc_offset (void)
{
time_t t = 0;
struct tm *tm = gmtime (&t);
return - mktime (tm);
tzset ();
return - timezone;
}
......
......@@ -918,3 +918,12 @@ mu_mailbox_translate (mu_mailbox_t mbox, int cmd, size_t from, size_t *to)
return rc;
}
int
mu_mailbox_access_time (mu_mailbox_t mbox, time_t *return_time)
{
_MBOX_CHECK_Q (mbox, _get_atime);
if (!return_time)
return MU_ERR_OUT_PTR_NULL;
return mbox->_get_atime (mbox, return_time);
}
......
......@@ -55,6 +55,7 @@ libmu_imap_la_SOURCES = \
resplist.c\
response.c\
resproc.c\
search.c\
select.c\
state.c\
status.c\
......
......@@ -1004,7 +1004,7 @@ _fetch_fold (void *item, void *data)
if (!mt->name)
{
mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE9,
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9,
("ignoring unknown FETCH item '%s'", kw));
env->state = resp_skip;
return 0;
......@@ -1112,7 +1112,7 @@ _mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list)
status = mu_list_create (&result);
if (status)
{
mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("mu_list_create: %s", mu_strerror (status)));
return 1;
}
......
......@@ -812,7 +812,7 @@ _imap_messages_count (mu_mailbox_t mbox, size_t *pcount)
return MU_ERR_INFO_UNAVAILABLE;
return 0;
}
static int
_imap_messages_recent (mu_mailbox_t mbox, size_t *pcount)
{
......@@ -820,7 +820,20 @@ _imap_messages_recent (mu_mailbox_t mbox, size_t *pcount)
if (imbx->stats.flags & MU_IMAP_STAT_RECENT_COUNT)
*pcount = imbx->stats.recent_count;
else
return MU_ERR_INFO_UNAVAILABLE;
{
int rc;
mu_folder_t folder = mbox->folder;
mu_imap_t imap = folder->data;
mu_msgset_t msgset;
rc = mu_imap_search (imap, 0, "RECENT", &msgset);
if (rc)
return rc;
rc = mu_msgset_count (msgset, pcount);
mu_msgset_free (msgset);
return rc;
}
return 0;
}
......@@ -840,9 +853,36 @@ _imap_message_unseen (mu_mailbox_t mbox, size_t *pn)
{
struct _mu_imap_mailbox *imbx = mbox->data;
if (imbx->stats.flags & MU_IMAP_STAT_FIRST_UNSEEN)
*pn = imbx->stats.uidnext;
*pn = imbx->stats.first_unseen;
else
return MU_ERR_INFO_UNAVAILABLE;
{
int rc;
mu_folder_t folder = mbox->folder;
mu_imap_t imap = folder->data;
mu_msgset_t msgset;
mu_list_t list;
rc = mu_imap_search (imap, 0, "UNSEEN", &msgset);
if (rc)
return rc;
if (mu_msgset_is_empty (msgset))
{
mu_msgset_free (msgset);
return MU_ERR_NOENT;
}
rc = mu_msgset_get_list (msgset, &list);
if (rc == 0)
{
struct mu_msgrange r;
rc = mu_list_head (list, (void **) &r);
if (rc == 0)
*pn = r.msg_beg;
}
mu_msgset_free (msgset);
return rc;
}
return 0;
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2011-2013 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <mailutils/errno.h>
#include <mailutils/msgset.h>
#include <mailutils/nls.h>
#include <mailutils/imap.h>
#include <mailutils/sys/imap.h>
struct search_closure
{
mu_msgset_t msgset;
int retcode;
};
static int
add_msgno (void *item, void *data)
{
int rc;
struct imap_list_element *elt = item;
struct search_closure *scp = data;
char *p;
unsigned long num;
if (elt->type != imap_eltype_string)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("unexpected list element in untagged response from SEARCH")));
scp->retcode = MU_ERR_BADREPLY;
return 1;
}
if (!scp->msgset)
{
/* First element (SEARCH): create the message set and return */
rc = mu_msgset_create (&scp->msgset, NULL, MU_MSGSET_NUM);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("cannot create msgset: %s"),
mu_strerror (rc)));
scp->retcode = rc;
return rc;
}
return 0;
}
num = strtoul (elt->v.string, &p, 10);
if (*p)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
(_("not a number in untagged response from SEARCH: %s"),
elt->v.string));
scp->retcode = MU_ERR_BADREPLY;
return 1;
}
rc = mu_msgset_add_range (scp->msgset, num, num, MU_MSGSET_NUM);
if (rc)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("mu_msgset_add_range: %s", mu_strerror (rc)));
scp->retcode = rc;
return 1;
}
return 0;
}
static void
search_handler (mu_imap_t imap, mu_list_t resp, void *data)
{
mu_list_foreach (resp, add_msgno, data);
}
int
mu_imap_search (mu_imap_t imap, int uid, const char *expr, mu_msgset_t *msgset)
{
char const *argv[3];
int i, rc;
static struct imap_command com;
struct search_closure clos;
if (!expr)
return EINVAL;
if (!msgset)
return MU_ERR_OUT_PTR_NULL;
i = 0;
if (uid)
argv[i++] = "UID";
argv[i++] = "SEARCH";
clos.msgset = NULL;
clos.retcode = 0;
com.session_state = MU_IMAP_SESSION_SELECTED;
com.capa = NULL;
com.rx_state = MU_IMAP_CLIENT_SEARCH_RX;
com.argc = i;
com.argv = argv;
com.extra = expr;
com.msgset = NULL;
com.tagged_handler = NULL;
com.untagged_handler = search_handler;
com.untagged_handler_data = &clos;
rc = mu_imap_gencom (imap, &com);
if (rc)
mu_msgset_free (clos.msgset);
else if (clos.retcode)
{
mu_msgset_free (clos.msgset);
rc = clos.retcode;
}
else
*msgset = clos.msgset;
return rc;
}
......@@ -39,7 +39,7 @@ struct mu_kwd _mu_imap_status_name_table[] = {
{ "RECENT", MU_IMAP_STAT_RECENT_COUNT },
{ "UIDNEXT", MU_IMAP_STAT_UIDNEXT },
{ "UIDVALIDITY", MU_IMAP_STAT_UIDVALIDITY },
{ "UNSEEN", MU_IMAP_STAT_FIRST_UNSEEN },
/* { "UNSEEN", MU_IMAP_STAT_NUM_UNSEEN }, */
{ NULL }
};
......
......@@ -1436,6 +1436,20 @@ mbox_expunge0 (mu_mailbox_t mailbox, int remove_deleted)
return status;
}
static int
mbox_get_atime (mu_mailbox_t mailbox, time_t *return_time)
{
mbox_data_t mud = mailbox->data;
struct stat st;
if (mud == NULL)
return EINVAL;
if (stat (mud->name, &st))
return errno;
*return_time = st.st_atime;
return 0;
}
/* Allocate the mbox_data_t struct(concrete mailbox), but don't do any
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)
mailbox->_get_size = mbox_get_size;
mailbox->_get_atime = mbox_get_atime;
/* Set our properties. */
{
mu_property_t property = NULL;
......
......@@ -18,6 +18,7 @@
#include "mailutils/kwd.h"
#include "mailutils/folder.h"
#include "mailutils/auth.h"
#include "mailutils/datetime.h"
#include <pwd.h>
static char doc[] = N_("GNU MH msgchk")"\v"
......@@ -295,11 +296,20 @@ checkmail (const char *username, int personal)
{
mu_off_t mbsiz = 0;
rc = mu_mailbox_messages_recent (mbox, &recent);
rc = mu_mailbox_message_unseen (mbox, &recent);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_recent",
mu_url_to_string (url), rc);
if (rc != ENOSYS && rc != MU_ERR_INFO_UNAVAILABLE)
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_unseen",
mu_url_to_string (url), rc);
rc = mu_mailbox_messages_recent (mbox, &recent);
}
if (rc)
{
if (rc != ENOSYS && rc != MU_ERR_INFO_UNAVAILABLE)
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_messages_recent",
mu_url_to_string (url), rc);
mu_mailbox_get_size (mbox, &mbsiz);
if (personal)
......@@ -327,7 +337,14 @@ checkmail (const char *username, int personal)
}
if (date_option)
/*FIXME*/;
{
time_t t;
if (mu_mailbox_access_time (mbox, &t) == 0)
mu_c_streamftime (mu_strout,
_("; last read on %a, %d %b %Y %H:%M:%S %z"),
localtime (&t), NULL);
}
mu_printf ("\n");
}
......
......@@ -1122,6 +1122,35 @@ com_copy (int argc, char **argv)
}
static int
com_search (int argc, char **argv)
{
mu_msgset_t mset;
size_t count;
int rc;
rc = mu_imap_search (imap, uid_mode, argv[1], &mset);
if (rc)
{
report_failure ("search", rc);
return 0;
}
rc = mu_msgset_count (mset, &count);
if (rc == EINVAL || count == 0)
{
mu_printf (_("no matches"));
return 0;
}
mu_printf ("%lu matches:", (unsigned long) count);
mu_msgset_print (mu_strout, mset);
mu_printf ("\n");
mu_msgset_free (mset);
return 0;
}
static int
print_list_item (void *item, void *data)
{
struct mu_list_response *resp = item;
......@@ -1293,6 +1322,10 @@ struct mutool_command imap_comtab[] = {
com_uid,
N_("[on|off]"),
N_("control UID mode") },
{ "search", 2, 2, CMD_COALESCE_EXTRA_ARGS,
com_search,
N_("args..."),
N_("search the mailbox") },
{ "quit", 1, 1, 0,
com_logout,
NULL,
......