/*
** Copyright (C) 1999,2000 Toivo Pedaste <toivo@ucs.uwa.edu.au>
**
*/

/*
** 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 in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
** MA 02110-1301, USA.
*/

/*
** Bug reports and questions can be sent to kde-devel@kde.org
*/


//////////////////////////////////////////////////////////////////////////////
///
///               RPM Program version
///
//////////////////////////////////////////////////////////////////////////////

#include <kdebug.h>
#include <tdelocale.h>
#include <tdeglobal.h>
#include <kiconloader.h>

#include "kpPty.h"
#include "kpackage.h"
#include "rpmInterface.h"
#include "updateLoc.h"
#include "cache.h"

RPM::RPM():pkgInterface()
{  
 head = "RPM";
 name = i18n("RPM");
 icon = "application-x-rpm";

 pict = UserIcon(icon);
 updated_pict = UserIcon("rupdated");
 new_pict = UserIcon("rnew");

 packagePattern = "*.rpm";
 typeID = "/rpm";

 locatedialog = new Locations(i18n("Location of RPM Package Archives"));
 locatedialog->dLocations(7,6, this, i18n("Folder","F"),
			  "Rpm","*.rpm", i18n("Location of Folders Containing RPM Packages"));

 connect(locatedialog,TQ_SIGNAL(returnVal(LcacheObj *)),
	 this,TQ_SLOT(setAvail(LcacheObj *)));
 locatedialog->apply_slot();

 paramsInst.append(new param(i18n("Upgrade"),true,false,"-U","-i"));
 paramsInst.append(new param(i18n("Replace Files"),false,false,"--replacefiles"));
 paramsInst.append(new param(i18n("Replace Packages"),true,false,"--replacepkgs"));
 paramsInst.append(new param(i18n("Check Dependencies"),true,true,"--nodeps"));
 paramsInst.append(new param(i18n("Test (do not install)"),false,false,"--test"));

 paramsUninst.append(new  param(i18n("Remove all versions"),false,false,"--allmatches"));
 paramsUninst.append(new  param(i18n("Use Scripts"),true,true,"--noscripts"));
 paramsUninst.append(new param(i18n("Check Dependencies"),true,true,"--nodeps"));
 paramsUninst.append(new param(i18n("Test (do not uninstall)"),false,false,"--test"));


 queryMsg = i18n("Querying RPM package list: ");

 TQDict<TQString> provides(1433,false);

 infoList.append("name/%{NAME}");
 infoList.append("version/%{VERSION}");
 infoList.append("release/%{RELEASE}");
 infoList.append("summary/%{SUMMARY}");
 infoList.append("url/%{URL}");
 infoList.append("architecture/%{ARCH}");
 infoList.append("group/%{GROUP}");
 infoList.append("distribution/%{DISTRIBUTION}");
 infoList.append("vendor/%{VENDOR}");
 infoList.append("packager/%{PACKAGER}");
 infoList.append("installtime/%{INSTALLTIME:date}");
 infoList.append("buildtime/%{BUILDTIME:date}");
 infoList.append("size/%{SIZE}");
 infoList.append("provides/[%{PROVIDES}, ]");
 infoList.append("requires/[%{REQUIRENAME} (%{REQUIREFLAGS:depflags} %{REQUIREVERSION}), ]");
 infoList.append("description/[%{DESCRIPTION}]");

 hasProgram = ifExe("rpm");
}

 RPM::~RPM(){}

bool RPM::isType(char *buf, const TQString & /* fname */)
{
  if (hasProgram) {
    if ((unsigned char)buf[0] == 0355 && (unsigned char)buf[1] == 0253 &&
        (unsigned char)buf[2] == 0356 && (unsigned char)buf[3] == 0333 ) {
     return true;
    } else
      return false;
  } else {
    return false;
  }
}

bool RPM::parseName(const TQString &name, TQString *n, TQString *v)
{
  int d1, d2, s1, s2;

  s2 = name.findRev('.');
  if (s2 > 0) {
    s1 = name.findRev('.',s2-1);
    if (s1 > 0) {
      d2 = name.findRev('-',s1-1);
      if (d2 > 0) {
	d1 = name.findRev('-',d2-1);
	if (d1 < 0)
	  d1 = d2;
	*n = name.left(d1);
	*v = name.mid(d1+1,s1-d1-1);
	return true;
      }
    }
  }
  return false;
}

