1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-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 "imap4d.h"
18 
19 /*
20  *
21  */
22 
23 typedef int (*status_funcp) (mu_mailbox_t);
24 
25 static int status_messages    (mu_mailbox_t);
26 static int status_recent      (mu_mailbox_t);
27 static int status_uidnext     (mu_mailbox_t);
28 static int status_uidvalidity (mu_mailbox_t);
29 static int status_unseen      (mu_mailbox_t);
30 
31 struct status_table {
32   char *name;
33   status_funcp fun;
34 } status_table[] = {
35   {"MESSAGES", status_messages},
36   {"RECENT", status_recent},
37   {"UIDNEXT", status_uidnext},
38   {"UIDVALIDITY", status_uidvalidity},
39   {"UNSEEN", status_unseen},
40   { NULL }
41 };
42 
43 static status_funcp
status_get_handler(const char * name)44 status_get_handler (const char *name)
45 {
46   struct status_table *p;
47 
48   for (p = status_table; p->name; p++)
49     if (mu_c_strcasecmp (p->name, name) == 0)
50       return p->fun;
51   return NULL;
52 }
53 
54 /*
55 6.3.10. STATUS Command
56 
57    Arguments:  mailbox name
58                status data item names
59 
60    Responses:  untagged responses: STATUS
61 
62    Result:     OK - status completed
63                NO - status failure: no status for that name
64                BAD - command unknown or arguments invalid
65 */
66 int
imap4d_status(struct imap4d_session * session,struct imap4d_command * command,imap4d_tokbuf_t tok)67 imap4d_status (struct imap4d_session *session,
68                struct imap4d_command *command, imap4d_tokbuf_t tok)
69 {
70   char *name;
71   char *mailbox_name;
72   mu_mailbox_t smbox = NULL;
73   int status;
74   int count = 0;
75   char *err_msg = NULL;
76   int argc = imap4d_tokbuf_argc (tok);
77   mu_record_t record;
78 
79   if (argc < 4)
80     return io_completion_response (command, RESP_BAD, "Invalid arguments");
81 
82   name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
83 
84   mailbox_name = namespace_get_name (name, &record, NULL);
85 
86   if (!mailbox_name)
87     return io_completion_response (command, RESP_NO, "Error opening mailbox");
88 
89   /* We may be opening the current mailbox, so make sure the attributes are
90      preserved */
91   imap4d_enter_critical ();
92   mu_mailbox_sync (mbox);
93   imap4d_leave_critical ();
94 
95   status = mu_mailbox_create_from_record (&smbox, record, mailbox_name);
96   if (status == 0)
97     {
98       status = mu_mailbox_open (smbox, MU_STREAM_READ);
99       if (status == 0)
100 	{
101           int space_sent = 0;
102 	  int i = IMAP4_ARG_2;
103 	  char *item = imap4d_tokbuf_getarg (tok, i);
104 
105 	  if (item[0] == '(')
106 	    {
107 	      if (imap4d_tokbuf_getarg (tok, argc - 1)[0] != ')')
108 		return io_completion_response (command, RESP_BAD,
109 		                               "Invalid arguments");
110 	      argc--;
111 	      i++;
112 	    }
113 
114 	  for (; i < argc; i++)
115 	    {
116 	      status_funcp fun;
117 
118 	      item = imap4d_tokbuf_getarg (tok, i);
119 	      fun = status_get_handler (item);
120 	      if (!fun)
121 		{
122 		  err_msg = "Invalid flag in list";
123 		  break;
124 		}
125 
126 	      if (count++ == 0)
127 		io_sendf ("* STATUS %s (", name);
128 	      else if (!space_sent)
129                 {
130                   space_sent = 1;
131 		  io_sendf (" ");
132                 }
133 
134 	      if (!fun (smbox))
135                 space_sent = 0;
136 	    }
137 
138 
139 	  if (count > 0)
140 	    io_sendf (")\n");
141 	  mu_mailbox_close (smbox);
142 	}
143       mu_mailbox_destroy (&smbox);
144     }
145   free (mailbox_name);
146 
147   if (status == 0)
148     {
149       if (count == 0)
150 	return io_completion_response (command, RESP_BAD,
151 	                               "Too few args (empty list)");
152       else if (err_msg)
153 	return io_completion_response (command, RESP_BAD, "%s", err_msg);
154       return io_completion_response (command, RESP_OK, "Completed");
155     }
156 
157   return io_completion_response (command, RESP_NO, "Error opening mailbox");
158 }
159 
160 static int
status_messages(mu_mailbox_t smbox)161 status_messages (mu_mailbox_t smbox)
162 {
163   size_t total = 0;
164   mu_mailbox_messages_count (smbox, &total);
165   io_sendf ("MESSAGES %lu", (unsigned long) total);
166   return 0;
167 }
168 
169 static int
status_recent(mu_mailbox_t smbox)170 status_recent (mu_mailbox_t smbox)
171 {
172   size_t recent = 0;
173   mu_mailbox_messages_recent (smbox, &recent);
174   io_sendf ("RECENT %lu", (unsigned long) recent);
175   return 0;
176 }
177 
178 static int
status_uidnext(mu_mailbox_t smbox)179 status_uidnext (mu_mailbox_t smbox)
180 {
181   size_t uidnext = 1;
182   mu_mailbox_uidnext (smbox, &uidnext);
183   io_sendf ("UIDNEXT %lu", (unsigned long) uidnext);
184   return 0;
185 }
186 
187 static int
status_uidvalidity(mu_mailbox_t smbox)188 status_uidvalidity (mu_mailbox_t smbox)
189 {
190   unsigned long uidvalidity = 0;
191   util_uidvalidity (smbox, &uidvalidity);
192   io_sendf ("UIDVALIDITY %lu", uidvalidity);
193   return 0;
194 }
195 
196 /* Note that unlike the unseen response code, which indicates the message
197    number of the first unseen message, the unseeen item in the response the
198    status command indicates the quantity of unseen messages.  */
199 static int
status_unseen(mu_mailbox_t smbox)200 status_unseen (mu_mailbox_t smbox)
201 {
202   size_t total = 0;
203   size_t unseen = 0;
204   size_t i;
205   mu_mailbox_messages_count (smbox, &total);
206   for (i = 1; i <= total; i++)
207     {
208       mu_message_t msg = NULL;
209       mu_attribute_t attr = NULL;
210       mu_mailbox_get_message (smbox, i, &msg);
211       mu_message_get_attribute (msg, &attr);
212       /* RFC 3501:
213            \Seen
214  	      Message has been read
215       */
216       if (!mu_attribute_is_read (attr))
217 	unseen++;
218     }
219   io_sendf ("UNSEEN %lu", (unsigned long) unseen);
220   return 0;
221 }
222