/***************************************************************************
 *   Copyright (C) 2006 by Raul Fernandes                                  *
 *   rgfbr@yahoo.com.br                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
 ***************************************************************************/

#include "krar.h"

#include <qfile.h>
#include <qdir.h>
#include <qdatetime.h>
#include <qtextcodec.h>

#include <kdebug.h>

KRar::KRar( const QString &filename, const QString &password )
  : KArchive( 0L )
{
  m_filename = filename;
  m_dirs = 0;
  m_files = 0;
  m_uncompressed = 0;
  m_compressed = 0;
  m_ratio = 0;
  m_volattr = false;
  volnum = -1;
  m_lock = false;
  m_solid = false;
  m_authenticity = false;
  m_password = password;
}


KRar::~KRar()
{
}


/*!
    \fn KRar::openArchive( int mode )
 */
bool KRar::openArchive( int mode )
{
  //kdDebug() << "KRar::openArchive()" << endl;
  switch( mode )
  {
    case IO_WriteOnly:
    case IO_ReadWrite:
      // Mode not supported
      return false;
    case IO_ReadOnly:
      if ( !m_filename.isEmpty() )
      {
        setDevice( new QFile( m_filename ) );
        if ( !device()->open( mode ) ) return false;
      }
      break;
    default:
      return false;
  }
  struct RAROpenArchiveDataEx OpenArchiveData;
  struct RARHeaderData HeaderData;
  int iReadHeaderCode, iProcessFileCode;
  HANDLE hArcData;
  const char* dir="/";

  memset(&OpenArchiveData, 0, sizeof(OpenArchiveData));

  OpenArchiveData.ArcName         = (char*)m_filename.ascii(); /* name arch */
  OpenArchiveData.CmtBuf          = NULL;
  OpenArchiveData.OpenMode        = RAR_OM_EXTRACT;

  hArcData = RAROpenArchiveEx(&OpenArchiveData);

  if (OpenArchiveData.OpenResult != 0)
  {
    //kdDebug() << QString( "Error archive open (%1)\n" ).arg( OpenArchiveData.OpenResult ) << endl;
    return false;
  }
  kdDebug() << OpenArchiveData.Flags << endl;

  m_volattr = OpenArchiveData.Flags & '\x01';
  m_lock = OpenArchiveData.Flags & '\x04';
  m_solid = OpenArchiveData.Flags & '\x08';
  m_authenticity = OpenArchiveData.Flags & '\x20';

  if( !m_password.isEmpty() ) RARSetPassword( hArcData, (char*)m_password.ascii() );

  HeaderData.CmtBuf = NULL;

  KRarFile *file;
  QString filename;
  unsigned long fileSize;
  int second;
  int minute;
  int hour;
  int day;
  int month;
  int year;
  QDateTime *time;
  mode_t mode;
  bool hasPassword;

  //m_volattr = header[3] & '\x01';
  //m_lock = header[3] & '\x04';
  //m_solid = header[3] & '\x08';
  //m_authenticity = header[3] & '\x20';

  while ((iReadHeaderCode = RARReadHeader(hArcData, &HeaderData)) == 0)
  {
    iProcessFileCode = RARProcessFile(hArcData, RAR_SKIP, (char *)dir, NULL);
    if (iProcessFileCode != 0)
    {
      //kdDebug() << QString("Error archive extract (%d)\n" ).arg( iProcessFileCode );
      break;
    }
    filename = HeaderData.FileName;
    hasPassword = (HeaderData.Flags & 0x0004) ? true : false;

    // Uncompressed file size
    fileSize = HeaderData.UnpSize;

    // Date and time
    second = HeaderData.FileTime & '\x1f';
    minute = (uint)((HeaderData.FileTime & 0x07e0 ) >> 5 );
    hour = (uint)((HeaderData.FileTime & 0xf800 ) >> 11 );
    day = (uint)((HeaderData.FileTime & 0x1f0000 ) >> 16 );
    month = (uint)((HeaderData.FileTime & 0x01e00000 ) >> 21 );
    year = (uint)((HeaderData.FileTime & 0xfe000000 ) >> 25 ) + 1980;
    time = new QDateTime( QDate( year, month, day), QTime( hour, minute, second ) );

    // Permissions
    mode = 0;
    if( HeaderData.HostOS == 0 )
    {
      //kdDebug() << "MS-DOS Type" << endl;

      // TODO: set the permissions to attributes for ms-dos files
      mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
    }else if( HeaderData.HostOS == 1 )
    {
      //kdDebug() << "OS/2 Type" << endl;

      // TODO: set the permissions to attributes for os/2 files
      mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
    }else if( HeaderData.HostOS == 2 )
    {
      //kdDebug() << "Win32 Type" << endl;

      // TODO: set the permissions to attributes for windows files
      mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
    }else if( HeaderData.HostOS == 3 )
    {
      //kdDebug() << "Unix Type" << endl;

      // owner permissions
      if( HeaderData.FileAttr & 256 ) mode |= S_IRUSR;
      if( HeaderData.FileAttr & 128 ) mode |= S_IWUSR;
      if( HeaderData.FileAttr & 64 ) mode |= S_IXUSR;

      // group permissions
      if( HeaderData.FileAttr & 32 ) mode |= S_IRGRP;
      if( HeaderData.FileAttr & 16 ) mode |= S_IWGRP;
      if( HeaderData.FileAttr & 8 ) mode |= S_IXGRP;

      // other permissions
      if( HeaderData.FileAttr & 4 ) mode |= S_IROTH;
      if( HeaderData.FileAttr & 2 ) mode |= S_IWOTH;
      if( HeaderData.FileAttr & 1 ) mode |= S_IXOTH;
    }

    // Encrypted with password
    kdDebug() << HeaderData.Flags << " - " << ((HeaderData.Flags & 0x0004) >> 2) << endl;
    if( (HeaderData.Flags & 0x00e0) >> 5 == 7 )
    {
      // directory
      mode |= S_IFDIR;
      // TODO: set the permissions to directories
      if( !rootDir()->entry( filename ) )
            findOrCreate( filename );
    }else
    {
      // file
      file = new KRarFile( this, filename.right( filename.length() - filename.findRev( '/' ) - 1 ), mode, time->toTime_t(), rootDir()->user(), rootDir()->group(), QString::null, 0, fileSize, hasPassword );

      // Test to see if the file is in root directory
      if( filename.find( '/' ) == -1 ) rootDir()->addEntry( file );
      else
      {
        // In some tar files we can find dir/./file => call cleanDirPath
        QString path = QDir::cleanDirPath( filename.left( filename.findRev( '/' ) ) );

        // Ensure container directory exists, create otherwise
        KArchiveDirectory * tdir = findOrCreate( path );
        tdir->addEntry(file);
      }
    }

  }

  RARCloseArchive(hArcData);

  return true;
}


/*!
    \fn KRar::closeArchive()
 */
bool KRar::closeArchive()
{
  device()->close();
  return true;
}


/*!
    \fn KRar::doneWriting( uint size )
 */
bool KRar::doneWriting( uint /*size*/ )
{
  return false;
}


/*!
    \fn KRar::writeDir( const QString& name, const QString& user, const QString& group )
 */
bool KRar::writeDir( const QString& /*name*/, const QString& /*user*/, const QString& /*group*/ )
{
  return false;
}


/*!
    \fn KRar::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
 */
bool KRar::prepareWriting( const QString& /*name*/, const QString& /*user*/, const QString& /*group*/, uint /*size*/ )
{
  return false;
}


KRarFile::KRarFile( KArchive *archive, const QString &name, int access, int date, const QString &user, const QString &group, const QString &symlink, int pos, int size, bool hasPassword ) : KArchiveFile( archive, name, access, date, user, group, symlink, pos, size )
{
  m_hasPassword = hasPassword; 
}

KRarFile::~KRarFile()
{
}

