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 int
make_interdir(const char * name,int delim,int perms)20 make_interdir (const char *name, int delim, int perms)
21 {
22 int rc;
23 size_t i;
24 struct mu_wordsplit ws;
25 char *namebuf;
26 size_t namelen = 0;
27 char delimbuf[2];
28
29 namebuf = mu_alloc (strlen (name) + 1);
30 if (name[0] == '/')
31 namebuf[namelen++] = name[0];
32
33 delimbuf[0] = delim;
34 delimbuf[1] = 0;
35 ws.ws_delim = delimbuf;
36 if (mu_wordsplit (name, &ws,
37 MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS|
38 MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
39 {
40 mu_error (_("cannot split line `%s': %s"), name,
41 mu_wordsplit_strerror (&ws));
42 free (namebuf);
43 return 1;
44 }
45
46 rc = 0;
47 for (i = 0; rc == 0 && i < ws.ws_wordc - 1; i++)
48 {
49 struct stat st;
50
51 strcpy (namebuf + namelen, ws.ws_wordv[i]);
52 namelen += strlen (ws.ws_wordv[i]);
53
54 if (stat (namebuf, &st))
55 {
56 if (errno == ENOENT)
57 {
58 if (mkdir (namebuf, perms))
59 {
60 mu_error (_("cannot create directory %s: %s"), namebuf,
61 mu_strerror (errno));
62 rc = 1;
63 }
64 }
65 else
66 {
67 mu_error (_("cannot stat file %s: %s"),
68 namebuf, mu_strerror (errno));
69 rc = 1;
70 }
71 }
72 else if (!S_ISDIR (st.st_mode))
73 {
74 mu_error (_("component %s is not a directory"), namebuf);
75 rc = 1;
76 }
77 namebuf[namelen++] = '/';
78 }
79
80 mu_wordsplit_free (&ws);
81 free (namebuf);
82 return rc;
83 }
84
85 /*
86 6.3.5. RENAME Command
87
88 Arguments: existing mailbox name
89 new mailbox name
90
91 Responses: no specific responses for this command
92
93 Result: OK - rename completed
94 NO - rename failure: can't rename mailbox with that name,
95 can't rename to mailbox with that name
96 BAD - command unknown or arguments invalid
97 */
98 /*
99 FIXME: Renaming a mailbox we must change the UIDVALIDITY
100 of the mailbox. */
101
102 int
imap4d_rename(struct imap4d_session * session,struct imap4d_command * command,imap4d_tokbuf_t tok)103 imap4d_rename (struct imap4d_session *session,
104 struct imap4d_command *command, imap4d_tokbuf_t tok)
105 {
106 char *oldname;
107 char *newname;
108 int rc = RESP_OK;
109 const char *msg = "Completed";
110 struct stat newst;
111 int mode = 0;
112 mu_record_t newrec;
113
114 if (imap4d_tokbuf_argc (tok) != 4)
115 return io_completion_response (command, RESP_BAD, "Invalid arguments");
116
117 oldname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
118 newname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
119
120 if (mu_c_strcasecmp (newname, "INBOX") == 0)
121 return io_completion_response (command, RESP_NO, "Name Inbox is reservered");
122
123 /* Allocates memory. */
124 newname = namespace_get_name (newname, &newrec, &mode);
125 if (!newname)
126 return io_completion_response (command, RESP_NO, "Permission denied");
127
128 /* It is an error to attempt to rename from a mailbox name that does not
129 exist or to a mailbox name that already exists. */
130 if (stat (newname, &newst) == 0)
131 {
132 /* FIXME: What if it's a maildir?!? */
133 if (!S_ISDIR (newst.st_mode))
134 {
135 free (newname);
136 return io_completion_response (command, RESP_NO,
137 "Already exist, delete first");
138 }
139 }
140
141 if (make_interdir (newname, MU_HIERARCHY_DELIMITER, MKDIR_PERMISSIONS))
142 {
143 free (newname);
144 return io_completion_response (command, RESP_NO, "Cannot rename");
145 }
146
147 /* Renaming INBOX is permitted, and has special behavior. It moves
148 all messages in INBOX to a new mailbox with the given name,
149 leaving INBOX empty. */
150 if (mu_c_strcasecmp (oldname, "INBOX") == 0)
151 {
152 mu_mailbox_t newmbox = NULL;
153 mu_mailbox_t inbox = NULL;
154
155 if (S_ISDIR (newst.st_mode))
156 {
157 free (newname);
158 return io_completion_response (command, RESP_NO,
159 "Cannot be a directory");
160 }
161 if (mu_mailbox_create_from_record (&newmbox, newrec, newname) != 0
162 || mu_mailbox_open (newmbox,
163 MU_STREAM_CREAT | MU_STREAM_RDWR | mode) != 0)
164 {
165 free (newname);
166 return io_completion_response (command, RESP_NO,
167 "Cannot create new mailbox");
168 }
169 free (newname);
170
171 if (mu_mailbox_create_default (&inbox, auth_data->name) == 0 &&
172 mu_mailbox_open (inbox, MU_STREAM_RDWR) == 0)
173 {
174 size_t no;
175 size_t total = 0;
176 mu_mailbox_messages_count (inbox, &total);
177 for (no = 1; no <= total; no++)
178 {
179 mu_message_t message;
180 if (mu_mailbox_get_message (inbox, no, &message) == 0)
181 {
182 mu_attribute_t attr = NULL;
183
184 imap4d_enter_critical ();
185 mu_mailbox_append_message (newmbox, message);
186 imap4d_leave_critical ();
187 mu_message_get_attribute (message, &attr);
188 mu_attribute_set_deleted (attr);
189 }
190 }
191 imap4d_enter_critical ();
192 mu_mailbox_expunge (inbox);
193 imap4d_leave_critical ();
194 mu_mailbox_close (inbox);
195 mu_mailbox_destroy (&inbox);
196 }
197 mu_mailbox_close (newmbox);
198 mu_mailbox_destroy (&newmbox);
199 return io_completion_response (command, RESP_OK, "Rename successful");
200 }
201
202 oldname = namespace_get_name (oldname, NULL, NULL);
203
204 /* It must exist. */
205 /* FIXME: 1. What if odlname or newname is a remote mailbox?
206 2. If newname is local and is in another namespace, its
207 permissions must be fixed.
208 3. All in all, it would perhaps be better to use the same
209 algorithm as for INBOX, and delete source mailbox afterwards.
210 */
211 if (!oldname)
212 {
213 rc = RESP_NO;
214 msg = "Failed";
215 }
216 else
217 {
218 rc = mu_rename_file (oldname, newname, 0);
219 if (rc)
220 {
221 switch (rc)
222 {
223 case MU_ERR_REMOVE_SOURCE:
224 mu_error (_("failed to remove source mailbox after moving %s to %s"),
225 oldname, newname);
226 break;
227
228 case MU_ERR_RESTORE_META:
229 mu_error (_("failed to restore mailbox ownership/modes after moving %s to %s"),
230 oldname, newname);
231 break;
232
233 default:
234 mu_error (_("error renaming mailbox %s to %s: %s"),
235 oldname, newname, mu_strerror (rc));
236 }
237
238 rc = RESP_NO;
239 msg = "Failed";
240 }
241 free (oldname);
242 }
243
244 free (newname);
245 return io_completion_response (command, rc, "%s", msg);
246 }
247