1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2011-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <config.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <limits.h>
21 #include <mailutils/types.h>
22 #include <mailutils/errno.h>
23 #include <mailutils/list.h>
24 #include <mailutils/iterator.h>
25 #include <mailutils/mailbox.h>
26 #include <mailutils/msgset.h>
27 #include <mailutils/sys/msgset.h>
28 
29 /* Special translation mode, indicating that the mailbox is empty */
30 #define MU_MSGSET_EMPTY -1
31 
32 /* This structure keeps parser state while parsing message set. */
33 struct parse_msgnum_env
34 {
35   const char *s;         /* Current position in string */
36   size_t minval;         /* Min. sequence number or UID */
37   size_t maxval;         /* Max. sequence number or UID */
38   mu_msgset_t msgset;    /* Message set being built. */
39   int mode;              /* Operation mode (num/uid/dry_run) */
40 };
41 
42 /* Get a single message number/UID from env->s and store it into *PN.
43    Return 0 on success and error code on error.
44 
45    Advance env->s to the point past the parsed message number.
46  */
47 static int
get_msgnum(struct parse_msgnum_env * env,size_t * pn)48 get_msgnum (struct parse_msgnum_env *env, size_t *pn)
49 {
50   size_t msgnum;
51   char *p;
52 
53   errno = 0;
54   msgnum = strtoul (env->s, &p, 10);
55   if (msgnum == ULONG_MAX && errno == ERANGE)
56     return MU_ERR_PARSE;
57   env->s = p;
58   if (env->minval && msgnum < env->minval)
59     msgnum = env->minval;
60   if (env->maxval && msgnum > env->maxval)
61     msgnum = env->maxval;
62   *pn = msgnum;
63   return 0;
64 }
65 
66 /* Parse a single message range (A:B). Treat '*' as the largest number/UID
67    in use. */
68 static int
parse_msgrange(struct parse_msgnum_env * env)69 parse_msgrange (struct parse_msgnum_env *env)
70 {
71   int rc;
72   struct mu_msgrange msgrange;
73 
74   if (*env->s == '*')
75     {
76       msgrange.msg_beg = env->maxval;
77       env->s++;
78     }
79   else if ((rc = get_msgnum (env, &msgrange.msg_beg)))
80     return rc;
81 
82   if (*env->s == ':')
83     {
84       if (*++env->s == '*')
85 	{
86 	  msgrange.msg_end = env->maxval;
87 	  ++env->s;
88 	}
89       else if (*env->s == 0)
90 	return MU_ERR_PARSE;
91       else if ((rc = get_msgnum (env, &msgrange.msg_end)))
92 	return rc;
93     }
94   else
95     msgrange.msg_end = msgrange.msg_beg;
96 
97   if (msgrange.msg_end && msgrange.msg_end < msgrange.msg_beg)
98     {
99       size_t tmp = msgrange.msg_end;
100       msgrange.msg_end = msgrange.msg_beg;
101       msgrange.msg_beg = tmp;
102     }
103 
104   if (env->mode == MU_MSGSET_EMPTY)
105     return 0;
106   return mu_msgset_add_range (env->msgset, msgrange.msg_beg, msgrange.msg_end,
107 			      env->mode);
108 }
109 
110 /* Parse IMAP-style message set specification S.
111 
112    On success, return 0 and populate MSET with the resulting message ranges.
113    On error, return error code and point END to the position in the input
114    string where parsing has failed. */
115 int
mu_msgset_parse_imap(mu_msgset_t mset,int mode,const char * s,char ** end)116 mu_msgset_parse_imap (mu_msgset_t mset, int mode, const char *s, char **end)
117 {
118   int rc;
119   struct parse_msgnum_env env;
120 
121   if (!s || !mset)
122     return EINVAL;
123   if (end)
124     *end = (char*) s;
125   if (!*s)
126     return MU_ERR_PARSE;
127 
128   memset (&env, 0, sizeof (env));
129   env.s = s;
130   env.msgset = mset;
131   env.minval = 1;
132   env.mode = mode;
133 
134   if (mset->mbox)
135     {
136       size_t lastmsgno;      /* Max. sequence number. */
137 
138       rc = mu_mailbox_messages_count (mset->mbox, &lastmsgno);
139       if (rc == 0)
140 	{
141 	  if (lastmsgno == 0)
142 	    {
143 	      env.mode = MU_MSGSET_EMPTY;
144 	    }
145 	  else if (mode == MU_MSGSET_UID)
146 	    {
147 	      rc = mu_mailbox_translate (mset->mbox, MU_MAILBOX_MSGNO_TO_UID,
148 					 lastmsgno, &env.maxval);
149 	      if (rc == 0)
150 		rc = mu_mailbox_translate (mset->mbox, MU_MAILBOX_MSGNO_TO_UID,
151 					   1, &env.minval);
152 	    }
153 	  else
154 	    env.maxval = lastmsgno;
155 	}
156       if (rc)
157 	return rc;
158     }
159 
160   while ((rc = parse_msgrange (&env)) == 0 && *env.s)
161     {
162       if (*env.s != ',' || *++env.s == 0)
163 	{
164 	  rc = MU_ERR_PARSE;
165 	  break;
166 	}
167     }
168 
169   if (end)
170     *end = (char*) env.s;
171   return rc;
172 }
173