TQString RPM::packageQuery() {
  TQString cmd =  " --queryformat '";
   for ( TQStringList::Iterator it = infoList.begin(); it != infoList.end(); ++it ) {
     TQStringList s = TQStringList::split("/",*it);
     cmd += "==";
     cmd += s[0];
     cmd += "\\n";
     cmd += s[1];
     cmd += "\\n";
   }
   cmd += "==\\n'";
   return cmd;
}

void RPM::listInstalledPackages(TQPtrList<packageInfo> *pki)
{
  int NLINES =  70000;

  packageInfo *p;
  TQStringList  plist;

  TQString  cmd = "rpm  -q -a";
  cmd += packageQuery();

  kpackage->setStatus(i18n("Querying RPM package list"));
  kpackage->setPercent(0);

  TQStringList list = kpty->run(cmd);
  kpackage->setStatus(i18n("Processing RPM package list"));
  //  kdDebug() << "P=" << list.count() <<"\n";
  kpackage->setPercent(50);


  if (list.count() > 0) {

    TQString s;

    kpackage->setPercent(0 );
    int cnt = 0;
    for ( TQStringList::Iterator it = list.begin();  it != list.end(); ++it ) {
      cnt++;
      if (cnt % (NLINES/20) == 0) {
	kpackage->setPercent((cnt * 100)/ NLINES );
      }
      if (*it != "==") {
	s = *it;
	//	kdDebug() << s.length() << "<" << s << ">\n";
	plist << s;
      } else {
	p = collectInfo(plist);
	if (p) {
	  if (!p->pkgInsert(pki, typeID, true)) {
	    delete p;
	  }
	}
	plist.clear();
      }
    }
  }

  list.clear();
  kpackage->setStatus(i18n("DEB APT"));
  kpackage->setPercent(100);
}

packageInfo* RPM::collectInfo(TQStringList &ln) {

  bool haveName = false;
  TQMap<TQString, TQString> a;

  TQString name, value;

  for ( TQStringList::Iterator it = ln.begin();  it != ln.end(); ++it ) {
    if ((*it).left(2) == "==" && (*it).length() >= 2) {
      name = (*it).right((*it).length() - 2);
    }
    value = "";
    it++;
    while (it != ln.end() && (*it).left(2) != "==") {
      value += *it;
      value += " ";
      it++;
    }
    it--;

    // kdDebug() << "name=" << name << " value='" << value << "'\n";
    if (name == "installtime") {
      a.insert("install time", value);
    } else if (name == "name") {
      if (!value.isEmpty())
	haveName = true;
      a.insert("name", value.stripWhiteSpace());    
    } else if (name == "buildtime") {
      a.insert("build-time", value);
    } else if (name == "requires") {
      value = value.replace(TQRegExp("\\(\\)"),"");
      value = value.replace(TQRegExp("\\( \\)"),"");
      value = value.stripWhiteSpace();
      if (value.endsWith(",")) {  
	value.truncate(value.length()-1);
      }
      a.insert("depends", value);
    } else if (name == "provides") {
      int s = 0, n;
      TQString t;

      if (!(*a.find("name")).isEmpty()) {
	while ((n = value.find(",",s)) > 0) {
	  t = value.mid(s,n-s);
	  t = t.stripWhiteSpace();
	  if (!t.isEmpty())
	    provides.insert(t,new TQString(*a.find("name")));
	  s = n+1;
	}
	t = value.mid(s);
	t = t.stripWhiteSpace();
	if (!t.isEmpty())
	  provides.insert(t,new TQString(*a.find("name")));

	value = value.stripWhiteSpace();
	if (value.endsWith(",")) {  
	  value.truncate(value.length()-1);
	}
	a.insert("provides", value);
      }
    } else {
      if (!name.isEmpty())
        a.insert(name, value.stripWhiteSpace());    
    }
    
  }

  TQString vers = a["version"];
  TQString rel = a["release"];
  if (!vers.isEmpty() && !rel.isEmpty()) {
    vers += "-";
    vers += rel;
    a["version"] = vers;
    a.remove("release");
  }

  if (haveName) {
    packageInfo *i = new packageInfo(a,this);
    i->packageState = packageInfo::INSTALLED;
    i->fixup();
    return i;
  } else {
    return 0;
  }
}

