2015-09-07 19:36:45 +02:00
# include "./alpmdatabase.h"
# include "./upgradelookup.h"
# include "./alpmpackage.h"
# include "./utilities.h"
2016-02-20 03:00:34 +01:00
# include "./config.h"
# include "../network/networkaccessmanager.h"
2015-09-04 14:37:01 +02:00
# include <c++utilities/misc/memory.h>
2016-02-14 23:48:43 +01:00
# include <KTar>
# include <KArchiveDirectory>
2015-09-05 17:25:05 +02:00
2015-09-04 14:37:01 +02:00
# include <QList>
# include <QJsonObject>
2016-02-20 03:00:34 +01:00
# include <QStringBuilder>
2015-09-05 17:25:05 +02:00
# include <QtConcurrent>
2016-02-20 03:00:34 +01:00
# include <QNetworkAccessManager>
# include <QNetworkRequest>
# include <QNetworkReply>
2015-09-04 14:37:01 +02:00
2016-02-14 23:48:43 +01:00
# include <iostream>
2015-09-04 14:37:01 +02:00
using namespace std ;
2015-09-05 17:25:05 +02:00
namespace RepoIndex {
2015-09-04 14:37:01 +02:00
using namespace Utilities ;
/*!
2016-01-18 20:34:29 +01:00
* \ class AlpmDatabase
* \ brief The AlpmDatabase class wraps an ALPM data base struct and holds additional meta information .
2015-09-04 14:37:01 +02:00
*
2016-01-18 20:34:29 +01:00
* All packages returned by the AlpmDatabase class are AlpmPackage instances .
2015-09-04 14:37:01 +02:00
*/
2015-09-05 17:25:05 +02:00
class LoadPackage
{
public :
2016-02-14 23:48:43 +01:00
LoadPackage ( AlpmDatabase * database , PackageOrigin origin ) :
m_db ( database ) ,
m_origin ( origin )
2015-09-05 17:25:05 +02:00
{ }
2016-02-14 23:48:43 +01:00
void operator ( ) ( const QPair < QString , QList < QByteArray > > & description )
2015-09-05 17:25:05 +02:00
{
2016-02-14 23:48:43 +01:00
m_db - > addPackageFromDescription ( description . first , description . second , m_origin ) ;
2015-09-05 17:25:05 +02:00
}
2015-09-11 21:59:47 +02:00
2015-09-05 17:25:05 +02:00
private :
2016-02-14 23:48:43 +01:00
AlpmDatabase * const m_db ;
const PackageOrigin m_origin ;
2015-09-05 17:25:05 +02:00
} ;
2016-02-25 22:53:33 +01:00
DatabaseError AlpmDatabase : : loadDescriptions ( QList < QPair < QString , QList < QByteArray > > > & descriptions )
2015-09-05 17:25:05 +02:00
{
2016-02-14 23:48:43 +01:00
QFileInfo pathInfo ( databasePath ( ) ) ;
if ( pathInfo . isDir ( ) ) {
static const QStringList relevantFiles = QStringList ( ) < < QStringLiteral ( " desc " ) < < QStringLiteral ( " files " ) ;
QDir dbDir ( databasePath ( ) ) ;
QStringList pkgDirNames = dbDir . entryList ( QDir : : Dirs | QDir : : Readable | QDir : : Executable | QDir : : NoDotAndDotDot ) ;
descriptions . reserve ( pkgDirNames . size ( ) ) ;
for ( QString & pkgDirName : pkgDirNames ) {
if ( dbDir . cd ( pkgDirName ) ) {
Utilities : : stripVersion ( pkgDirName ) ;
const QStringList descFileNames = dbDir . entryList ( relevantFiles , QDir : : Files | QDir : : Readable | QDir : : NoDotAndDotDot ) ;
QList < QByteArray > descData ;
descData . reserve ( descFileNames . size ( ) ) ;
for ( const QString & descFileName : descFileNames ) {
QFile descFile ( dbDir . absoluteFilePath ( descFileName ) ) ;
if ( descFile . open ( QFile : : ReadOnly ) ) {
descData < < descFile . readAll ( ) ;
} else {
2016-02-25 22:53:33 +01:00
return DatabaseError : : UnableToOpenDescFile ;
2016-02-14 23:48:43 +01:00
}
}
if ( ! descData . isEmpty ( ) ) {
descriptions < < qMakePair ( pkgDirName , descData ) ;
}
dbDir . cdUp ( ) ;
} else {
2016-02-25 22:53:33 +01:00
return DatabaseError : : UnableToEnterDirectory ;
2016-02-14 23:48:43 +01:00
}
}
} else if ( pathInfo . isFile ( ) ) {
KTar tar ( databasePath ( ) ) ;
const KArchiveDirectory * dbDir ;
if ( tar . open ( QIODevice : : ReadOnly ) & & ( dbDir = tar . directory ( ) ) ) {
QStringList pkgDirNames = dbDir - > entries ( ) ;
descriptions . reserve ( pkgDirNames . size ( ) ) ;
for ( QString & pkgDirName : pkgDirNames ) {
if ( const auto * pkgEntry = dbDir - > entry ( pkgDirName ) ) {
if ( pkgEntry - > isDirectory ( ) ) {
Utilities : : stripVersion ( pkgDirName ) ;
const auto * pkgDir = static_cast < const KArchiveDirectory * > ( pkgEntry ) ;
const QStringList descFileNames = pkgDir - > entries ( ) ;
QList < QByteArray > descData ;
descData . reserve ( descFileNames . size ( ) ) ;
for ( const QString & descFileName : descFileNames ) {
if ( const auto * descEntry = pkgDir - > entry ( descFileName ) ) {
if ( descEntry - > isFile ( ) ) {
descData < < static_cast < const KArchiveFile * > ( descEntry ) - > data ( ) ;
} else {
2016-02-25 22:53:33 +01:00
// there shouldn't be any subdirs anyways
2016-02-14 23:48:43 +01:00
}
}
}
if ( ! descData . isEmpty ( ) ) {
descriptions < < qMakePair ( pkgDirName , descData ) ;
}
} else {
2016-02-25 22:53:33 +01:00
// there shouldn't be any files anyways
2016-02-14 23:48:43 +01:00
}
}
}
} else {
2016-02-25 22:53:33 +01:00
return DatabaseError : : UnableToOpenArchive ;
2016-02-14 23:48:43 +01:00
}
} else {
2016-02-25 22:53:33 +01:00
return DatabaseError : : NotFound ;
2015-09-05 17:25:05 +02:00
}
2016-02-25 22:53:33 +01:00
return DatabaseError : : NoError ;
2016-02-14 23:48:43 +01:00
}
2016-02-25 22:53:33 +01:00
AlpmPackageLoader : : AlpmPackageLoader ( AlpmDatabase * repository , PackageOrigin origin ) :
m_db ( repository )
2016-02-14 23:48:43 +01:00
{
2016-02-25 22:53:33 +01:00
if ( ( m_error = repository - > loadDescriptions ( m_descriptions ) ) = = DatabaseError : : NoError ) {
m_future = QtConcurrent : : map ( m_descriptions , LoadPackage ( repository , origin ) ) ;
}
2015-09-05 17:25:05 +02:00
}
2015-09-04 14:37:01 +02:00
/*!
* \ brief Creates a new instance wrapping the specified database struct .
*/
2016-02-14 23:48:43 +01:00
AlpmDatabase : : AlpmDatabase ( const QString & name , const QString & dbPath , RepositoryUsage usage , SignatureLevel sigLevel , uint32 index , QObject * parent ) :
Repository ( name , index , parent ) ,
m_dbPath ( dbPath )
{
m_usage = usage ;
m_sigLevel = sigLevel ;
}
2015-09-05 17:25:05 +02:00
2016-02-25 22:53:33 +01:00
AlpmPackageLoader * AlpmDatabase : : internalInit ( )
2015-09-04 14:37:01 +02:00
{
2016-02-14 23:48:43 +01:00
// set description, determine origin
PackageOrigin origin ;
2015-09-04 14:37:01 +02:00
if ( m_name . compare ( QLatin1String ( " local " ) , Qt : : CaseInsensitive ) = = 0 ) {
m_description = QStringLiteral ( " The local database " ) ;
2016-02-14 23:48:43 +01:00
origin = PackageOrigin : : LocalDb ;
2015-09-04 14:37:01 +02:00
} else {
2016-02-14 23:48:43 +01:00
if ( ( m_usage & RepositoryUsage : : Sync ) | | ( m_usage & RepositoryUsage : : Install ) | | ( m_usage & RepositoryUsage : : Upgrade ) ) {
2015-09-04 14:37:01 +02:00
m_description = QStringLiteral ( " Sync database »%1« " ) . arg ( m_name ) ;
} else {
m_description = QStringLiteral ( " Database »%1« " ) . arg ( m_name ) ;
}
2016-02-14 23:48:43 +01:00
origin = PackageOrigin : : SyncDb ;
2015-09-04 14:37:01 +02:00
}
2016-02-20 03:00:34 +01:00
// wipe current packages
wipePackages ( ) ;
2016-02-25 22:53:33 +01:00
// initialization of packages is done concurrently via AlpmPackageLoader
2016-02-14 23:48:43 +01:00
return new AlpmPackageLoader ( this , origin ) ;
2016-02-25 22:53:33 +01:00
// without concurrency
2016-02-14 23:48:43 +01:00
//QList<QPair<QString, QList<QByteArray> > > descriptions;
//loadDescriptions(descriptions);
//for(const auto &description : descriptions) {
// addPackageFromDescription(description.first, description.second, origin);
//}
2016-02-25 22:53:33 +01:00
//emit initialized();
2016-02-14 23:48:43 +01:00
//return nullptr;
}
2015-09-04 14:37:01 +02:00
2015-09-05 17:25:05 +02:00
RepositoryType AlpmDatabase : : type ( ) const
2015-09-04 14:37:01 +02:00
{
2015-09-05 17:25:05 +02:00
return RepositoryType : : AlpmDatabase ;
2015-09-04 14:37:01 +02:00
}
2015-09-05 17:25:05 +02:00
PackageDetailAvailability AlpmDatabase : : requestsRequired ( PackageDetail packageDetail ) const
2015-09-04 14:37:01 +02:00
{
2015-09-05 17:25:05 +02:00
switch ( packageDetail ) {
case PackageDetail : : Basics :
case PackageDetail : : Dependencies :
case PackageDetail : : PackageInfo :
2015-09-11 21:59:47 +02:00
case PackageDetail : : AllAvailable :
2015-09-05 17:25:05 +02:00
return PackageDetailAvailability : : Immediately ;
default :
return PackageDetailAvailability : : Never ;
}
2015-09-04 14:37:01 +02:00
}
2016-02-20 03:00:34 +01:00
QNetworkRequest AlpmDatabase : : regularDatabaseRequest ( )
{
return QNetworkRequest ( QUrl ( serverUrls ( ) . front ( ) % QChar ( ' / ' ) % name ( ) % QStringLiteral ( " .db " ) ) ) ;
}
QNetworkRequest AlpmDatabase : : filesDatabaseRequest ( )
{
return QNetworkRequest ( QUrl ( serverUrls ( ) . front ( ) % QChar ( ' / ' ) % name ( ) % QStringLiteral ( " .files " ) ) ) ;
}
/*!
* \ brief Downloads the database from the server .
* \ param targetDir Specifies the directory to store the downloaded database file . Shall not include the filename .
* \ remarks
* - The download is performed asynchronously - this method returns immediately .
* - After successfull download the database path is update to the path of the new file and the
* repository is reinitiated .
* - Does nothing if there is not at least one server URL available .
* - Status messages are printed via cerr .
*/
void AlpmDatabase : : downloadDatabase ( const QString & targetDir , bool filesDatabase )
{
2016-02-25 22:53:33 +01:00
QWriteLocker locker ( lock ( ) ) ;
2016-02-20 03:00:34 +01:00
if ( serverUrls ( ) . isEmpty ( ) ) {
2016-02-25 22:53:33 +01:00
return ; // no server URLs available
2016-02-20 03:00:34 +01:00
}
cerr < < shchar < < " Downloading " < < ( filesDatabase ? " files " : " regular " ) < < " database for [ " < < name ( ) . toLocal8Bit ( ) . data ( ) < < " ] from mirror " < < serverUrls ( ) . front ( ) . toLocal8Bit ( ) . data ( ) < < " ... " < < endl ;
QNetworkReply * reply = networkAccessManager ( ) . get ( filesDatabase ? filesDatabaseRequest ( ) : regularDatabaseRequest ( ) ) ;
reply - > setProperty ( " filesDatabase " , filesDatabase ) ;
m_downloadTargetDir = targetDir . isEmpty ( ) ? QString ( QChar ( ' . ' ) ) : targetDir ;
connect ( reply , & QNetworkReply : : finished , this , & AlpmDatabase : : databaseDownloadFinished ) ;
}
/*!
* \ brief Refreshes the database by downloading it from the server
* or just reinitializing it if there is not at least one server URL available .
* \ param targetDir Specifies the directory to store the downloaded database file . Shall not include the filename .
* Ignored when downloading is not possible .
* \ remarks Effectively updates the database file of sync databases and just refreshes
* local databases .
*/
void AlpmDatabase : : refresh ( const QString & targetDir )
{
if ( serverUrls ( ) . isEmpty ( ) ) {
init ( ) ;
} else {
downloadDatabase ( targetDir , true ) ;
}
}
2016-02-12 01:05:08 +01:00
std : : unique_ptr < Package > AlpmDatabase : : emptyPackage ( )
{
return make_unique < AlpmPackage > ( this ) ;
}
2016-02-20 03:00:34 +01:00
/*!
* \ brief Internally called to handle finishing of database download .
* \ remarks Connected in downloadDatabase ( ) .
*/
void AlpmDatabase : : databaseDownloadFinished ( )
{
auto * reply = static_cast < QNetworkReply * > ( sender ( ) ) ;
2016-02-25 22:53:33 +01:00
reply - > deleteLater ( ) ;
2016-02-20 03:00:34 +01:00
bool filesDatabase = reply - > property ( " filesDatabase " ) . toBool ( ) ;
2016-02-25 22:53:33 +01:00
QReadLocker locker ( lock ( ) ) ;
2016-02-20 03:00:34 +01:00
if ( reply - > error ( ) = = QNetworkReply : : NoError ) {
2016-02-25 22:53:33 +01:00
QString newDatabasePath ;
2016-02-20 03:00:34 +01:00
cerr < < " Downloaded database file for [ " < < name ( ) . toLocal8Bit ( ) . data ( ) < < " ] successfully. " < < endl ;
2016-02-25 22:53:33 +01:00
newDatabasePath = m_downloadTargetDir % QChar ( ' / ' ) % name ( ) % ( filesDatabase ? QStringLiteral ( " .files " ) : QStringLiteral ( " .db " ) ) ;
2016-02-20 03:00:34 +01:00
if ( QFile : : exists ( newDatabasePath ) ) {
QString backupFile ( newDatabasePath + QStringLiteral ( " .bak " ) ) ;
QFile : : remove ( backupFile ) ;
if ( ! QFile : : rename ( newDatabasePath , backupFile ) ) {
cerr < < " An IO error occured when storing database file for [ " < < name ( ) . toLocal8Bit ( ) . data ( ) < < " ]: Unable to rename present database file. " < < endl ;
2016-02-25 22:53:33 +01:00
reply = nullptr ;
2016-02-20 03:00:34 +01:00
return ;
}
}
2016-02-25 22:53:33 +01:00
locker . unlock ( ) ;
2016-02-20 03:00:34 +01:00
QFile outputFile ( newDatabasePath ) ;
if ( outputFile . open ( QFile : : WriteOnly ) & & outputFile . write ( reply - > readAll ( ) ) ) {
outputFile . close ( ) ;
2016-02-25 22:53:33 +01:00
{
QWriteLocker locker ( lock ( ) ) ;
m_dbPath = newDatabasePath ;
}
initAsSoonAsPossible ( ) ;
2016-02-20 03:00:34 +01:00
} else {
2016-02-25 22:53:33 +01:00
locker . relock ( ) ;
2016-02-20 03:00:34 +01:00
cerr < < " An IO error occured when storing database file for [ " < < name ( ) . toLocal8Bit ( ) . data ( ) < < " ]: Unable to create/write output file. " < < endl ;
}
} else {
cerr < < " An error occured when dwonloading database file for [ " < < name ( ) . toLocal8Bit ( ) . data ( ) < < " ]: " < < reply - > errorString ( ) . toLocal8Bit ( ) . data ( ) < < endl ;
2016-02-25 22:53:33 +01:00
if ( filesDatabase & & reply - > error ( ) = = QNetworkReply : : ContentNotFoundError ) {
cerr < < " -> Attempting to download regular database file instead of files database file. " < < endl ;
locker . unlock ( ) ;
downloadDatabase ( m_downloadTargetDir , false ) ;
}
2016-02-20 03:00:34 +01:00
}
}
2015-09-04 14:37:01 +02:00
} // namespace Alpm