//////////////////////////////////////////////////////////////
//      $Id: rpmInterface.cpp,v 1.77.2.1 2002/12/13 15:20:22 toivo Exp $
//
// Author: Toivo Pedaste
//
#include "../config.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <qregexp.h>
#include <qfile.h>

#ifdef HAVE_RPM
/* stupid RPM headers! */
typedef __off64_t off64_t;
#include <rpm/rpmlib.h>

#include <rpm/rpmio.h>

#if RPM_V1 > 3 || RPM_V2 > 0 || RPM_V3 >= 4
#    define DECLARE_HEADER_HANDLE  FD_t fd
#else
#    define DECLARE_HEADER_HANDLE  _FD *fd
#endif

#include "rpmInstall.h"
#include "rpmVerify.h"

#if RPM_V1 < 4
#include <rpm/dbindex.h>
#endif

#include <time.h>

#include <klocale.h>
#include <kglobal.h>
#include <kiconloader.h>

#include "rpmInterface.h"
#include "updateLoc.h"
#include "packageInfo.h"
#include "managementWidget.h"
#include "kpackage.h"
#include "rpmutils.h"
#include "options.h"
#include "cache.h"

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
RPM::RPM():pkgInterface()
{
  char * rcfile = NULL;
  char *arch = NULL;

  head = "RPM";
  name = i18n("RPM");
  icon = "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("D"),
			   "Rpm","*.rpm", i18n("Location of Directories Containing RPM Packages"));

  connect(locatedialog,SIGNAL(returnVal(LcacheObj *)),
	  this,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("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: ");

  QDict<QString> provides(1433,false);

  rpmErrorSetCallback(rpmErr);

  if(rpmReadConfigFiles(rcfile, arch)) {
    rpmSetup = FALSE;
  } else {
    rpmSetup = TRUE;
  }
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
RPM::~RPM()
{
}


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
bool RPM::isType(char *buf, const QString & /* fname */)
{
  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;
}

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


bool RPM::parseName(const QString &name, QString *n, QString *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;
}

void RPM::collectDepends(packageInfo *p, const QString &name, int src)
{
  QString dlist;
  QStringList list = depends(name,src);

  if (list.count() > 0) {
    int i = 0;

    for ( QStringList::ConstIterator it = list.begin();
	  it != list.end();
	  it++, i++ )
    {
      if (i)
	dlist += " , ";
      dlist += *it;
    }
    if (!dlist.isNull())
      p->info->insert("unsatisfied dependencies",new QString(dlist));
  }
}

void RPM::listInstalledPackages(QPtrList<packageInfo> *pki)
{
  int percentage;
#if RPM_V1 < 4
  int offset;
#endif
  rpmdb db;
  Header h;
  QString prefix("");
  packageInfo *p;

  if (!rpmSetup)
    return;

  kpackage->setStatus(i18n("Querying RPM database for installed packages"));
  percentage=0;
  kpackage->setPercent(percentage);

  //  if ( rpmdbOpenForTraversal(prefix.ascii(), &db) )
  //    return;
  if ( rpmdbOpen(const_cast<char*>(prefix.ascii()), &db, O_RDONLY, 0644) )
    return;

#if RPM_V1 < 4
  offset = rpmdbFirstRecNum(db);
  while (offset)
    {
      percentage+=5;
      if(percentage > 100) percentage=0;
      kpackage->setPercent(percentage);
      h = rpmdbGetRecord(db, offset);
      if (!h)
	{
	  KpMsgE(i18n("could not read database record\n"),TRUE);
	  return;
	}

      p = collectInfo(h);
      if (p) {
	if (!p->pkgInsert(pki, typeID,TRUE))
	  delete p;
      }

      headerFree(h);
      offset = rpmdbNextRecNum(db, offset);
    }
#else
  rpmdbMatchIterator mi;
  char *group;
  mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, NULL, 0);
  while((h = rpmdbNextIterator(mi)) != NULL) {
      percentage += 5;
      if(percentage > 100) percentage=0;
      kpackage->setPercent(percentage);
      if (!headerGetEntry(h, RPMTAG_GROUP, NULL, (void **) &group, NULL))
	{
	  KpMsgE(i18n("could not read database record\n"),TRUE);
	  return;
	}

      p = collectInfo(h);
      if (p) {
	if (!p->pkgInsert(pki, typeID,TRUE))
	  delete p;
      }
  }
  rpmdbFreeIterator(mi);
#endif

  kpackage->setPercent(100);
  rpmdbClose(db);
  kpackage->setStatus("");
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// mode: i = query installed    u = query uninstalled
packageInfo *RPM::getPackageInfo(char mode,
                                 const QString &name,
                                 const QString & /*version*/ )
{
  rpmdb db;
  DECLARE_HEADER_HANDLE;
  int  rc=0, isSource;
  Header h;
  QString prefix(""); // this is the root directory -- should be configured!
  packageInfo *pki = 0;;

  switch(mode)
    {
      ////////////////////////////////////////////////////////////////////////
      // query an installed package!
    case 'i':
      if (rpmdbOpen(const_cast<char*>(prefix.ascii()),&db, O_RDONLY, 0644) )
	return 0;

#if RPM_V1 < 4
      dbiIndexSet matches;

      rc = findPackageByLabel(db, QFile::encodeName(name), &matches);
      if(rc==1)
      {
	rpmdbClose(db);
	return 0;
      }
	//	KpMsgE(i18n("Package %1 is not installed\n").arg(name),TRUE);
      else if(rc==2)
	KpMsgE(i18n("Error looking for package %1\n").arg(name),TRUE);
      else
	{
	  int i;
	  for(i=0; i<matches.count; i++)
	    {
	      if(matches.recs[i].recOffset)
		{
		  h = rpmdbGetRecord(db, matches.recs[i].recOffset);
		  if(!h)
		    KpMsgE(  i18n("Could not read database record\n"),TRUE);
		  else
		    {
		      pki = RPM::collectInfo(h);
		      headerFree(h);
		    }
		}
	    }

	  collectDepends(pki, pki->getProperty("name"),VERIFY_PACKAGE);

	  dbiFreeIndexRecord(matches);
	}
#else /* RPM 4 */
      {
      rpmdbMatchIterator mi;
      Header tmp;
      bool found=false;
      mi=rpmdbInitIterator(db, RPMDBI_LABEL, QFile::encodeName(name), 0);
      while ((tmp = rpmdbNextIterator(mi)) != NULL) {
	      found=true;
	      pki = RPM::collectInfo(tmp);
      }
      if(!found)
      {
	rpmdbClose(db);
	      return 0; //KpMsgE(i18n("Error looking for package %1\n").arg(name),TRUE);
      }
      collectDepends(pki, pki->getProperty("name"), VERIFY_PACKAGE);
      rpmdbFreeIterator(mi);
      }
#endif
      rpmdbClose(db);
      break;

      ////////////////////////////////////////////////////////////////////
      // query an uninstalled package
    case 'u':
#if RPM_V1 < 4
      if((fd = fdOpen(QFile::encodeName(name), O_RDONLY, 0)) < 0) {
	  KpMsgE(i18n("Problem opening %1\n").arg(name),TRUE);
	  return pki;
      }
#else /* RPM 4 */
      if((fd = Fopen(QFile::encodeName(name), "r.fdio")) < 0) {
	  KpMsgE(i18n("Problem opening %1\n").arg(name),TRUE);
	  return pki;
      }
#endif
      if(fd!=0)	{
	  rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
#if RPM_V1 < 4
	  fdClose(fd);
#else /* RPM 4 */
	  Fclose(fd);
#endif
      }
      switch(rc)
	{
	case 0:
	  if(!h)
	    KpMsgE(i18n("Old format source packages cannot be queried!\n"),TRUE);
	  else
	    {
	      pki = RPM::collectInfo(h);
	      headerFree(h);
	      collectDepends(pki, name, VERIFY_RPM);
	    }
	  break;
	case 1:
	  KpMsgE(i18n("%1 does not appear to be a RPM package\n").arg(name),TRUE);
	  // fallthrough
	case 2:
	  KpMsgE(i18n("Query of %1 failed!\n").arg(name),TRUE);
	  return 0;
	}
      break;
    }
  if (pki)
    pki->updated = TRUE;
  return pki;
}


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
#define DO(x,z)                                                  \
{                                                                \
  char *k;                                                       \
  headerGetEntry(h, z, &type, (void **) &k, &count);             \
  if(k)                                                          \
    a->insert(x, new QString(QString::fromLocal8Bit(k)));        \
}								 \

packageInfo *RPM::collectInfo(Header h)
{
  int_32 type,count;
  time_t dateint;

  QDict<QString> *a = new QDict<QString>;
  a->setAutoDelete(TRUE);

  DO("name",RPMTAG_NAME);
  DO("version",RPMTAG_VERSION);
  DO("release",RPMTAG_RELEASE);
  DO("summary",RPMTAG_SUMMARY);
  DO("group",RPMTAG_GROUP);
  DO("distribution",RPMTAG_DISTRIBUTION);
  DO("vendor",RPMTAG_VENDOR);
  DO("packager",RPMTAG_PACKAGER);
  //  DO("source",RPMTAG_SOURCE);

  QString *vers = a->find("version");
  QString *rel = a->find("release");
  if (rel) {
    *vers += "-";
    *vers += *rel;
    a->remove("release");
  }

  char *k;


  // Get nice description
  headerGetEntry(h, RPMTAG_DESCRIPTION, &type, (void **) &k, &count);
  if(k) {
    char *p;
    for (p = k; *p!=0; p++) {	// remove newlines
      if (*p == '\n') {
	if (*(p+1) == '\n') {
	  p++;
	} else if (*(p+1) == ' ') {
	  p++;
	} else
	  *p = ' ';
      }
    }
    a->insert("description",new QString(QString::fromLocal8Bit(k)));
  }

  // Get nice install time
  headerGetEntry(h, RPMTAG_INSTALLTIME, &type, (void **) &k, &count);
  if(k)
    {
      /* this is important if sizeof(int_32) ! sizeof(time_t) */
      dateint = *(((int_32 *) k) + 0);
      QDateTime datetime;
      datetime.setTime_t(dateint);
      QString str = KGlobal::locale()->formatDateTime(datetime);

      a->insert("installtime", new QString(str));
    }

  // Get nice build time
  headerGetEntry(h, RPMTAG_BUILDTIME, &type, (void **) &k, &count);
  if(k) {
      /* this is important if sizeof(int_32) ! sizeof(time_t) */
      dateint = *(((int_32 *) k) + 0);
      QDateTime datetime;
      datetime.setTime_t(dateint);
      QString str = KGlobal::locale()->formatDateTime(datetime);

      a->insert("build-time", new QString(str));
  }

  // Get nice size
  headerGetEntry(h, RPMTAG_SIZE, &type, (void **) &k, &count);
  if(k) {
      QString s;
      a->insert("size", new QString(s.setNum(*(int_32 *)k)));
  }

#if RPM_V1 > 3 || RPM_V2 > 0 || RPM_V3 >= 3
  const char *error;
#else
  char *error;
#endif
  char *str;
  str = headerSprintf(h, "[%{REQUIRENAME} (%{REQUIREFLAGS:depflags}"
                         " %{REQUIREVERSION}), ]",
		      rpmTagTable, rpmHeaderFormats, &error);
  if (str) {
    QString req = str;
    req.remove(req.length() - 2, 2);
    req.replace(QRegExp("( )"),"");
    //    printf("D=%s=\n",req.data());
    a->insert("depends", new QString(req));
  }

  str = headerSprintf(h, "[%{PROVIDES}, ]",rpmTagTable,
			    rpmHeaderFormats, &error);
  if (str && *str) {
    //    printf("S=%s\n",str);
    QString prov = str;
    prov.remove(prov.length() - 2, 2);

    int s = 0, n;
    QString t;

    while ((n = prov.find(",",s)) > 0) {
      t = prov.mid(s,n-s);
      t = t.stripWhiteSpace();
      provides.insert(t,new QString(*a->find("name")));
      s = n+1;
    }
    t = prov.mid(s);
    t = t.stripWhiteSpace();
    provides.insert(t,new QString(*a->find("name")));

    a->insert("provides", new QString(prov));
  }
  //  printf("S=%s\n",str);

  packageInfo *i = new packageInfo(a,this);
  i->packageState = packageInfo::INSTALLED;
  i->fixup();
  return i;
}
#undef DO


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

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList RPM::getFileList(packageInfo *p)
{

  rpmdb db;
  DECLARE_HEADER_HANDLE;
  int  rc=0, isSource;
  Header h;
  QString prefix(""); // this is the root directory -- should be configured!
  QString name;
  char mode;

  QString fn = p->getFilename();
  if(!fn.isEmpty())
    mode = 'u';
  else
    mode = 'i';

  QStringList filelist;

  switch(mode)
    {
      ////////////////////////////////////////////////////////////////////////
      // query an installed package!
    case 'i':
      name = p->getProperty("name");
      if (rpmdbOpen(const_cast<char*>(prefix.ascii()),&db, O_RDONLY, 0644) )
	return 0;

#if RPM_V1 < 4
      dbiIndexSet matches;

      rc = findPackageByLabel(db, QFile::encodeName(name), &matches);
      if(rc==1)
	KpMsgE(i18n("Package %1 is not installed\n").arg(name),TRUE);
      else if(rc==2)
	KpMsgE(i18n("Error looking for package %1\n").arg(name),TRUE);
      else
	{
	  int i;
	  for(i=0; i<matches.count; i++)
	    {
	      if(matches.recs[i].recOffset)
		{
		  h = rpmdbGetRecord(db, matches.recs[i].recOffset);
		  if(!h)
		    KpMsgE(i18n("Could not read database record\n"),TRUE);
		  else
		    {
		      filelist = RPM::collectFileList(h);
		      headerFree(h);
		    }
		}
	    }
	  dbiFreeIndexRecord(matches);
	}
#else /* RPM v4 */
      {
      rpmdbMatchIterator mi;
      bool found=false;
      mi=rpmdbInitIterator(db, RPMDBI_LABEL, QFile::encodeName(name), 0);
      while((h=rpmdbNextIterator(mi)) != NULL) {
	      found=true;
	      filelist = RPM::collectFileList(h);
      }
      if(!found)
	      KpMsgE(i18n("Could not read database record\n"), TRUE);
      rpmdbFreeIterator(mi);
      }
#endif
      rpmdbClose(db);
      break;

      ////////////////////////////////////////////////////////////////////
      // query an uninstalled package
    case 'u':
      name = fn;
#if RPM_V1 < 4
      if((fd = fdOpen(name.ascii(), O_RDONLY, 0)) < 0)
	{
	  KpMsgE(i18n("ERROR opening %1\n").arg(name),TRUE);
	  return filelist;
	}
#else /* RPM 4 */
      if((fd = Fopen(name.ascii(), "r.fdio")) < 0)
	{
	  KpMsgE(i18n("ERROR opening %1\n").arg(name),TRUE);
	  return filelist;
	}
#endif

      if(fd!=0)	{
	  rc = rpmReadPackageHeader(fd, &h, &isSource, NULL, NULL);
#if RPM_V1 < 4
	  fdClose(fd);
#else /* RPM 4 */
	  Fclose(fd);
#endif
	}
      switch(rc) {
	case 0:
	  if(!h)
	    KpMsgE(i18n("Old format source packages cannot be queried\n"),TRUE);
	  else
	    {
	      filelist = RPM::collectFileList(h);
	      headerFree(h);
	    }
	  break;
	case 1:
	  KpMsgE(i18n("%1 does not appear to be a RPM package\n").arg(name),TRUE);
	  // fallthrough
	case 2:
	  KpMsgE(i18n("Query of %1 failed\n").arg(name),TRUE);
	  return filelist;
	}
      break;
    }

  return filelist;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList RPM::collectFileList(Header h)
{
  char **fileList;
// Code for RPM 3.0.4 or greater
#if RPM_V1 >= 3 && (RPM_V1 > 3 || RPM_V2 > 0 || RPM_V3 >= 4)
  int count2;
#else
  int_32 type, count;
#endif
  QStringList files;

  kpackage->setStatus(i18n("Getting file list"));
  kpackage->setPercent(0);

// Code for RPM 3.0.4 or greater
#if RPM_V1 >= 3 && (RPM_V1 > 3 || RPM_V2 > 0 || RPM_V3 >= 4)

  rpmBuildFileList(h, (const char ***) &fileList, &count2);
  if(fileList != 0)
    {
      int i;
      for(i=0; i<count2; i++)
        {
          kpackage->setPercent( (i/count2) * 100);
          files.append(fileList[i]);
        }
      free(fileList);
    }

#else

  if(headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, &count))
    {
      int i;
      for(i=0; i<count; i++)
	{
	  kpackage->setPercent( (i/count) * 100);
	  files.append(fileList[i]);
	}
      free(fileList);
    }

#endif

  kpackage->setPercent(100);
  return files;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList RPM::depends(const QString &name, int src)
{
  kpackage->setStatus(i18n("Depends"));
  kpackage->setPercent(0);

  int verifyFlags = 0;
  verifyFlags |= VERIFY_DEPS;

  QStringList fls;
  fls.append(name);

  QStringList result;

  doVerify("/", (enum verifysources) src, fls, verifyFlags, result);
  return result;
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList RPM::verify(packageInfo *p, const QStringList &)
{
  kpackage->setStatus(i18n("Verifying"));
  kpackage->setPercent(0);

  int verifyFlags = 0;
  verifyFlags |= VERIFY_FILES |  VERIFY_SCRIPT |  VERIFY_MD5;

  QStringList result;

  QStringList fls;
  fls.append( p->getProperty("name"));

  doVerify("/", VERIFY_PACKAGE, fls, verifyFlags, result);
  return result;
}

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

QString RPM::uninstall(int uninstallFlags, QPtrList<packageInfo> *plist, bool &test)
{
  QStringList 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);
  }
}

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

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

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

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

    if (uninstallFlags>>2 & 1)
      test = TRUE;

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

    return s;
}