//////////////////////////////////////////////////////////////////////////////

TQStringList RPM::getChangeLog(packageInfo *p)
{
  TQStringList clog;
  TQString fn( p->getFilename());

  if(!fn.isEmpty())
    return getUChangeLog(fn);
  else
    return getIChangeLog(p);

  return clog;
}


// query an installed package
TQStringList RPM::getIChangeLog(packageInfo *p)
{
  TQString name = p->getProperty("name");

  TQString cmd = "rpm -q --changelog ";
  cmd += name;

  TQStringList filelist = kpty->run(cmd);

  return filelist;
}


// query an uninstalled package
TQStringList RPM::getUChangeLog(const TQString &fn)
{
  TQString cmd = "rpm -q --changelog -p ";
  cmd += quotePath(fn);

  TQStringList filelist = kpty->run(cmd);

  return filelist;
}


bool RPM::filesTab(packageInfo *p) {
  if (p->packageState == packageInfo::INSTALLED) {
    return true;
  } else if (p->isFileLocal()) {
    return true;
  } 
  return false;
}

bool RPM::changeTab(packageInfo *p) {
  if (p->packageState == packageInfo::INSTALLED) {
    return true;
  } else if (p->isFileLocal()) {
    return true;
  } 
  return false;
}

//////////////////////////////////////////////////////////////////////////////


TQStringList RPM::getFileList(packageInfo *p)
{
  TQStringList filelist;
  TQString fn( p->getFilename());

  if(!fn.isEmpty())
    return getUFileList(fn);
  else
    return getIFileList(p);

  return filelist;
}



// query an installed package
TQStringList RPM::getIFileList(packageInfo *p)
{
  TQString name = p->getProperty("name");

  TQString cmd = "rpm -q -l ";
  cmd += name;

  TQStringList filelist = kpty->run(cmd);

  return filelist;
}


// query an uninstalled package
TQStringList RPM::getUFileList(const TQString &fn)
{
  TQString cmd = "rpm -q -l -p ";
  cmd += quotePath(fn);

  TQStringList filelist = kpty->run(cmd);

  return filelist;
}

//////////////////////////////////////////////////////////////////////////////
packageInfo *RPM::getPackageInfo(char mode, const TQString &name, const TQString &)
{
  if (mode == 'i') {
    return getIPackageInfo(name);
  } else
    return getUPackageInfo(name);
}

packageInfo *RPM::getIPackageInfo( const TQString &name )
{
  // query an installed package!
  TQString cmd = "rpm -q";
  cmd += packageQuery();
  cmd += " ";
  cmd += name;

  TQStringList infoList = kpty->run(cmd);
  packageInfo *pki = collectInfo(infoList);
  if (pki) {
    pki->packageState = packageInfo::INSTALLED;
    collectDepends(pki,name,0);
  }
  return pki;
}

packageInfo *RPM::getUPackageInfo( const TQString &name )
{
  // query an uninstalled package
  TQString cmd = "rpm -q";
  cmd += packageQuery();
  cmd += " -p ";
  cmd += quotePath(name);

  TQStringList infoList =  kpty->run(cmd);
  packageInfo *pki = collectInfo(infoList);
  if (pki) {
    pki->updated = true;
    pki->packageState = packageInfo::AVAILABLE;
    if (pki->hasProperty("install time"))
      pki->info.remove("install time");
    collectDepends(pki,name,1);
  }

  return pki;
}

TQString RPM::provMap( const TQString &p )
{
  TQString *r = provides[p];
  if (r) {
    TQString s = *r;
    //    printf("%s=>%s\n",p.data(),s.data());
    return s;
  } else {
    return p;
  }
}


//////////////////////////////////////////////////////////////////////////////
void RPM::collectDepends(packageInfo *p, const TQString &name, int src)
{
  TQString cmd = "rpm -V --nofiles ";
  if (src) {
    cmd += "-p ";
  }
  cmd += quotePath(name);

  //  cmd = "cat /home/toivo/rpm.deps";
  TQStringList list =  kpty->run(cmd);

  if (list.count() > 0) {
     TQStringList::Iterator it = list.begin();
     int pt = (*it).find(":");
     if (pt > 0) {
       TQString s = (*it).mid(pt+1);
       if (!s.isEmpty()) {
	 //	 kdDebug() << "S=" << s << "\n";
	 p->info.insert("unsatisfied dependencies", s);
       }
     }
  }
}

