/*
  This file is part of the KDE libraries
  Copyright (c) 1999 Waldo Bastian <bastian@kde.org>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License version 2 as published by the Free Software Foundation.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include <config.h>

#include <sys/types.h>

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#include <unistd.h>
#include <fcntl.h>

#ifdef HAVE_TEST
#include <test.h>
#endif

#include <tqdatetime.h>
#include <tqdir.h>

#include <kde_file.h>
#include "kapplication.h"
#include "ksavefile.h"
#include "kstandarddirs.h"

KSaveFile::KSaveFile(const TQString &filename, int mode)
 : mTempFile(true)
{
   // follow symbolic link, if any
   TQString real_filename = KStandardDirs::realFilePath(filename);

   // we only check here if the directory can be written to
   // the actual filename isn't written to, but replaced later
   // with the contents of our tempfile
   if (!checkAccess(real_filename, W_OK))
   {
      mTempFile.setError(EACCES);
      return;
   }

   if (mTempFile.create(real_filename, TQString::fromLatin1(".new"), mode))
   {
      mFileName = real_filename; // Set filename upon success

      // if we're overwriting an existing file, ensure temp file's
      // permissions are the same as existing file so the existing
      // file's permissions are preserved
      KDE_struct_stat stat_buf;
      if (KDE_stat(TQFile::encodeName(real_filename), &stat_buf)==0)
      {
         // But only if we own the existing file
         if (stat_buf.st_uid == getuid())
         {
            bool changePermission = true;
            if (stat_buf.st_gid != getgid())
      {
               if (fchown(mTempFile.handle(), (uid_t) -1, stat_buf.st_gid) != 0)
               {
                  // Use standard permission if we can't set the group
                  changePermission = false;
               }
            }
            if (changePermission)
               fchmod(mTempFile.handle(), stat_buf.st_mode);
         }
      }
   }
}

KSaveFile::~KSaveFile()
{
   if (mTempFile.bOpen)
      close(); // Close if we were still open
}

TQString
KSaveFile::name() const
{
   return mFileName;
}

void
KSaveFile::abort()
{
   mTempFile.close();
   mTempFile.unlink();
}

bool
KSaveFile::close()
{
   if (mTempFile.name().isEmpty() || mTempFile.handle()==-1)
      return false; // Save was aborted already
   if (!mTempFile.sync())
   {
      abort();
      return false;
   }
   if (mTempFile.close())
   {
      if (0==KDE_rename(TQFile::encodeName(mTempFile.name()), TQFile::encodeName(mFileName)))
         return true; // Success!
      mTempFile.setError(errno);
   }
   // Something went wrong, make sure to delete the interim file.
   mTempFile.unlink();
   return false;
}

static int
write_all(int fd, const char *buf, size_t len)
{
   while (len > 0)
   {
      int written = write(fd, buf, len);
      if (written < 0)
      {
          if (errno == EINTR)
             continue;
          return -1;
      }
      buf += written;
      len -= written;
   }
   return 0;
}

bool KSaveFile::backupFile( const TQString& qFilename, const TQString& backupDir,
                            const TQString& backupExtension)
{
   TQCString cFilename = TQFile::encodeName(qFilename);
   const char *filename = cFilename.data();

   int fd = KDE_open( filename, O_RDONLY );
   if (fd < 0)
      return false;

   KDE_struct_stat buff;
   if ( KDE_fstat( fd, &buff) < 0 )
   {
      ::close( fd );
      return false;
   }

   TQCString cBackup;
   if ( backupDir.isEmpty() )
       cBackup = cFilename;
   else
   {
       TQCString nameOnly;
       int slash = cFilename.findRev('/');
       if (slash < 0)
	   nameOnly = cFilename;
       else
	   nameOnly = cFilename.mid(slash + 1);
       cBackup = TQFile::encodeName(backupDir);
       if ( backupDir[backupDir.length()-1] != (QChar)'/' )
           cBackup += '/';
       cBackup += nameOnly;
   }
   cBackup += TQFile::encodeName(backupExtension);
   const char *backup = cBackup.data();
   int permissions = buff.st_mode & 07777;

   if ( KDE_stat( backup, &buff) == 0)
   {
      if ( unlink( backup ) != 0 )
      {
         ::close(fd);
         return false;
      }
   }

   mode_t old_umask = umask(0);
   int fd2 = KDE_open( backup, O_WRONLY | O_CREAT | O_EXCL, permissions | S_IWUSR);
   umask(old_umask);

   if ( fd2 < 0 )
   {
      ::close(fd);
      return false;
   }

    char buffer[ 32*1024 ];

    while( 1 )
    {
       int n = ::read( fd, buffer, 32*1024 );
       if (n == -1)
       {
          if (errno == EINTR)
              continue;
          ::close(fd);
          ::close(fd2);
          return false;
       }
       if (n == 0)
          break; // Finished

       if (write_all( fd2, buffer, n))
       {
          ::close(fd);
          ::close(fd2);
          return false;
       }
    }

    ::close( fd );

    if (::close(fd2))
        return false;
    return true;
}