extensions/net.sf.basedb.ftp/trunk/src/net/sf/basedb/clients/ftp/BaseFtpFile.java

Code
Comments
Other
Rev Date Author Line
716 02 Jun 08 nicklas 1 /**
716 02 Jun 08 nicklas 2   $Id $
716 02 Jun 08 nicklas 3
716 02 Jun 08 nicklas 4   Copyright (C) 2008 Nicklas Nordborg
716 02 Jun 08 nicklas 5
1381 15 Aug 11 martin 6   This file is part of the FTP Server extension for BASE.
716 02 Jun 08 nicklas 7   Available at http://baseplugins.thep.lu.se/
1381 15 Aug 11 martin 8   BASE main site: http://base.thep.lu.se/
1381 15 Aug 11 martin 9   -----------------------------------------------------------
1381 15 Aug 11 martin 10   
1381 15 Aug 11 martin 11   This is free software; you can redistribute it and/or
716 02 Jun 08 nicklas 12   modify it under the terms of the GNU General Public License
1381 15 Aug 11 martin 13   as published by the Free Software Foundation; either version 3
716 02 Jun 08 nicklas 14   of the License, or (at your option) any later version.
1381 15 Aug 11 martin 15   
1381 15 Aug 11 martin 16   The software is distributed in the hope that it will be useful,
716 02 Jun 08 nicklas 17   but WITHOUT ANY WARRANTY; without even the implied warranty of
716 02 Jun 08 nicklas 18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
716 02 Jun 08 nicklas 19   GNU General Public License for more details.
1381 15 Aug 11 martin 20   
1381 15 Aug 11 martin 21   You should have received a copy of the GNU General Public License
1381 15 Aug 11 martin 22   along with BASE. If not, see <http://www.gnu.org/licenses/>.
716 02 Jun 08 nicklas 23
716 02 Jun 08 nicklas 24 */
714 30 May 08 nicklas 25 package net.sf.basedb.clients.ftp;
714 30 May 08 nicklas 26
714 30 May 08 nicklas 27 import java.io.IOException;
714 30 May 08 nicklas 28 import java.io.InputStream;
714 30 May 08 nicklas 29 import java.io.OutputStream;
714 30 May 08 nicklas 30 import java.util.ArrayList;
939 21 Jan 09 nicklas 31 import java.util.Date;
714 30 May 08 nicklas 32 import java.util.List;
714 30 May 08 nicklas 33
714 30 May 08 nicklas 34 import net.sf.basedb.core.DbControl;
714 30 May 08 nicklas 35 import net.sf.basedb.core.Directory;
714 30 May 08 nicklas 36 import net.sf.basedb.core.File;
714 30 May 08 nicklas 37 import net.sf.basedb.core.Include;
714 30 May 08 nicklas 38 import net.sf.basedb.core.Item;
714 30 May 08 nicklas 39 import net.sf.basedb.core.ItemQuery;
1225 05 May 10 nicklas 40 import net.sf.basedb.core.Location;
714 30 May 08 nicklas 41 import net.sf.basedb.core.OwnedItem;
714 30 May 08 nicklas 42 import net.sf.basedb.core.Path;
714 30 May 08 nicklas 43 import net.sf.basedb.core.Permission;
714 30 May 08 nicklas 44 import net.sf.basedb.core.query.Hql;
714 30 May 08 nicklas 45 import net.sf.basedb.core.query.Orders;
714 30 May 08 nicklas 46
915 11 Dec 08 nicklas 47 import org.apache.ftpserver.ftplet.FtpFile;
716 02 Jun 08 nicklas 48 import org.slf4j.Logger;
716 02 Jun 08 nicklas 49 import org.slf4j.LoggerFactory;
714 30 May 08 nicklas 50
714 30 May 08 nicklas 51 /**
714 30 May 08 nicklas 52   A file object can represent three different things:
714 30 May 08 nicklas 53   <ol>
714 30 May 08 nicklas 54   <li>An existing file on the BASE server (LIST, RETR)
714 30 May 08 nicklas 55   <li>An existing directory on the BASE server (CWD, LIST)
714 30 May 08 nicklas 56   <li>An path name that doesn't exists and may be created as either 
714 30 May 08 nicklas 57     a directory (MKDIR) or file (STOR)
714 30 May 08 nicklas 58   </ol>
716 02 Jun 08 nicklas 59   @author Nicklas
716 02 Jun 08 nicklas 60   @version 1.0
714 30 May 08 nicklas 61 */
915 11 Dec 08 nicklas 62 public class BaseFtpFile 
915 11 Dec 08 nicklas 63   implements FtpFile 
714 30 May 08 nicklas 64 {
915 11 Dec 08 nicklas 65   private static final Logger log = LoggerFactory.getLogger(BaseFtpFile.class);
714 30 May 08 nicklas 66
714 30 May 08 nicklas 67   private BaseUser user;
714 30 May 08 nicklas 68   private File file;
714 30 May 08 nicklas 69   private Directory directory;
714 30 May 08 nicklas 70   private String owner;
714 30 May 08 nicklas 71   private String name;
714 30 May 08 nicklas 72   private String path;
714 30 May 08 nicklas 73   
714 30 May 08 nicklas 74   /**
714 30 May 08 nicklas 75     Create a file object that represents an existing file on
714 30 May 08 nicklas 76     the BASE server.
714 30 May 08 nicklas 77   */
915 11 Dec 08 nicklas 78   public BaseFtpFile(BaseUser user, File file)
714 30 May 08 nicklas 79   {
714 30 May 08 nicklas 80     this.user = user;
714 30 May 08 nicklas 81     this.file = file;
714 30 May 08 nicklas 82     this.name = file.getName();
714 30 May 08 nicklas 83     this.owner = getOwner(file);
714 30 May 08 nicklas 84     this.path = file.getPath().toString();
714 30 May 08 nicklas 85   }
714 30 May 08 nicklas 86   
714 30 May 08 nicklas 87   /**
714 30 May 08 nicklas 88     Create a file object that represents an existing directory on
714 30 May 08 nicklas 89     the BASE server.
714 30 May 08 nicklas 90   */
915 11 Dec 08 nicklas 91   public BaseFtpFile(BaseUser user, Directory directory)
714 30 May 08 nicklas 92   {
714 30 May 08 nicklas 93     this.user = user;
714 30 May 08 nicklas 94     this.directory = directory;
714 30 May 08 nicklas 95     this.name = directory.getName();
714 30 May 08 nicklas 96     this.owner = getOwner(directory);
714 30 May 08 nicklas 97     this.path = directory.getPath().toString();
714 30 May 08 nicklas 98   }
714 30 May 08 nicklas 99   
714 30 May 08 nicklas 100   /**
714 30 May 08 nicklas 101     Create a file object that represents an non-existing file or
714 30 May 08 nicklas 102     directory.
714 30 May 08 nicklas 103   */
915 11 Dec 08 nicklas 104   public BaseFtpFile(BaseUser user, String path)
714 30 May 08 nicklas 105   {
714 30 May 08 nicklas 106     this.user = user;
714 30 May 08 nicklas 107     this.path = path;
714 30 May 08 nicklas 108     this.name = path.substring(path.lastIndexOf('/')+1);
714 30 May 08 nicklas 109     this.owner = user.getName();
714 30 May 08 nicklas 110   }
714 30 May 08 nicklas 111   
714 30 May 08 nicklas 112   /*
915 11 Dec 08 nicklas 113     From the FtpFile interface
714 30 May 08 nicklas 114     ------------------------------------------
714 30 May 08 nicklas 115   */
714 30 May 08 nicklas 116   @Override
915 11 Dec 08 nicklas 117   public String getAbsolutePath() 
714 30 May 08 nicklas 118   {
714 30 May 08 nicklas 119     return path;
714 30 May 08 nicklas 120   }
714 30 May 08 nicklas 121   
714 30 May 08 nicklas 122   @Override
915 11 Dec 08 nicklas 123   public String getName() 
714 30 May 08 nicklas 124   {
714 30 May 08 nicklas 125     return name;
714 30 May 08 nicklas 126   }
714 30 May 08 nicklas 127
714 30 May 08 nicklas 128   /**
714 30 May 08 nicklas 129     Check if this represents an existing file or directory on
714 30 May 08 nicklas 130     the BASE server.
714 30 May 08 nicklas 131   */
714 30 May 08 nicklas 132   @Override
714 30 May 08 nicklas 133   public boolean doesExist() 
714 30 May 08 nicklas 134   {
714 30 May 08 nicklas 135     return file != null || directory != null;
714 30 May 08 nicklas 136   }
714 30 May 08 nicklas 137   
714 30 May 08 nicklas 138   /**
714 30 May 08 nicklas 139     Checks if this represents an existing file on the
714 30 May 08 nicklas 140     BASE server.
714 30 May 08 nicklas 141   */
714 30 May 08 nicklas 142   @Override
714 30 May 08 nicklas 143   public boolean isFile() 
714 30 May 08 nicklas 144   {
714 30 May 08 nicklas 145     return file != null;
714 30 May 08 nicklas 146   }
714 30 May 08 nicklas 147   
714 30 May 08 nicklas 148   /**
714 30 May 08 nicklas 149     Checks if this represents an existing directory on the
714 30 May 08 nicklas 150     BASE server.
714 30 May 08 nicklas 151   */
714 30 May 08 nicklas 152   @Override
714 30 May 08 nicklas 153   public boolean isDirectory() 
714 30 May 08 nicklas 154   {
714 30 May 08 nicklas 155     return directory != null;
714 30 May 08 nicklas 156   }
714 30 May 08 nicklas 157   
714 30 May 08 nicklas 158   /**
714 30 May 08 nicklas 159     Existing files or directories that have been marked for removal
714 30 May 08 nicklas 160     are hidden.
714 30 May 08 nicklas 161   */
714 30 May 08 nicklas 162   @Override
714 30 May 08 nicklas 163   public boolean isHidden() 
714 30 May 08 nicklas 164   {
714 30 May 08 nicklas 165     return file != null && file.isRemoved() || directory != null && directory.isRemoved();
714 30 May 08 nicklas 166   }
714 30 May 08 nicklas 167
714 30 May 08 nicklas 168   /**
714 30 May 08 nicklas 169     Get the size of the file if it exists in BASE, 
714 30 May 08 nicklas 170     or 0 otherwise.
714 30 May 08 nicklas 171   */
714 30 May 08 nicklas 172   @Override
714 30 May 08 nicklas 173   public long getSize() 
714 30 May 08 nicklas 174   {
1225 05 May 10 nicklas 175     return file == null || file.getSize() < 0 ? 0 : file.getSize();
714 30 May 08 nicklas 176   }
714 30 May 08 nicklas 177
714 30 May 08 nicklas 178   /**
716 02 Jun 08 nicklas 179     3 for directories, 1 for files (TODO - I don't understand why, but this is how
716 02 Jun 08 nicklas 180     the {@link NativeFileObject} object does it).
714 30 May 08 nicklas 181   */
714 30 May 08 nicklas 182   @Override
714 30 May 08 nicklas 183   public int getLinkCount() 
714 30 May 08 nicklas 184   {
714 30 May 08 nicklas 185     return directory == null ? 1 : 3;
714 30 May 08 nicklas 186   }
714 30 May 08 nicklas 187
714 30 May 08 nicklas 188   @Override
714 30 May 08 nicklas 189   public String getOwnerName() 
714 30 May 08 nicklas 190   {
714 30 May 08 nicklas 191     return owner;
714 30 May 08 nicklas 192   }
714 30 May 08 nicklas 193
714 30 May 08 nicklas 194   /**
714 30 May 08 nicklas 195     Always an empty string.
714 30 May 08 nicklas 196   */
714 30 May 08 nicklas 197   @Override
714 30 May 08 nicklas 198   public String getGroupName() 
714 30 May 08 nicklas 199   {
714 30 May 08 nicklas 200     return "";
714 30 May 08 nicklas 201   }
714 30 May 08 nicklas 202
714 30 May 08 nicklas 203   /**
716 02 Jun 08 nicklas 204     If it is a file, get it's last update time. 
716 02 Jun 08 nicklas 205     If it is a directory, return the current time.
716 02 Jun 08 nicklas 206     Otherwise, return 0.
714 30 May 08 nicklas 207   */
714 30 May 08 nicklas 208   @Override
714 30 May 08 nicklas 209   public long getLastModified()
714 30 May 08 nicklas 210   {
939 21 Jan 09 nicklas 211     Date time = null;
939 21 Jan 09 nicklas 212     if (file != null) time = file.getLastUpdate();
939 21 Jan 09 nicklas 213     return time == null ? System.currentTimeMillis() : time.getTime();
714 30 May 08 nicklas 214   }
714 30 May 08 nicklas 215   
714 30 May 08 nicklas 216   /**
917 12 Dec 08 nicklas 217     Ignored since BASE doesn't allow us to change this.
985 11 Mar 09 nicklas 218     @return Always "false"
917 12 Dec 08 nicklas 219   */
917 12 Dec 08 nicklas 220   @Override
985 11 Mar 09 nicklas 221   public boolean setLastModified(long lastModified) 
985 11 Mar 09 nicklas 222   {
985 11 Mar 09 nicklas 223     return false;
985 11 Mar 09 nicklas 224   }
917 12 Dec 08 nicklas 225   
917 12 Dec 08 nicklas 226   /**
714 30 May 08 nicklas 227     Always true.
714 30 May 08 nicklas 228   */
714 30 May 08 nicklas 229   @Override
915 11 Dec 08 nicklas 230   public boolean isReadable() 
714 30 May 08 nicklas 231   {
714 30 May 08 nicklas 232     return true;
714 30 May 08 nicklas 233   }
714 30 May 08 nicklas 234
714 30 May 08 nicklas 235   /**
714 30 May 08 nicklas 236     Check the existing file or directory for delete permission.
714 30 May 08 nicklas 237     FALSE in other cases.
714 30 May 08 nicklas 238   */
714 30 May 08 nicklas 239   @Override
915 11 Dec 08 nicklas 240   public boolean isRemovable() 
714 30 May 08 nicklas 241   {
714 30 May 08 nicklas 242     boolean permission = false;
714 30 May 08 nicklas 243     if (file != null)
714 30 May 08 nicklas 244     {
714 30 May 08 nicklas 245       permission = file.hasPermission(Permission.DELETE);
714 30 May 08 nicklas 246     }
714 30 May 08 nicklas 247     else if (directory != null)
714 30 May 08 nicklas 248     {
714 30 May 08 nicklas 249       permission = directory.hasPermission(Permission.DELETE);
714 30 May 08 nicklas 250     }
714 30 May 08 nicklas 251     return permission;
714 30 May 08 nicklas 252   }
714 30 May 08 nicklas 253
714 30 May 08 nicklas 254   /**
714 30 May 08 nicklas 255     Check the existing file or directory for write permission.
714 30 May 08 nicklas 256     TRUE in other cases.
714 30 May 08 nicklas 257   */
714 30 May 08 nicklas 258   @Override
915 11 Dec 08 nicklas 259   public boolean isWritable() 
714 30 May 08 nicklas 260   {
714 30 May 08 nicklas 261     boolean permission = true;
714 30 May 08 nicklas 262     if (file != null)
714 30 May 08 nicklas 263     {
1225 05 May 10 nicklas 264       permission = file.hasPermission(Permission.WRITE) && 
1225 05 May 10 nicklas 265         file.getLocation() != Location.EXTERNAL;
714 30 May 08 nicklas 266     }
714 30 May 08 nicklas 267     else if (directory != null)
714 30 May 08 nicklas 268     {
714 30 May 08 nicklas 269       permission = directory.hasPermission(Permission.WRITE);
714 30 May 08 nicklas 270     }
714 30 May 08 nicklas 271     return permission;
714 30 May 08 nicklas 272   }
714 30 May 08 nicklas 273   
714 30 May 08 nicklas 274   /**
714 30 May 08 nicklas 275     If this represents an existing directory, returns all subdirectories and
714 30 May 08 nicklas 276     files inside it. Otherwise null is returned.
714 30 May 08 nicklas 277   */
714 30 May 08 nicklas 278   @Override
915 11 Dec 08 nicklas 279   public List<FtpFile> listFiles() 
714 30 May 08 nicklas 280   {
716 02 Jun 08 nicklas 281     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 282     {
716 02 Jun 08 nicklas 283       log.debug("List files");
716 02 Jun 08 nicklas 284       log.debug("   dir=" + this);
716 02 Jun 08 nicklas 285     }
714 30 May 08 nicklas 286     if (directory == null) return null;
714 30 May 08 nicklas 287     
915 11 Dec 08 nicklas 288     List<FtpFile> all = new ArrayList<FtpFile>();
714 30 May 08 nicklas 289     DbControl dc = user.sc.newDbControl();
714 30 May 08 nicklas 290     try
714 30 May 08 nicklas 291     {
714 30 May 08 nicklas 292       // Add subdirectories
714 30 May 08 nicklas 293       ItemQuery<Directory> dirQuery = directory.getSubDirectories();
714 30 May 08 nicklas 294       dirQuery.order(Orders.asc(Hql.property("name")));
714 30 May 08 nicklas 295       dirQuery.include(Include.ALL);
714 30 May 08 nicklas 296       for (Directory dir : dirQuery.list(dc))
714 30 May 08 nicklas 297       {
915 11 Dec 08 nicklas 298         all.add(new BaseFtpFile(user, dir));
714 30 May 08 nicklas 299       }
714 30 May 08 nicklas 300       // Add files
714 30 May 08 nicklas 301       ItemQuery<File> fileQuery = directory.getFiles();
714 30 May 08 nicklas 302       fileQuery.order(Orders.asc(Hql.property("name")));
714 30 May 08 nicklas 303       fileQuery.include(Include.ALL);
714 30 May 08 nicklas 304       for (File file : fileQuery.list(dc))
714 30 May 08 nicklas 305       {
915 11 Dec 08 nicklas 306         all.add(new BaseFtpFile(user, file));
714 30 May 08 nicklas 307       }
714 30 May 08 nicklas 308     }
716 02 Jun 08 nicklas 309     catch (RuntimeException ex)
716 02 Jun 08 nicklas 310     {
716 02 Jun 08 nicklas 311       log.error("Could not list files in directory: " + this, ex);
716 02 Jun 08 nicklas 312       throw ex;
716 02 Jun 08 nicklas 313     }
714 30 May 08 nicklas 314     finally
714 30 May 08 nicklas 315     {
714 30 May 08 nicklas 316       if (dc != null) dc.close();
714 30 May 08 nicklas 317     }
716 02 Jun 08 nicklas 318     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 319     {
716 02 Jun 08 nicklas 320       log.debug("List files ok: size=" + all.size());
716 02 Jun 08 nicklas 321     }
915 11 Dec 08 nicklas 322     return all;
714 30 May 08 nicklas 323   }
714 30 May 08 nicklas 324   
714 30 May 08 nicklas 325   /**
714 30 May 08 nicklas 326     Try to create the directory if this file object represents
714 30 May 08 nicklas 327     a non-existing item. 
714 30 May 08 nicklas 328   */
714 30 May 08 nicklas 329   @Override
714 30 May 08 nicklas 330   public boolean mkdir() 
714 30 May 08 nicklas 331   {
716 02 Jun 08 nicklas 332     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 333     {
716 02 Jun 08 nicklas 334       log.debug("Make directory");
716 02 Jun 08 nicklas 335       log.debug("   dir=" + this);
716 02 Jun 08 nicklas 336     }
714 30 May 08 nicklas 337     if (file != null || directory != null) return false;
714 30 May 08 nicklas 338     if (!user.sc.hasPermission(Permission.CREATE, Item.DIRECTORY)) return false;
714 30 May 08 nicklas 339     
714 30 May 08 nicklas 340     Path p = new Path(path, Path.Type.DIRECTORY);
714 30 May 08 nicklas 341     DbControl dc = user.sc.newDbControl();
714 30 May 08 nicklas 342     try
714 30 May 08 nicklas 343     {
714 30 May 08 nicklas 344       directory = Directory.getNew(dc, p);
714 30 May 08 nicklas 345       dc.commit();
714 30 May 08 nicklas 346     }
716 02 Jun 08 nicklas 347     catch (RuntimeException ex)
716 02 Jun 08 nicklas 348     {
716 02 Jun 08 nicklas 349       log.error("Could not create directory: " + this, ex);
716 02 Jun 08 nicklas 350       throw ex;
716 02 Jun 08 nicklas 351     }
714 30 May 08 nicklas 352     finally
714 30 May 08 nicklas 353     {
714 30 May 08 nicklas 354       if (dc != null) dc.close();
714 30 May 08 nicklas 355     }
716 02 Jun 08 nicklas 356     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 357     {
716 02 Jun 08 nicklas 358       log.debug("Make directory ok: " + this);
716 02 Jun 08 nicklas 359     }
714 30 May 08 nicklas 360     return true;
714 30 May 08 nicklas 361   }
714 30 May 08 nicklas 362   
714 30 May 08 nicklas 363   /**
714 30 May 08 nicklas 364     If this file objects represents an existing file or directory, try
714 30 May 08 nicklas 365     to delete it. Otherwise do nothing.
714 30 May 08 nicklas 366   */  
714 30 May 08 nicklas 367   @Override
714 30 May 08 nicklas 368   public boolean delete() 
714 30 May 08 nicklas 369   {
716 02 Jun 08 nicklas 370     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 371     {
716 02 Jun 08 nicklas 372       log.debug("Delete");
716 02 Jun 08 nicklas 373       log.debug("   file/dir=" + this);
716 02 Jun 08 nicklas 374     }
915 11 Dec 08 nicklas 375     if (!isRemovable()) return false;
714 30 May 08 nicklas 376     
714 30 May 08 nicklas 377     DbControl dc = user.sc.newDbControl();
714 30 May 08 nicklas 378     try
714 30 May 08 nicklas 379     {
714 30 May 08 nicklas 380       if (file != null) 
714 30 May 08 nicklas 381       {
714 30 May 08 nicklas 382         file = File.getById(dc, file.getId());
714 30 May 08 nicklas 383         dc.deleteItem(file);
714 30 May 08 nicklas 384       }
714 30 May 08 nicklas 385       else if (directory != null) 
714 30 May 08 nicklas 386       {
714 30 May 08 nicklas 387         directory = Directory.getById(dc, directory.getId());
714 30 May 08 nicklas 388         dc.deleteItem(directory);
714 30 May 08 nicklas 389       }
714 30 May 08 nicklas 390       dc.commit();
716 02 Jun 08 nicklas 391       file = null;
716 02 Jun 08 nicklas 392       directory = null;
714 30 May 08 nicklas 393     }
716 02 Jun 08 nicklas 394     catch (RuntimeException ex)
716 02 Jun 08 nicklas 395     {
716 02 Jun 08 nicklas 396       log.error("Could not delete file/directory: " + this, ex);
716 02 Jun 08 nicklas 397       throw ex;
716 02 Jun 08 nicklas 398     }
714 30 May 08 nicklas 399     finally
714 30 May 08 nicklas 400     {
714 30 May 08 nicklas 401       if (dc != null) dc.close();
714 30 May 08 nicklas 402     }
716 02 Jun 08 nicklas 403     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 404     {
716 02 Jun 08 nicklas 405       log.debug("Delete ok: " + this);
716 02 Jun 08 nicklas 406     }
714 30 May 08 nicklas 407     return true;
714 30 May 08 nicklas 408   }
714 30 May 08 nicklas 409   
714 30 May 08 nicklas 410   /**
714 30 May 08 nicklas 411     Move this existing file or directory to a non-existing location.
714 30 May 08 nicklas 412   */
714 30 May 08 nicklas 413   @Override
915 11 Dec 08 nicklas 414   public boolean move(FtpFile dest)
714 30 May 08 nicklas 415   {
716 02 Jun 08 nicklas 416     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 417     {
716 02 Jun 08 nicklas 418       log.debug("Move");
716 02 Jun 08 nicklas 419       log.debug("   this=" + this);
716 02 Jun 08 nicklas 420       log.debug("   dest=" + dest);
716 02 Jun 08 nicklas 421     }
714 30 May 08 nicklas 422     if (!doesExist() || dest.doesExist()) return false;
714 30 May 08 nicklas 423     DbControl dc = user.sc.newDbControl();
714 30 May 08 nicklas 424     try
714 30 May 08 nicklas 425     {
714 30 May 08 nicklas 426       Directory newParentDir = null;
716 02 Jun 08 nicklas 427       // Do we need to change the parent directory?
716 02 Jun 08 nicklas 428       // If we don't have to we should not call File.setDirectory since it
716 02 Jun 08 nicklas 429       // may cause a PermissionDeniedException
714 30 May 08 nicklas 430       if (!hasSameParentDirectory(this, dest))
714 30 May 08 nicklas 431       {
915 11 Dec 08 nicklas 432         Path to = new Path(dest.getAbsolutePath(), isDirectory() ? Path.Type.DIRECTORY : Path.Type.FILE);
714 30 May 08 nicklas 433         newParentDir = Directory.getByPath(dc, to.getParent());
714 30 May 08 nicklas 434       }
716 02 Jun 08 nicklas 435       if (log.isDebugEnabled())
716 02 Jun 08 nicklas 436       {
716 02 Jun 08 nicklas 437         log.debug("   new parent directory=" + newParentDir);
716 02 Jun 08 nicklas 438       }
714 30 May 08 nicklas 439       if (file != null)
714 30 May 08 nicklas 440       {
714 30 May 08 nicklas 441         file = File.getById(dc, file.getId());
714 30 May 08 nicklas 442         if (newParentDir != null) file.setDirectory(newParentDir);
915 11 Dec 08 nicklas 443         file.setName(dest.getName());
714 30 May 08 nicklas 444       }
714 30 May 08 nicklas 445       else if (directory != null)
714 30 May 08 nicklas 446       {
714 30 May 08 nicklas 447         directory = Directory.getById(dc, directory.getId());
714 30 May 08 nicklas 448         if (newParentDir != null) directory.setParent(newParentDir);
915 11 Dec 08 nicklas 449         directory.setName(dest.getName());
714 30 May 08 nicklas 450       }
714 30 May 08 nicklas 451       dc.commit();
714 30 May 08 nicklas 452     }
716 02 Jun 08 nicklas 453     catch (RuntimeException ex)
716 02 Jun 08 nicklas 454     {
716 02 Jun 08 nicklas 455       log.error("Could not move: " + this + " --> " + dest, ex);
716 02 Jun 08 nicklas 456       throw ex;
716 02 Jun 08 nicklas 457     }
714 30 May 08 nicklas 458     finally
714 30 May 08 nicklas 459     {
714 30 May 08 nicklas 460       if (dc != null) dc.close();
714 30 May 08 nicklas 461     }
716 02 Jun 08 nicklas 462     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 463     {
716 02 Jun 08 nicklas 464       log.debug("Move ok: " + this + " --> " + dest);
716 02 Jun 08 nicklas 465     }
714 30 May 08 nicklas 466     return true;
714 30 May 08 nicklas 467   }
714 30 May 08 nicklas 468
714 30 May 08 nicklas 469   
714 30 May 08 nicklas 470   @Override
714 30 May 08 nicklas 471   public InputStream createInputStream(long offset) 
714 30 May 08 nicklas 472     throws IOException 
714 30 May 08 nicklas 473   {
716 02 Jun 08 nicklas 474     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 475     {
716 02 Jun 08 nicklas 476       log.debug("Reading from file " + this + "[offset="+offset + "]");
716 02 Jun 08 nicklas 477     }
714 30 May 08 nicklas 478     if (file == null)
714 30 May 08 nicklas 479     {
714 30 May 08 nicklas 480       throw new IOException("Not a file: " + this);
714 30 May 08 nicklas 481     }
2569 11 Aug 14 nicklas 482     DbControl dc = null;
2569 11 Aug 14 nicklas 483     InputStream in = null;
2569 11 Aug 14 nicklas 484     try
2569 11 Aug 14 nicklas 485     {
2569 11 Aug 14 nicklas 486       if (file.getLocation() == Location.EXTERNAL)
2569 11 Aug 14 nicklas 487       {
2569 11 Aug 14 nicklas 488         dc = user.sc.newDbControl();
2569 11 Aug 14 nicklas 489         dc.reattachItem(file, false);
2569 11 Aug 14 nicklas 490       }
2569 11 Aug 14 nicklas 491       in = file.getDownloadStream(offset);
2569 11 Aug 14 nicklas 492     }
2569 11 Aug 14 nicklas 493     finally
2569 11 Aug 14 nicklas 494     {
2569 11 Aug 14 nicklas 495       if (dc != null) dc.close();
2569 11 Aug 14 nicklas 496     }
2569 11 Aug 14 nicklas 497     return in;
714 30 May 08 nicklas 498   }
714 30 May 08 nicklas 499   
714 30 May 08 nicklas 500   /**
714 30 May 08 nicklas 501     If this object represents an existing directory, return null.
714 30 May 08 nicklas 502     If this object represents an existing file, return an upload
714 30 May 08 nicklas 503     stream to it. If this object represents a non-existing item,
714 30 May 08 nicklas 504     try to create the file.
714 30 May 08 nicklas 505   */
714 30 May 08 nicklas 506   @Override
714 30 May 08 nicklas 507   public OutputStream createOutputStream(long offset) 
714 30 May 08 nicklas 508     throws IOException 
714 30 May 08 nicklas 509   {
716 02 Jun 08 nicklas 510     if (log.isDebugEnabled())
716 02 Jun 08 nicklas 511     {
716 02 Jun 08 nicklas 512       log.debug("Writing to file " + this + "[offset="+offset + "]");
716 02 Jun 08 nicklas 513     }
714 30 May 08 nicklas 514     if (directory != null) return null;
714 30 May 08 nicklas 515     
714 30 May 08 nicklas 516     OutputStream upload = null;
714 30 May 08 nicklas 517     DbControl dc = user.sc.newDbControl();
714 30 May 08 nicklas 518     try
714 30 May 08 nicklas 519     {
714 30 May 08 nicklas 520       if (file != null)
714 30 May 08 nicklas 521       {
714 30 May 08 nicklas 522         // Upload to an existing file
1225 05 May 10 nicklas 523         dc.reattachItem(file, false);
714 30 May 08 nicklas 524       }
714 30 May 08 nicklas 525       else
714 30 May 08 nicklas 526       {
714 30 May 08 nicklas 527         // Upload to a new file; always ignore offset
915 11 Dec 08 nicklas 528         file = File.getByPath(dc, new Path(getAbsolutePath(), Path.Type.FILE), true);
714 30 May 08 nicklas 529         dc.saveItem(file);
714 30 May 08 nicklas 530         offset = 0;
714 30 May 08 nicklas 531       }
716 02 Jun 08 nicklas 532       
716 02 Jun 08 nicklas 533       if (log.isDebugEnabled())
716 02 Jun 08 nicklas 534       {
716 02 Jun 08 nicklas 535         log.debug("Uploading to BASE file: " + file);
716 02 Jun 08 nicklas 536       }
716 02 Jun 08 nicklas 537       
714 30 May 08 nicklas 538       file.setMimeTypeAuto(null, null);
714 30 May 08 nicklas 539       InputStream in = offset > 0 ? file.getDownloadStream(0) : null;
714 30 May 08 nicklas 540       
714 30 May 08 nicklas 541       // The TransactionOutputStream will make sure DbControl is committed
716 02 Jun 08 nicklas 542       upload = new TransactionalOutputStream(dc, file.getUploadStream(false), toString());
714 30 May 08 nicklas 543       if (in != null)
714 30 May 08 nicklas 544       {
716 02 Jun 08 nicklas 545         long remain = offset;
716 02 Jun 08 nicklas 546         while (remain > 0)
714 30 May 08 nicklas 547         {
714 30 May 08 nicklas 548           int b = in.read();
716 02 Jun 08 nicklas 549           if (b == -1) break;
716 02 Jun 08 nicklas 550           upload.write(b);
716 02 Jun 08 nicklas 551           remain--;
714 30 May 08 nicklas 552         }
714 30 May 08 nicklas 553         in.close();
716 02 Jun 08 nicklas 554         log.debug((offset - remain) + " bytes copied from existing file");
714 30 May 08 nicklas 555       }
714 30 May 08 nicklas 556       dc.disconnect();
714 30 May 08 nicklas 557     }
714 30 May 08 nicklas 558     catch (RuntimeException ex)
714 30 May 08 nicklas 559     {
716 02 Jun 08 nicklas 560       log.error("Could not create upload stream to " + this, ex);
714 30 May 08 nicklas 561       if (dc != null) dc.close();
714 30 May 08 nicklas 562       throw ex;
714 30 May 08 nicklas 563     }
714 30 May 08 nicklas 564     return upload;
714 30 May 08 nicklas 565   }
714 30 May 08 nicklas 566   // -------------------------------------------
714 30 May 08 nicklas 567   
716 02 Jun 08 nicklas 568   /*
716 02 Jun 08 nicklas 569     From the Object class
716 02 Jun 08 nicklas 570     ---------------------
716 02 Jun 08 nicklas 571   */
716 02 Jun 08 nicklas 572   @Override
716 02 Jun 08 nicklas 573   public String toString() 
716 02 Jun 08 nicklas 574   {
716 02 Jun 08 nicklas 575     String type = "path";
716 02 Jun 08 nicklas 576     if (file != null) 
716 02 Jun 08 nicklas 577     {
716 02 Jun 08 nicklas 578       type = "file";
716 02 Jun 08 nicklas 579     }
716 02 Jun 08 nicklas 580     else if (directory != null) 
716 02 Jun 08 nicklas 581     {
716 02 Jun 08 nicklas 582       type = "dir";
716 02 Jun 08 nicklas 583     }
915 11 Dec 08 nicklas 584     return "BaseFtpFile[" + type + "=" + path + "]";
716 02 Jun 08 nicklas 585   }
716 02 Jun 08 nicklas 586   // -------------------------------------------
716 02 Jun 08 nicklas 587   
716 02 Jun 08 nicklas 588   
714 30 May 08 nicklas 589   private String getOwner(OwnedItem item)
714 30 May 08 nicklas 590   {
714 30 May 08 nicklas 591     String owner = "unknown";
714 30 May 08 nicklas 592     try
714 30 May 08 nicklas 593     {
714 30 May 08 nicklas 594       owner = item.getOwner().getLogin();
714 30 May 08 nicklas 595     }
714 30 May 08 nicklas 596     catch (Exception ex)
714 30 May 08 nicklas 597     {}
714 30 May 08 nicklas 598     return owner;
714 30 May 08 nicklas 599   }
714 30 May 08 nicklas 600   
714 30 May 08 nicklas 601   /**
714 30 May 08 nicklas 602     Check if two file objects are located in the same or different directories.
714 30 May 08 nicklas 603   */
915 11 Dec 08 nicklas 604   private boolean hasSameParentDirectory(FtpFile fo1, FtpFile fo2)
714 30 May 08 nicklas 605   {
915 11 Dec 08 nicklas 606     int index1 = fo1.getAbsolutePath().length() - fo1.getName().length();
915 11 Dec 08 nicklas 607     String parent1 = fo1.getAbsolutePath().substring(0, index1);
915 11 Dec 08 nicklas 608     int index2 = fo2.getAbsolutePath().length() - fo2.getName().length();
915 11 Dec 08 nicklas 609     String parent2 = fo2.getAbsolutePath().substring(0, index2);
714 30 May 08 nicklas 610     return parent1.equals(parent2);
714 30 May 08 nicklas 611   }
915 11 Dec 08 nicklas 612
714 30 May 08 nicklas 613 }