//////////////////////////////////////////////////////////////////////////////
void RPM::setLocation()
{
    locatedialog->restore();
}

void RPM::setAvail(LcacheObj *slist)
{
  if (packageLoc)
    delete packageLoc;
  packageLoc = slist;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

TQString RPM::uninstall(int uninstallFlags, TQPtrList<packageInfo> *plist, bool &test)
{
  TQStringList files;

  packageInfo *pk;
  for (pk = plist->first(); pk != 0; pk = plist->next()) {
    files.append( pk->getProperty("name") );
  }

  if (getuid() == 0) {
    return  doUninst(uninstallFlags,files, test);
  } else {
    return  doUninstP(uninstallFlags,files, test);
  }
}

TQString RPM::uninstall(int uninstallFlags, packageInfo *p, bool &test)
{
  TQStringList files;
  files.append( p->getProperty("name") );

  if (getuid() == 0) {
    return  doUninstP(uninstallFlags,files, test);
  } else {
    return  doUninstP(uninstallFlags,files, test);
  }
}

TQString RPM::doUninstP(int uninstallFlags, const TQStringList &files, bool &test)
{
    TQString s = "rpm -e ";
    s += setOptions(uninstallFlags, paramsUninst);

    for (TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it ) {
      s += " ";
      s += *it;
    }

    if (uninstallFlags>>3 & 1)
      test = true;

    kdDebug() << "uCMD=" << s << " test=" << test << "\n";

    return s;
}



//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
TQString RPM::install(int installFlags, TQPtrList<packageInfo> *plist, bool &test)
{
  TQStringList files;

  for (packageInfo *pk = plist->first(); pk != 0; pk = plist->next()) {
    TQString fname( pk->fetchFilename() );
    if (!fname.isEmpty()) {
       files.append(quotePath(fname));
    }
  }

  if (getuid() == 0) {
    return doinstP(installFlags,files,test);
  } else {
    return doinstP(installFlags,files,test);
  }
}

TQString RPM::install(int installFlags, packageInfo *p, bool &test)
{
  TQStringList files;
  files.append(quotePath(p->fetchFilename()));
  if (getuid() == 0) {
    return doinstP(installFlags,files,test);
  } else {
    return doinstP(installFlags,files,test);
  }
}


TQString RPM::doinstP(int installFlags, const TQStringList &files, bool &test)
{
    TQString s = "rpm ";
    s += setOptions(installFlags, paramsInst);

    for (TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it ) {
      s += " ";
      s += (*it).utf8();
    }

    if (installFlags>>4 & 1)
      test = true;

    kdDebug() << "iCMD=" << s << " test=" << test << "\n";

    return s;
}

 TQStringList RPM::verify(packageInfo *p, const TQStringList &files){
   return pkgInterface::verify(p,files);}

//////////////////////////////////////////////////////////////////////////////
TQStringList RPM::FindFile(const TQString &name, bool) {
  TQString cmd = "rpm -q -a --filesbypkg";
    
  TQStringList list =  kpty->run(cmd);
  TQStringList retlist;
  if (kpty->Result > 0) {
    list.clear();
  } else {
    for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
      int p =  (*it).find(" ");
      int nm = (*it).find(name,p);
      if (nm >= 0) {
	(*it).replace(p, 1, "\t");
	retlist.append(*it);
      }
    }
  }

  return retlist;
}

//////////////////////////////////////////////////////////////////////////////
TQString RPM::quotePath( const TQString &path) {
  TQString s = path;
  s = s.replace(" ","\\ ");
  return ( "'" + s + "'" );
}

//////////////////////////////////////////////////////////////////////////////
  TQStringList RPM::depends(const TQString &, int){return 0;}

  TQString RPM::doUninst(int, const TQStringList &, bool &){return "0=";}
  TQString RPM::doinst(int, const TQStringList &, bool &){return "0=";}


#include "rpmInterface.moc"
