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