Blame view

imap4d/rename.c 6.04 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2001, 2005, 2007, 2008, 2009, 2010 Free Software
   Foundation, Inc.
Jakob Kaivo authored
4

5
   GNU Mailutils is free software; you can redistribute it and/or modify
Jakob Kaivo authored
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 3, or (at your option)
Jakob Kaivo authored
8 9
   any later version.

10
   GNU Mailutils is distributed in the hope that it will be useful,
Jakob Kaivo authored
11 12 13 14 15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
16
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
Jakob Kaivo authored
17 18 19

#include "imap4d.h"

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
int
make_interdir (const char *name, int delim, int perms)
{
  int rc;
  size_t i;
  struct mu_wordsplit ws;
  char *namebuf;
  size_t namelen = 0;
  char delimbuf[2];
  
  namebuf = malloc (strlen (name) + 1);
  if (!namebuf)
    imap4d_bye (ERR_NO_MEM);
  if (name[0] == '/')
    namebuf[namelen++] = name[0];

  delimbuf[0] = delim;
  delimbuf[1] = 0;
  ws.ws_delim = delimbuf;
  if (mu_wordsplit (name, &ws,
		    MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS|
		    MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
    {
      mu_error (_("cannot split line `%s': %s"), name,
		mu_wordsplit_strerror (&ws));
      free (namebuf);
      return 1;
    }

  rc = 0;
  for (i = 0; rc == 0 && i < ws.ws_wordc - 1; i++)
    {
      struct stat st;
      
      strcpy (namebuf + namelen, ws.ws_wordv[i]);
      namelen += strlen (ws.ws_wordv[i]);

      if (stat (namebuf, &st))
	{
	  if (errno == ENOENT)
	    {
	      if (mkdir (namebuf, perms))
		{
		  mu_error (_("cannot create directory %s: %s"), namebuf,
			    mu_strerror (errno));
		  rc = 1;
		}
	    }
	  else
	    {
	      mu_error (_("cannot stat file %s: %s"),
			namebuf, mu_strerror (errno));
	      rc = 1;
	    }
	}
      else if (!S_ISDIR (st.st_mode))
	{
	  mu_error (_("component %s is not a directory"), namebuf);
	  rc = 1;
	}
      namebuf[namelen++] = '/';
    }
  
  mu_wordsplit_free (&ws);
  free (namebuf);
  return rc;
}

Jakob Kaivo authored
88
/*
89 90 91 92 93 94 95 96 97 98 99 100 101
6.3.5.  RENAME Command

   Arguments:  existing mailbox name
               new mailbox name

   Responses:  no specific responses for this command

   Result:     OK - rename completed
               NO - rename failure: can't rename mailbox with that name,
                    can't rename to mailbox with that name
               BAD - command unknown or arguments invalid
*/  
/*
102 103
  FIXME: Renaming a mailbox we must change the UIDVALIDITY
  of the mailbox.  */
Jakob Kaivo authored
104 105

int
106
imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok)
Jakob Kaivo authored
107
{
108 109 110 111 112 113
  char *oldname;
  char *newname;
  int rc = RESP_OK;
  const char *msg = "Completed";
  struct stat newst;
  const char *delim = "/";
Sergey Poznyakoff authored
114 115
  int ns;
  
116
  if (imap4d_tokbuf_argc (tok) != 4)
117
    return io_completion_response (command, RESP_BAD, "Invalid arguments");
118 119 120
  
  oldname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
  newname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
121

122
  if (mu_c_strcasecmp (newname, "INBOX") == 0)
123
    return io_completion_response (command, RESP_NO, "Name Inbox is reservered");
124 125

  /* Allocates memory.  */
Sergey Poznyakoff authored
126
  newname = namespace_getfullpath (newname, delim, &ns);
Sergey Poznyakoff authored
127
  if (!newname)
128
    return io_completion_response (command, RESP_NO, "Permission denied");
129 130 131 132 133 134 135 136

  /* It is an error to attempt to rename from a mailbox name that already
     exist.  */
  if (stat (newname, &newst) == 0)
    {
      if (!S_ISDIR(newst.st_mode))
	{
	  free (newname);
137 138
	  return io_completion_response (command, RESP_NO,
	                                 "Already exist, delete first");
139 140 141
	}
    }

142 143 144 145 146 147
  if (make_interdir (newname, delim[0], MKDIR_PERMISSIONS))
    {
      free (newname);
      return io_completion_response (command, RESP_NO, "Cannot rename");
    }
  
148 149 150
  /* Renaming INBOX is permitted, and has special behavior.  It moves
     all messages in INBOX to a new mailbox with the given name,
     leaving INBOX empty.  */
151
  if (mu_c_strcasecmp (oldname, "INBOX") == 0)
152
    {
153 154
      mu_mailbox_t newmbox = NULL;
      mu_mailbox_t inbox = NULL;
155

Sergey Poznyakoff authored
156
      if (S_ISDIR (newst.st_mode))
157 158
	{
	  free (newname);
159 160
	  return io_completion_response (command, RESP_NO, 
	                                 "Cannot be a directory");
161
	}
162
      if (mu_mailbox_create (&newmbox, newname) != 0
Sergey Poznyakoff authored
163 164 165
	  || mu_mailbox_open (newmbox,
			      MU_STREAM_CREAT | MU_STREAM_RDWR
			        | mailbox_mode[ns]) != 0)
166 167
	{
	  free (newname);
168 169
	  return io_completion_response (command, RESP_NO,
	                                 "Cannot create new mailbox");
170 171
	}
      free (newname);
172

173 174
      if (mu_mailbox_create_default (&inbox, auth_data->name) == 0 &&
	  mu_mailbox_open (inbox, MU_STREAM_RDWR) == 0)
175
	{
176 177
	  size_t no;
	  size_t total = 0;
178
	  mu_mailbox_messages_count (inbox, &total);
179
	  for (no = 1; no <= total; no++)
180
	    {
181
	      mu_message_t message;
182
	      if (mu_mailbox_get_message (inbox, no, &message) == 0)
183
		{
184
		  mu_attribute_t attr = NULL;
185
		  mu_mailbox_append_message (newmbox, message);
186
		  mu_message_get_attribute (message, &attr);
187
		  mu_attribute_set_deleted (attr);
188 189
		}
	    }
190 191 192
	  mu_mailbox_expunge (inbox);
	  mu_mailbox_close (inbox);
	  mu_mailbox_destroy (&inbox);
193
	}
194 195
      mu_mailbox_close (newmbox);
      mu_mailbox_destroy (&newmbox);
196
      return io_completion_response (command, RESP_OK, "Rename successful");
197 198
    }

199
  oldname = namespace_getfullpath (oldname, delim, NULL);
200 201

  /* It must exist.  */
Sergey Poznyakoff authored
202 203
  /* FIXME: 1. What if odlname or newname is a remote mailbox?
            2. If newname is local and is in another namespace, its
204 205 206 207 208
  	       permissions must be fixed.
            3. All in all, it would perhaps be better to use the same
	       algorithm as for INBOX, and delete source mailbox afterwards.
  */
  if (!oldname)
209 210 211 212
    {
      rc = RESP_NO;
      msg = "Failed";
    }
213 214 215 216 217 218 219 220 221 222
  else
    {
      if (rename (oldname, newname) != 0)
	{
	  mu_diag_funcall (MU_DIAG_ERROR, "rename", oldname, errno);
          rc = RESP_NO;
          msg = "Failed";
	}
      free (oldname);
    }
223
  free (newname);
224
  return io_completion_response (command, rc, msg);
Jakob Kaivo authored
225
}