// KreateCD - CD recording software for the K desktop environment
//
// 1999 by Alexander Feigl <Alexander.Feigl@gmx.de>
//
// This file is subject to the terms and conditions of the GNU General
// Public License.  See the file COPYING in the main directory of the
// KreateCD distribution for more details.

#include "IsoImage.h"
#include "IsoFile.h"
#include "ProgressDialog.h"
#include "uidcontrol.h"

#include <kapp.h>
#include <kconfig.h>
#include <kprocess.h>
#include <klocale.h>
#include <kmsgbox.h>



#include <qstring.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

#define OPTION_DEFAULTS 4

extern KLocale *locale;

static const bool option_defaults[OPTION_DEFAULTS][10]=
      {
      /*otdot ndeep long  leado nover  rr   anorr trans joli  all*/
       {false,false,true ,true ,false,false,true ,false,false,true }, /* UNIX RockRidge*/
       {false,true ,true ,true ,false,false,true ,false,true ,true }, /* UNIX + WIN */
       {false,true ,true ,true ,false,false,false,false,true ,true }, /* WIN */
       {false,false,false,false,false,false,false,false,false,true }  /* MS-DOS*/
      };



ISOImage::ISOImage(void)
 {
  KConfig *config;
  QString qs("/");
  rootObject=new ISOFile(ISOFile::ISO_ISODir,&qs);
  fileTree=0;

  config=kapp->getConfig();
  config->setGroup("ISOOptions");
  loadConfig(config);
 }

void ISOImage::loadConfig(KConfig *config)
 {
  QString ts;
  ts=config->readEntry("ApplicationID","KreateCD");
  strcpy(applicationID,ts.data());
  ts=config->readEntry("PublisherID","No publisher");
  strcpy(publisherID,ts.data());
  ts=config->readEntry("PreparerID","No preparer");
  strcpy(preparerID,ts.data());
  ts=config->readEntry("VolumeID","UNNAMED");
  strcpy(volumeID,ts.data());
  ts=config->readEntry("SystemID","LINUX");
  strcpy(systemID,ts.data());

  omitTrailingDot=config->readBoolEntry("NoTrailingDots",false);
  noDeepRelocation=config->readBoolEntry("NoDeepRelocation",false);
  longISONames=config->readBoolEntry("LongISONames",true);
  allowLeadingDot=config->readBoolEntry("LeadingDots",true);
  omitVersions=config->readBoolEntry("OmitVersions",false);
  withRockRidge=config->readBoolEntry("RockRidge",false);
  withAnonymousRockRidge=config->readBoolEntry("AnonymousRockRidge",true);
  makeTransTab=config->readBoolEntry("TransTab",false);
  withJoliet=config->readBoolEntry("Joliet",false);
  allFiles=config->readBoolEntry("AllFiles",true);
 }

ISOImage::~ISOImage(void)
 {
  if (fileTree!=0) clearImageTree();
  delete(rootObject);
 }


ISOFile *ISOImage::imageRoot(void)
 {
  return(rootObject);
 }

bool ISOImage::buildImageTree(void)
 {
  KConfig *config;
  QString tempdir;
  int i=0,r;
  char numbersuf[16];

  if (fileTree!=0) clearImageTree();

  config=kapp->getConfig();
  config->setGroup("Path");

  while (i<500)
   {
    r=rand()%1000;
    sprintf(numbersuf,"/isotree_%d",r);
    tempdir=config->readEntry("PathTemporary","/tmp");
    tempdir+=numbersuf;
    if (mkdir(tempdir.data(),0700)==0) break;
    ++i;
   }
   if (i==500)
    {
     return(false);
    }

  if (!rootObject->createTree(tempdir.data()))
   {
    rootObject->deleteTree(tempdir.data());
    rmdir(tempdir.data());
    return(false);
   }
  fileTree=new QString(tempdir.data());
  return(true);
 }

bool ISOImage::clearImageTree(void)
 {
  if (fileTree==0) return(true);
  if (!rootObject->deleteTree(fileTree->data()))
   {
    delete(fileTree);
    fileTree=0;
    return(false);
   }

  if (rmdir(fileTree->data())!=0)
   {
    delete(fileTree);
    fileTree=0;
    return(false);
   }

  delete(fileTree);
  fileTree=0;
  return(true);
 }

void ISOImage::setOptionsDefault(ISOImage::ISOType type)
 {
  int itype;
  if (type==CustomType) return;
  itype=type-1;
  omitTrailingDot=option_defaults[itype][0];
  noDeepRelocation=option_defaults[itype][1];
  longISONames=option_defaults[itype][2];
  allowLeadingDot=option_defaults[itype][3];
  omitVersions=option_defaults[itype][4];
  withRockRidge=option_defaults[itype][5];
  withAnonymousRockRidge=option_defaults[itype][6];
  makeTransTab=option_defaults[itype][7];
  withJoliet=option_defaults[itype][8];
  allFiles=option_defaults[itype][9];
  emit(imageChanged());
 }