QString RPM::doUninst(int uninstallFlags, const QStringList &files, bool &test)
{
  int interfaceFlags = 0;
  int untransFlags = 0;

  param *p = paramsUninst.first();
  if ((uninstallFlags>>0 & 1) ^ p->invert)
    untransFlags|=RPMTRANS_FLAG_NOSCRIPTS;

  p = paramsUninst.next();
  if ((uninstallFlags>>1 & 1) ^ p->invert)
    interfaceFlags|=UNINSTALL_NODEPS;

  p = paramsUninst.next();
  if ((uninstallFlags>>2 & 1) ^ p->invert) {
    untransFlags|= RPMTRANS_FLAG_TEST;
    test = TRUE;
  }

  QString errs;
  int ret = doUninstal("/", files, untransFlags, interfaceFlags, errs) || test;
   if (ret) {
     QString err = "1=";
     err += errs;
     return err;
   } else {
     return "0=";
   }
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QString RPM::install(int installFlags, QPtrList<packageInfo> *plist, bool &test)
{
  QStringList files;

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

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

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


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

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

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

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

    return s;
}

QString RPM::doinst(int installFlags, const QStringList &files, bool &test)
{
  int interfaceFlags = 0 , transFlags = 0, probFlags = 0;

  param *p = paramsInst.first();
  if ((installFlags>>0 & 1) ^ p->invert) {
    interfaceFlags |= INSTALL_UPGRADE;
  }

  p = paramsInst.next();
  if ((installFlags>>1 & 1) ^ p->invert) {
    probFlags|= RPMPROB_FILTER_REPLACEOLDFILES;
    probFlags|= RPMPROB_FILTER_REPLACENEWFILES;
  }

  p = paramsInst.next();
  if ((installFlags>>2 & 1) ^ p->invert) {
    probFlags|=RPMPROB_FILTER_REPLACEPKG;
    probFlags|=RPMPROB_FILTER_OLDPACKAGE;
  }

  p = paramsInst.next();
  if ((installFlags>>3 & 1) ^ p->invert)
    interfaceFlags|=INSTALL_NODEPS;

  p = paramsInst.next();
  if ((installFlags>>4 & 1) ^ p->invert) {
    transFlags|=RPMTRANS_FLAG_TEST;
    test = TRUE;
  }
  interfaceFlags|=INSTALL_PERCENT;

  QString errs;
  int ret = doInstal("/", files, transFlags,
               interfaceFlags,  probFlags, 0, errs) || test;
   if (ret) {
     QString err = "1=";
     err += errs;
     return err;
   } else {
     return "0=";
   }
}

//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QStringList RPM::FindFile(const QString &name) {

  QString s = "";
  QRegExp regexp(QString("*")+name+QString("*"), true, true);

  QStringList filelist;

  int percentage;
#if RPM_V1 < 4
  int offset;
#endif
  rpmdb db;
  Header h;
  const char *prefix = "";
  const char *n;
  int_32 count, type;
  int pos, len;
  QStringList fileList;

  if (!rpmSetup)
    return "";

  kpackage->setStatus(i18n("Querying RPM database for file"));
  percentage=0;
  kpackage->setPercent(percentage);

  if ( rpmdbOpen(prefix,&db, O_RDONLY, 0644) )
    return "";

#if RPM_V1 < 4
  offset = rpmdbFirstRecNum(db);
  while (offset) {
    percentage+=5;
    if(percentage > 100) percentage=0;
    kpackage->setPercent(percentage);
    h = rpmdbGetRecord(db, offset);
    if (!h) {
      KpMsgE(i18n("could not read database record\n"),TRUE);
      return "";
    }

    fileList =  RPM::collectFileList(h);
    for ( QStringList::ConstIterator it = fileList.begin();
	  it != fileList.end();
	  it++ ) {
      if ( (pos = regexp.match(*it, 0, &len)) >= 0) {
	headerGetEntry(h, RPMTAG_NAME, &type, (void **) &n, &count);

	s = n;
	s += "\t";
	s += *it;
	filelist.append(s);
      }
    }

    headerFree(h);
    offset = rpmdbNextRecNum(db, offset);
  }
#else /* RPM v4 */
  rpmdbMatchIterator mi=rpmdbInitIterator(db, RPMDBI_LABEL, NULL, 0);
  while ((h=rpmdbNextIterator(mi))!=NULL) {
    percentage+=5;
    if(percentage > 100) percentage=0;
    kpackage->setPercent(percentage);

    fileList =  RPM::collectFileList(h);
    for ( QStringList::ConstIterator it = fileList.begin();
	  it != fileList.end();
	  it++ ) {
      if ( (pos = regexp.match(*it, 0, &len)) >= 0) {
	headerGetEntry(h, RPMTAG_NAME, &type, (void **) &n, &count);

	s = n;
	s += "\t";
	s += *it;
	filelist.append(s);
      }
    }
  }
  rpmdbFreeIterator(mi);
#endif

  kpackage->setPercent(100);
  rpmdbClose(db);
  kpackage->setStatus("");

  return filelist;
}


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

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

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

