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 static int select_flags;
20 
21 /* select          ::= "SELECT" SPACE mailbox  */
22 
23 int
imap4d_select(struct imap4d_session * session,struct imap4d_command * command,imap4d_tokbuf_t tok)24 imap4d_select (struct imap4d_session *session,
25                struct imap4d_command *command, imap4d_tokbuf_t tok)
26 {
27   if (imap4d_tokbuf_argc (tok) != 3)
28     return io_completion_response (command, RESP_BAD, "Invalid arguments");
29   return imap4d_select0 (command, imap4d_tokbuf_getarg (tok, IMAP4_ARG_1),
30 			 MU_STREAM_RDWR);
31 }
32 
33 /* This code is shared with EXAMINE.  */
34 int
imap4d_select0(struct imap4d_command * command,const char * mboxname,int flags)35 imap4d_select0 (struct imap4d_command *command, const char *mboxname,
36 		int flags)
37 {
38   int status;
39   char *mailbox_name;
40   mu_record_t record;
41 
42   /* FIXME: Check state.  */
43 
44   /* Even if a mailbox is selected, a SELECT EXAMINE or LOGOUT
45      command MAY be issued without previously issuing a CLOSE command.
46      The SELECT, EXAMINE, and LOGUT commands implictly close the
47      currently selected mailbox without doing an expunge.  */
48   if (mbox)
49     {
50       imap4d_enter_critical ();
51       mu_mailbox_sync (mbox);
52       mu_mailbox_close (mbox);
53       manlock_unlock (mbox);
54       imap4d_leave_critical ();
55       mu_mailbox_destroy (&mbox);
56       /* Destroy the old uid table.  */
57       imap4d_sync ();
58     }
59 
60   if (mu_c_strcasecmp (mboxname, "INBOX") == 0)
61     flags |= MU_STREAM_CREAT;
62   mailbox_name = namespace_get_name (mboxname, &record, NULL);
63 
64   if (!mailbox_name)
65     return io_completion_response (command, RESP_NO, "Couldn't open mailbox");
66 
67   if (flags & MU_STREAM_WRITE)
68     {
69       status = manlock_open_mailbox_from_record (&mbox, record,
70 						 mailbox_name, flags);
71       if (status)
72 	flags &= ~MU_STREAM_WRITE;
73     }
74 
75   if (!(flags & MU_STREAM_WRITE))
76     {
77       status = mu_mailbox_create_from_record (&mbox, record, mailbox_name);
78 
79       if (status)
80 	mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_create_from_record",
81 			 mailbox_name,
82 			 status);
83       else
84 	{
85 	  status = mu_mailbox_open (mbox, flags);
86 	  if (status)
87 	    {
88 	      mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_open",
89 			       mailbox_name,
90 			       status);
91 	      mu_mailbox_destroy (&mbox);
92 	    }
93 	}
94     }
95 
96   if (status == 0)
97     {
98       select_flags = flags;
99       state = STATE_SEL;
100 
101       imap4d_set_observer (mbox);
102 
103       if ((status = imap4d_select_status ()) == 0)
104 	{
105 	  free (mailbox_name);
106 	  /* Need to set the state explicitely for select.  */
107 	  return io_sendf ("%s OK [%s] %s Completed\n", command->tag,
108 			   ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR) ?
109 			   "READ-WRITE" : "READ-ONLY", command->name);
110 	}
111     }
112 
113   mu_mailbox_destroy (&mbox);
114   status = io_completion_response (command, RESP_NO, "Could not open %s: %s",
115 			mboxname, mu_strerror (status));
116   free (mailbox_name);
117   return status;
118 }
119 
120 /* The code is shared between select and noop */
121 int
imap4d_select_status()122 imap4d_select_status ()
123 {
124   const char *mflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
125   unsigned long uidvalidity = 0;
126   size_t count = 0, recent = 0, unseen = 0, uidnext = 0;
127   int status = 0;
128 
129   if (state != STATE_SEL)
130     return 0; /* FIXME: this should be something! */
131 
132   if ((status = util_uidvalidity (mbox, &uidvalidity))
133       || (status = mu_mailbox_uidnext (mbox, &uidnext))
134       || (status = mu_mailbox_messages_count (mbox, &count))
135       || (status = mu_mailbox_messages_recent (mbox, &recent))
136       || (status = mu_mailbox_message_unseen (mbox, &unseen)))
137     return status;
138 
139   /* This outputs EXISTS and RECENT responses */
140   imap4d_sync();
141   io_untagged_response (RESP_OK, "[UIDVALIDITY %lu] UID valididy status",
142                            uidvalidity);
143   io_untagged_response (RESP_OK, "[UIDNEXT %lu] Predicted next uid",
144 	                   (unsigned long) uidnext);
145   if (unseen)
146     io_untagged_response (RESP_OK, "[UNSEEN %lu] first unseen message",
147 	                     (unsigned long) unseen);
148   io_untagged_response (RESP_NONE, "FLAGS (%s)", mflags);
149   /* FIXME:
150      - '\*' can be supported if we use the attribute_set userflag()
151      - Answered is still not set in the mailbox code.  */
152   if (!(select_flags & MU_STREAM_WRITE))
153     io_untagged_response (RESP_OK, "[PERMANENTFLAGS ()] No permanent flags");
154   else
155     io_untagged_response (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags",
156                           mflags);
157 
158   return 0;
159 }
160 
161