ISOImage::ISOType ISOImage::getOptionsDefault(void)
 {
  int itype;
  for (itype=0;itype<OPTION_DEFAULTS;++itype)
   {
    if (omitTrailingDot!=option_defaults[itype][0]) continue;
    if (noDeepRelocation!=option_defaults[itype][1]) continue;
    if (longISONames!=option_defaults[itype][2]) continue;
    if (allowLeadingDot!=option_defaults[itype][3]) continue;
    if (omitVersions!=option_defaults[itype][4]) continue;
    if (withRockRidge!=option_defaults[itype][5]) continue;
    if (withAnonymousRockRidge!=option_defaults[itype][6]) continue;
    if (makeTransTab!=option_defaults[itype][7]) continue;
    if (withJoliet!=option_defaults[itype][8]) continue;
    if (allFiles!=option_defaults[itype][9]) continue;
    return( (ISOImage::ISOType) (itype+1) );
   }
  return(CustomType);
 }

void ISOImage::setOmitTrailingDot(bool b)
 {
  omitTrailingDot=b;
  emit(imageChanged());
 }

void ISOImage::setNoDeepRelocation(bool b)
 {
  noDeepRelocation=b;
  emit(imageChanged());
 }

void ISOImage::setLongISONames(bool b)
 {
  longISONames=b;
  emit(imageChanged());
 }

void ISOImage::setAllowLeadingDot(bool b)
 {
  allowLeadingDot=b;
  emit(imageChanged());
 }

void ISOImage::setOmitVersions(bool b)
 {
  omitVersions=b;
  emit(imageChanged());
 }

void ISOImage::setWithRockRidge(bool b)
 {
  withRockRidge=b;
  if ( withRockRidge && withAnonymousRockRidge) withAnonymousRockRidge=false;
  emit(imageChanged());
 }

void ISOImage::setWithAnonymousRockRidge(bool b)
 {
  withAnonymousRockRidge=b;
  if ( withRockRidge && withAnonymousRockRidge) withRockRidge=false;
  emit(imageChanged());
 }

void ISOImage::setMakeTransTab(bool b)
 {
  makeTransTab=b;
  emit(imageChanged());
 }

void ISOImage::setWithJoliet(bool b)
 {
  withJoliet=b;
  emit(imageChanged());
 }

void ISOImage::setAllFiles(bool b)
 {
  allFiles=b;
  emit(imageChanged());
 }

static void scopy(char *dest,const char *source,int i)
 {
  while ( (i>0) && (*source!=0) )
   {
    *(dest++)=*(source++);
    i--;
   }
  *dest='\0';
 }

void ISOImage::setApplicationID(const QString &id)
 {
  scopy(applicationID,id.data(),128);
  emit(imageChanged());
 }

void ISOImage::setPreparerID(const QString &id)
 {
  scopy(preparerID,id.data(),128);
  emit(imageChanged());
 }

void ISOImage::setPublisherID(const QString &id)
 {
  scopy(publisherID,id.data(),128);
  emit(imageChanged());
 }

void ISOImage::setVolumeID(const QString &id)
 {
  scopy(volumeID,id.data(),32);
  emit(imageChanged());
 }

void ISOImage::setSystemID(const QString &id)
 {
  scopy(systemID,id.data(),32);
  emit(imageChanged());
 }

void ISOImage::setApplicationID(const char *id)
 {
  scopy(applicationID,id,128);
  emit(imageChanged());
 }

void ISOImage::setPreparerID(const char *id)
 {
  scopy(preparerID,id,128);
  emit(imageChanged());
 }

void ISOImage::setPublisherID(const char *id)
 {
  scopy(publisherID,id,128);
  emit(imageChanged());
 }

void ISOImage::setVolumeID(const char *id)
 {
  scopy(volumeID,id,32);
  emit(imageChanged());
 }

void ISOImage::setSystemID(const char *id)
 {
  scopy(systemID,id,32);
  emit(imageChanged());
 }

bool ISOImage::getOmitTrailingDot(void)
 {
  return(omitTrailingDot);
 }

bool ISOImage::getNoDeepRelocation(void)
 {
  return(noDeepRelocation);
 }

bool ISOImage::getLongISONames(void)
 {
  return(longISONames);
 }

bool ISOImage::getAllowLeadingDot(void)
 {
  return(allowLeadingDot);
 }

bool ISOImage::getOmitVersions(void)
 {
  return(omitVersions);
 }

bool ISOImage::getWithRockRidge(void)
 {
  return(withRockRidge);
 }

bool ISOImage::getWithAnonymousRockRidge(void)
 {
  return(withAnonymousRockRidge);
 }

bool ISOImage::getMakeTransTab(void)
 {
  return(makeTransTab);
 }

bool ISOImage::getWithJoliet(void)
 {
  return(withJoliet);
 }

bool ISOImage::getAllFiles(void)
 {
  return(allFiles);
 }

const char *ISOImage::getApplicationID(void)
 {
  return(applicationID);
 }

const char *ISOImage::getPreparerID(void)
 {
  return(preparerID);
 }

const char *ISOImage::getPublisherID(void)
 {
  return(publisherID);
 }

const char *ISOImage::getSystemID(void)
 {
  return(systemID);
 }