#include <kdebug.h>
#include <klocale.h>
#include <kglobal.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 = "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("D"),
			  "Rpm","*.rpm", i18n("Location of Directories Containing RPM Packages"));

 connect(locatedialog,SIGNAL(returnVal(LcacheObj *)),
	 this,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("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: ");

 QDict<QString> provides(1433,false);

 infoList.append("name/%{NAME}");
 infoList.append("version/%{VERSION}");
 infoList.append("release/%{RELEASE}");
 infoList.append("summary/%{SUMMARY}");
 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}]");
}

 RPM::~RPM(){}

bool RPM::isType(char *buf, const QString & /* fname */)
{
  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;
}

bool RPM::parseName(const QString &name, QString *n, QString *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;
}

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

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

  packageInfo *p;
  QStringList  plist;

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

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

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


  if (list.count() > 0) {

    QString s;

    kpackage->setPercent(0 );
    int cnt = 0;
    for ( QStringList::Iterator it = list.begin();  it != list.end(); ++it ) {
      cnt++;
      if (cnt % (NLINES/20) == 0) {
	kpackage->setPercent((cnt * 100)/ NLINES );
      }
      //      kdDebug() << *it << "\n";
      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(QStringList &ln) {

  bool haveName = FALSE;
  QDict<QString> *a = new QDict<QString>;
  a->setAutoDelete(TRUE);

  QString name, value;

  for ( QStringList::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", new QString(value));
    } else if (name == "name") {
      if (!value.isEmpty())
	haveName = TRUE;
      a->insert("name", new QString(value.stripWhiteSpace()));    
    } else if (name == "buildtime") {
      a->insert("build-time", new QString(value));
    } else if (name == "requires") {
      value = value.replace(QRegExp("\\(\\)"),"");
      value = value.replace(QRegExp("\\( \\)"),"");
      value = value.stripWhiteSpace();
      if (value.endsWith(",")) {  
	value.truncate(value.length()-1);
      }
      a->insert("depends", new QString(value));
    } else if (name == "provides") {
      int s = 0, n;
      QString t;

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

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

  QString *vers = a->find("version");
  QString *rel = a->find("release");
  if (rel) {
    *vers += "-";
    *vers += *rel;
    a->remove("release");
  }

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

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

QStringList RPM::getFileList(packageInfo *p)
{
  QStringList filelist;
  QString fn( p->getFilename());

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

  return filelist;
}



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

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

  QStringList filelist = kpty->run(cmd);

  return filelist;
}


// query an uninstalled package
QStringList RPM::getUFileList(QString fn)
{
  QString cmd = "rpm -q -l -p ";
  cmd += quotePath(fn);

  QStringList filelist = kpty->run(cmd);

  return filelist;
}

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

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

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

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

  QStringList 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;
}

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


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

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

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

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

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

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

QString RPM::uninstall(int uninstallFlags, QPtrList<packageInfo> *plist, bool &test)
{
  QStringList 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);
  }
}

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

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

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

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

    if (uninstallFlags>>2 & 1)
      test = TRUE;

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

    return s;
}



//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
QString RPM::install(int installFlags, QPtrList<packageInfo> *plist, bool &test)
{
  QStringList files;

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

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

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


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

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

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

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

    return s;
}

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

//////////////////////////////////////////////////////////////////////////////
QStringList RPM::FindFile(const QString &name) {
  QString cmd = "rpm -q -a --filesbypkg";
    
  QStringList list =  kpty->run(cmd);
  QStringList retlist;
  if (kpty->Result > 0) {
    list.clear();
  } else {
    for ( QStringList::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;
}

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

//////////////////////////////////////////////////////////////////////////////
  QStringList RPM::depends(const QString &, int){return 0;}

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

  QStringList RPM::collectFileList(Header){return 0;}

#endif


#include "rpmInterface.moc"