const char *ISOImage::getVolumeID(void)
 {
  return(volumeID);
 }


bool ISOImage::makeImage(const char *filename)
 {
  int retval;
  KProcess *mkiproc;
  KConfig *config;


  mkiproc=new KProcess();
  config=kapp->getConfig();
  config->setGroup("Path");
  *mkiproc<<config->readEntry("PathMkisofs","mkisofs");

  if (allFiles) *mkiproc<<"-a";
  if (omitTrailingDot) *mkiproc<<"-d";
  if (noDeepRelocation) *mkiproc<<"-D";
  if (longISONames) *mkiproc<<"-l";
  if (allowLeadingDot) *mkiproc<<"-L";
  if (omitVersions) *mkiproc<<"-N";
  if (withRockRidge) *mkiproc<<"-R";
  if (withAnonymousRockRidge) *mkiproc<<"-r";
  if (makeTransTab) *mkiproc<<"-T";
  if (withJoliet) *mkiproc<<"-J";

  *mkiproc<<"-o"<<filename;
  *mkiproc<<"-f"; // always follow symbolic links

  if (applicationID[0]!=0) *mkiproc<<"-A"<<applicationID;
  if (publisherID[0]!=0) *mkiproc<<"-P"<<publisherID;
  if (preparerID[0]!=0) *mkiproc<<"-p"<<preparerID;
  if (volumeID[0]!=0) *mkiproc<<"-V"<<volumeID;
  if (systemID[0]!=0) *mkiproc<<"-sysid"<<systemID;

  linebuffer[0]='\0';

  connect(mkiproc,SIGNAL(receivedStderr(KProcess *,char *,int)),
          this,SLOT(getIsofs(KProcess *,char *,int)));
  connect(mkiproc,SIGNAL(receivedStdout(KProcess *,char *,int)),
          this,SLOT(getIsofs(KProcess *,char *,int)));
  connect(mkiproc,SIGNAL(processExited(KProcess *)),
          this,SLOT(leaveIsofs(KProcess *)));

  isoprogress=new ProgressDialog(0,0,locale->translate("Creating directory tree..."),false);
  isoprogress->show();

  if (!buildImageTree())
   {
#if QT_VERSION >= 200
    KMsgBox::message(0,QString::null,locale->translate("Unable to build directory tree!"),KMsgBox::EXCLAMATION);
#else
    KMsgBox::message(0,0,locale->translate("Unable to build directory tree!"),KMsgBox::EXCLAMATION);
#endif
    delete(mkiproc);
    delete(isoprogress);
    return(false);
   }
  delete(isoprogress);
  isoprogress=new ProgressDialog(0,0,locale->translate("Creating ISO 9660 image ..."));
  connect(isoprogress,SIGNAL(canceled()),isoprogress,SLOT(abortCancel()));


  *mkiproc<<(fileTree->data());

  //getRootRights();
  isoflag=0;
  mkiproc->start(KProcess::NotifyOnExit,KProcess::AllOutput);
  //dropRootRights();

  retval=isoprogress->exec();  // 1=ripping was OK  -1=abort -2=ripping error  0=window hard closed

  delete(isoprogress);
  isoprogress=new ProgressDialog(0,0,locale->translate("Cleaning up directory tree ..."),false);
  if (!clearImageTree())
   {
#if QT_VERSION >= 200
    KMsgBox::message(0,QString::null,locale->translate("Unable to delete directory tree!"),KMsgBox::EXCLAMATION);
#else
    KMsgBox::message(0,0,locale->translate("Unable to delete directory tree!"),KMsgBox::EXCLAMATION);
#endif
   }

  if (retval!=1) remove(filename);
  if (retval==-2)
   {
#if QT_VERSION >= 200
    KMsgBox::message(0,QString::null,locale->translate("Unable to create ISO image!"),KMsgBox::EXCLAMATION);
#else
    KMsgBox::message(0,0,locale->translate("Unable to create ISO image!"),KMsgBox::EXCLAMATION);
#endif
   }

  delete (mkiproc);
  delete (isoprogress);

  if (retval!=1) return(false);

  return(true);
 }

void ISOImage::leaveIsofs(KProcess *)
 {
  isoprogress->abortDialog(isoflag?1:-2);
 }

void ISOImage::getIsofs(KProcess *,char *buffer,int bufflen)
 {
  int i;
  char *buf,*buf2;

  i=bufflen;
  buf=buffer;
  buf2=linebuffer;
  while (*buf2!=0) ++buf2;
  while (i>0)
   {
    if ( (*buf=='\n') || (*buf=='\r') )
     {
      *buf2=0;
      buf2=linebuffer;
      if (strstr(linebuffer,"done, estimate finish")!=0)
       {
        unsigned long fin;
        fin=strtoul(linebuffer,0,10);
        isoprogress->setProgress(fin,100);
        goto ignLine;
       }

      if (strstr(linebuffer,"extents written")!=0)
       {
        isoflag=1;
        goto ignLine;
       }

      ignLine:
      *buf2=0;
     }
     else
     {
      *(buf2++)=*buf;
      *buf2=0;
     }
    ++buf;
    --i;
   }
 }
