akabeihelpers.cpp 43.8 KB
Newer Older
1 2 3
/* This file is part of the Chakra project

   Copyright (C) 2010 Dario Freddi <drf@chakra-project.org>
4
   Copyright (C) 2011 - 2012 Lukas Appelhans <l.appelhans@gmx.de>
5 6 7 8 9 10 11 12 13 14 15

   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.

   A big portion of this code was taken from ArchLinux's pacman
   Copyright (c) 2006-2009 Pacman Development Team <pacman-dev@archlinux.org>
   Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
*/

16 17 18 19 20 21 22 23 24 25
#include <akabeihelpers_p.h>

#include <akabeiconfig.h>
#include <akabeigroup.h>
#include <akabeihook.h>
#include <akabeidelta.h>
#include <libarchive++/archivehandler.h>
#include <md5.h>
#include <akabeidatabase_p.h>
#include <akabeipackage_p.h>
26 27

#include <QDir>
28
#include <QUrl>
29 30 31
#include <QCoreApplication>
#include <QDBusInterface>
#include <QDBusMessage>
32
#include <akabeidebug.h>
Lisa's avatar
Lisa committed
33
#include <akabeiquery.h>
34

Dario Freddi's avatar
Dario Freddi committed
35 36
#include <stdlib.h>

37 38 39
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
40
#include <sys/statvfs.h>
41 42 43

#include <PolkitQt1/Authority>

44 45
namespace Akabei
{
46

47 48
class QueryHelper
{
Lukas Appelhans's avatar
Lukas Appelhans committed
49 50 51 52
public:
    static sqlite3 *dbFromAkabeiDatabase(Akabei::Database *db);
    static PackagePrivate *packagePrivateFromPackage(Akabei::Package *p);
    static DatabasePrivate *databasePrivateFromDatabase(Akabei::Database *db);
53 54 55 56 57 58
};

PackagePrivate* QueryHelper::packagePrivateFromPackage(Package* p)
{
    return p->d_func();
}
59

60 61 62 63 64 65 66 67
DatabasePrivate* QueryHelper::databasePrivateFromDatabase(Database* db)
{
    return db->d_func();
}

namespace Helpers
{

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
QString formatByteSize(double size)
{
    // Per IEC 60027-2

    // Binary prefixes
    //Tebi-byte             TiB             2^40    1,099,511,627,776 bytes
    //Gibi-byte             GiB             2^30    1,073,741,824 bytes
    //Mebi-byte             MiB             2^20    1,048,576 bytes
    //Kibi-byte             KiB             2^10    1,024 bytes

    QString s;
    // Gibi-byte
    if ( size >= 1073741824.0 ) {
        size /= 1073741824.0;
        if ( size > 1024 ) // Tebi-byte
            s = QObject::tr("%1 TiB").arg(QLocale().toString(size / 1024.0, 'f', 1));
        else
            s = QObject::tr("%1 GiB").arg(QLocale().toString(size, 'f', 1));
    }
    // Mebi-byte
    else if ( size >= 1048576.0 ) {
        size /= 1048576.0;
        s = QObject::tr("%1 MiB").arg(QLocale().toString(size, 'f', 1));
    }
    // Kibi-byte
    else if ( size >= 1024.0 ) {
        size /= 1024.0;
        s = QObject::tr("%1 KiB").arg(QLocale().toString(size, 'f', 1));
    }
    // Just byte
    else if ( size > 0 ) {
        s = QObject::tr("%1 B").arg(QLocale().toString(size, 'f', 1));
    }
    // Nothing
    else {
        s = QObject::tr("0 B");
    }
    return s;
}
107 108 109 110 111 112 113 114 115 116 117 118 119

qlonglong availablePartitionSpace(const QByteArray& path)
{
    qlonglong freeSpace = 0;
    struct statvfs statvfs_buf;
    
    if (statvfs(path.constData(), &statvfs_buf) == 0) {
        const quint64 blksize = quint64( statvfs_buf.f_frsize ); // cast to avoid overflow
        freeSpace = statvfs_buf.f_bavail * blksize;
    }
    
    return freeSpace;
}
120
    
121
/*
Lisa's avatar
Lisa committed
122
 * NOTE: a rule is applied which doesn't seem obvious:
123 124
 * if we have 2.3-1 and 2.3, 0 is returned (so they're equal); this aims to maintain
 * backward compatibility for when pkgrels weren't used.
125
 *
Lisa's avatar
Lisa committed
126
 * Another note: this function assumes a and b are well-formed version strings
127
 */
128
int compare_versions(QString const& a, QString const& b)
129
{
Lisa's avatar
Lisa committed
130
    /* First split takes pkgrels */
131 132
    QStringList withpkgrel1 = a.split(QStringLiteral("-"));
    QStringList withpkgrel2 = b.split(QStringLiteral("-"));
Lisa's avatar
Lisa committed
133
    QString pkgrel1, pkgrel2;
134

Lisa's avatar
Lisa committed
135 136 137 138 139 140
    if (withpkgrel1.size() >= 2) {
        pkgrel1 = withpkgrel1.at(1);
    }
    if (withpkgrel2.size() >= 2) {
        pkgrel2 = withpkgrel2.at(1);
    }
141 142 143 144

    QString s1( withpkgrel1.first() ); /* takes the rest */
    QString s2( withpkgrel2.first() );

Lisa's avatar
Lisa committed
145
    /* Second split is to separate actual version numbers (or strings) */
146 147
    QStringList v1 = s1.split(QStringLiteral("."));
    QStringList v2 = s2.split(QStringLiteral("."));
148

149 150
    QStringList::iterator i1 = v1.begin();
    QStringList::iterator i2 = v2.begin();
151 152

    for (; i1 < v1.end() && i2 < v2.end() ; i1++, i2++) {
153 154 155 156 157 158 159 160
        bool conversion1, conversion2;
        int p1 = i1->toInt(&conversion1);
        int p2 = i2->toInt(&conversion2);
        
        //Basically we have something that are not only strings
        if (!conversion1 || !conversion2) {
            for (int i = 0; i != i1->length(); ++i) {
                if (i2->length() > i && i1->at(i) != i2->at(i))
AlmAck's avatar
AlmAck committed
161
                    return i1->at(i).toLatin1() > i2->at(i).toLatin1() ? 1 : -1;
162
                
163 164
                p1 = i1->midRef(i + 1).toInt(&conversion1);
                p2 = i2->midRef(i + 1).toInt(&conversion2);
165 166 167 168 169
                
                if (conversion1 && conversion2)
                    break;
            }
        }
170

Lisa's avatar
Lisa committed
171
        if (p1 > p2) {
172 173
            return 1;
        }
Lisa's avatar
Lisa committed
174
        else if (p1 < p2) {
175 176 177
            return -1;
        }
    }
178

179 180 181 182
    /* This is, like, v1 = 2.3 and v2 = 2.3.1: v2 wins */
    if (i1 == v1.end() && i2 != v2.end()) {
        return -1;
    }
183

184 185 186 187
    /* The opposite case as before */
    if (i2 == v2.end() && i1 != v1.end()) {
        return 1;
    }
188

Lisa's avatar
Lisa committed
189 190 191 192
    /* The rule explained above */
    if ((!pkgrel1.isEmpty() && pkgrel2.isEmpty()) || (pkgrel1.isEmpty() && !pkgrel2.isEmpty())) {
        return 0;
    }
193

Lisa's avatar
Lisa committed
194 195 196
    /* Normal pkgrel comparison */
    int pg1 = pkgrel1.toInt();
    int pg2 = pkgrel2.toInt();
197

Lisa's avatar
Lisa committed
198 199 200 201 202
    if (pg1 > pg2) {
        return 1;
    } else if (pg2 > pg1) {
        return -1;
    }
203

204
    return 0;
205 206
}

207 208
QByteArray processMd5(unsigned char output[16])
{
209
    /* allocate 32 chars plus 1 for null */
210 211
    char *_p_md5sum = (char*)calloc(33, sizeof(char));

212
    /* Convert the result to something readable */
213
    for (int i = 0; i < 16; i++) {
214
        /* sprintf is acceptable here because we know our output */
215 216
        sprintf(_p_md5sum +(i * 2), "%02x", output[i]);
    }
217
    _p_md5sum[32] = '\0';
218

219
    QByteArray md5sumComputed(_p_md5sum);
220

221 222 223 224 225 226 227
    free(_p_md5sum);

    return md5sumComputed;
}

QByteArray md5sum(const QByteArray& data)
{
228
    unsigned char output[16];
Lukas Appelhans's avatar
Lukas Appelhans committed
229
    unsigned char * bytes = (unsigned char*)data.data();
230

Lukas Appelhans's avatar
Lukas Appelhans committed
231
    md5(bytes, data.length(), output);
232
    return processMd5(output);
233 234
}

235
QByteArray md5sumOfFileWithoutPolkit(const QString &path)
236 237 238 239 240 241 242
{
    unsigned char output[16];
    int ret;

    ret = md5_file(path.toUtf8().data(), output);

    if (ret > 0) {
243
        // Uhm... what's up here?
244 245 246 247 248 249
        return QByteArray();
    }

    return processMd5(output);
}

250 251
QByteArray md5sumOfFile(const QString& path)
{
252
    if (!QFileInfo(path).isReadable()) {
253
        if (Helpers::checkAuthorizationSync(QStringLiteral("org.chakraproject.akabeicorehelper.filesystem.read"))) {
254
            qDebug() << "Authorization granted";
255
            QDBusInterface iface(QStringLiteral("org.chakraproject.akabeicorehelper"), QStringLiteral("/filesystem"), QStringLiteral("org.chakraproject.akabeicorehelper.filesystem"), QDBusConnection::systemBus());
256

257
            QDBusMessage mes = iface.call(QStringLiteral("md5sumOfFile"), path);
258 259 260 261 262 263 264 265 266 267
            if (!mes.arguments().isEmpty())
                return mes.arguments().first().toByteArray();
        } else {
            qDebug() << "Authorization not granted" << PolkitQt1::Authority::instance()->errorDetails();
        }
        return QByteArray();
    }
    return md5sumOfFileWithoutPolkit(path);
}

Dario Freddi's avatar
Dario Freddi committed
268 269 270 271 272 273 274 275 276 277 278 279 280
QHash< QString, QString > versionedTargets(const QStringList &targets)
{
    QHash< QString, QString > rethash;
    foreach (const QString &target, targets) {
        QPair< QString, QString > vTarget = versionedTarget(target);
        rethash.insert(vTarget.first, vTarget.second);
    }

    return rethash;
}

QPair< QString, QString > versionedTarget(const QString& target)
{
281
    QString separator;
282 283 284 285
    if (target.contains(QStringLiteral(">="))) {
        separator = QStringLiteral(">=");
    } else if (target.contains(QStringLiteral("<="))) {
        separator = QStringLiteral("<=");
286 287
    } else if (target.contains('<')) {
        separator = '<';
Dario Freddi's avatar
Dario Freddi committed
288
    } else if (target.contains('>')) {
289
        separator = '>';
Dario Freddi's avatar
Dario Freddi committed
290
    } else if (target.contains('=')) {
291 292 293 294 295
        separator = '=';
    }
    if (!separator.isEmpty()) { //We found some versioned dependency
        QStringList split = target.split(separator);
        return qMakePair<QString, QString>(split.first(), separator + split.last());
Dario Freddi's avatar
Dario Freddi committed
296
    }
297
    return qMakePair<QString, QString>(target, QString());
Dario Freddi's avatar
Dario Freddi committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
}

QStringList unversionedTargets(const QStringList &targets)
{
    QStringList retlist;
    foreach (const QString &target, targets) {
        retlist << unversionedTarget(target);
    }

    return retlist;
}

QString unversionedTarget(const QString& target)
{
    if (target.contains('<')) {
        return target.split('<').first();
    } else if (target.contains('>')) {
        return target.split('>').first();
    } else if (target.contains('=')) {
        return target.split('=').first();
    } else {
        // No version costraint.
        return target;
    }
}

324
QStringList stringlistFromDb(const QString &dbstr)
325 326 327
{
    QStringList retlist = dbstr.split(';');

328
    if (!retlist.isEmpty() && retlist.first().isEmpty()) {
329 330 331
        retlist.removeFirst();
    }

332
    if (!retlist.isEmpty() && retlist.last().isEmpty()) {
333 334
        retlist.removeLast();
    }
335

336 337 338 339

    return retlist;
}

340 341 342
Package* latestVersionOfPackage(const QString& pkgName)
{
    Package::Version latestVersion;
343
    Package *latestPkg = nullptr;
Lukas Appelhans's avatar
Lukas Appelhans committed
344

345 346 347 348 349 350 351 352
    foreach (Akabei::Database *db, Akabei::Backend::instance()->databases()) {
        foreach (Akabei::Package *pkg, db->searchPackages(pkgName, SearchNameEqual)) {
            if (pkg->version() > latestVersion) {
                latestVersion = pkg->version();
                latestPkg = pkg;
            }
        }
    }
Lukas Appelhans's avatar
Lukas Appelhans committed
353

354 355 356
    return latestPkg;
}

357 358 359 360 361
bool removeFile(const QString &file)
{
    if (!Akabei::Config::instance()->needsPrivileges())
        return QFile::remove(file);

362
    if (Helpers::checkAuthorizationSync(QStringLiteral("org.chakraproject.akabeicorehelper.filesystem.remove"))) {
363
        qDebug() << "Authorization granted";
364
        QDBusInterface iface(QStringLiteral("org.chakraproject.akabeicorehelper"), QStringLiteral("/filesystem"), QStringLiteral("org.chakraproject.akabeicorehelper.filesystem"), QDBusConnection::systemBus());
365

366
        QDBusMessage mes = iface.call(QStringLiteral("remove"), file);
367 368 369
        if (!mes.arguments().isEmpty())
            return mes.arguments().first().toBool();
    } else {
Lukas Appelhans's avatar
Lukas Appelhans committed
370 371 372 373 374 375 376 377 378 379
        Akabei::ErrorQueue::instance()->appendError(Akabei::Error(Akabei::Error::PermissionError, PolkitQt1::Authority::instance()->errorDetails()));
    }
    return false;
}

bool copyFile(const QString &source, const QString &destination)
{
    if (!Akabei::Config::instance()->needsPrivileges())
        return QFile::copy(source, destination);

380
    if (Helpers::checkAuthorizationSync(QStringLiteral("org.chakraproject.akabeicorehelper.filesystem.copy"))) {
Lukas Appelhans's avatar
Lukas Appelhans committed
381
        qDebug() << "Authorization granted";
382
        QDBusInterface iface(QStringLiteral("org.chakraproject.akabeicorehelper"), QStringLiteral("/filesystem"), QStringLiteral("org.chakraproject.akabeicorehelper.filesystem"), QDBusConnection::systemBus());
Lukas Appelhans's avatar
Lukas Appelhans committed
383

384
        QDBusMessage mes = iface.call(QStringLiteral("copy"), source, destination);
Lukas Appelhans's avatar
Lukas Appelhans committed
385 386 387 388
        if (!mes.arguments().isEmpty())
            return mes.arguments().first().toBool();
    } else {
        Akabei::ErrorQueue::instance()->appendError(Akabei::Error(Akabei::Error::PermissionError, PolkitQt1::Authority::instance()->errorDetails()));
389 390 391 392 393 394 395 396 397
    }
    return false;
}

Permissions permissionsOfPath(const QString &path)
{
    //NOTE: Don't EVER put a qDebug() in here, it will let akabei freeze at the start because of our heavy mutexing

    Permissions perm = NoPermission;
Lukas Appelhans's avatar
Lukas Appelhans committed
398 399
    QFileInfo file(path);
    if (file.isWritable()) {
400 401
        perm = perm | WritePermission;
    }
Lukas Appelhans's avatar
Lukas Appelhans committed
402
    if (file.isReadable()) {
403 404 405 406 407 408 409
        perm = perm | ReadPermission;
    }
    return perm;
}

bool checkAuthorizationSync(const QString &action)
{
410 411 412 413
    return PolkitQt1::Authority::Yes == PolkitQt1::Authority::instance()->checkAuthorizationSync(
        action,
        PolkitQt1::UnixProcessSubject(static_cast<uint>(QCoreApplication::applicationPid())),
        PolkitQt1::Authority::AllowUserInteraction);
414 415
}

416 417 418
///////
class PackageEventLoop::Private
{
Lukas Appelhans's avatar
Lukas Appelhans committed
419 420 421
public:
    QUuid uuid;
    QList<Akabei::Package*> result;
422 423 424
};

PackageEventLoop::PackageEventLoop(QObject *parent)
Lukas Appelhans's avatar
Lukas Appelhans committed
425 426
        : QEventLoop(parent)
        , d(new Private)
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
{
}

PackageEventLoop::~PackageEventLoop()
{
    delete d;
}

void PackageEventLoop::setUuid(const QUuid &uuid)
{
    d->uuid = uuid;
}

QList<Akabei::Package*> PackageEventLoop::result() const
{
    return d->result;
}

void PackageEventLoop::requestQuit(const QUuid &uuid, const QList<Akabei::Package*> &result) {
    if (uuid == d->uuid) {
        d->result = result;
        quit();
    }
}

//////

454 455 456
QString stringFromStringList(const QStringList& list)
{
    QString retstring;
457
    retstring.append(';');
458 459 460 461 462 463 464
    foreach (const QString &str, list) {
        retstring.append(str);
        retstring.append(';');
    }
    return retstring;
}

465
int QueryPerformer::beginTransaction(Database* db)
466
{
467
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
468

469 470 471 472 473
    return beginTransaction(d->dbConnection);
}

int QueryPerformer::beginTransaction(AbstractSQLiteConnection& dbConnection)
{
474
    try {
475
        dbConnection.query( Queries::begin() );
476
    } catch (SQLiteException& e) {
Lukas Appelhans's avatar
Lukas Appelhans committed
477
        return SQLITE_ABORT;
478 479
    }

480
    return SQLITE_OK;
481 482
}

483
int QueryPerformer::commitTransaction(Database* db)
484
{
485
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
486

487 488 489 490 491
    return commitTransaction(d->dbConnection);
}

int QueryPerformer::commitTransaction(AbstractSQLiteConnection& dbConnection)
{
492
    try {
493
        dbConnection.query( Queries::commit() );
494
    } catch (SQLiteException& e) {
Lukas Appelhans's avatar
Lukas Appelhans committed
495
        return SQLITE_ABORT;
496 497
    }

498
    return SQLITE_OK;
499 500
}

501
int QueryPerformer::rollbackTransaction(Database* db)
502
{
503
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
504

505 506 507 508 509
    return rollbackTransaction(d->dbConnection);
}

int QueryPerformer::rollbackTransaction(AbstractSQLiteConnection& dbConnection)
{
510
    try {
511
        dbConnection.query( Queries::rollback() );
512
    } catch (SQLiteException& e) {
Lukas Appelhans's avatar
Lukas Appelhans committed
513
        return SQLITE_ABORT;
514
    }
Dario Freddi's avatar
Dario Freddi committed
515

516
    return SQLITE_OK;
Dario Freddi's avatar
Dario Freddi committed
517 518 519
}

int QueryPerformer::insertPackage(Database *db, Package* p)
520
{
Lukas Appelhans's avatar
Lukas Appelhans committed
521 522
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return insertPackage(d->dbConnection, p);
523 524
}

525
int QueryPerformer::insertPackage(AbstractSQLiteConnection &dbConnection, Package* p)
526
{
527
    QString null = QStringLiteral("NULL");
528

529
    try {
Lukas Appelhans's avatar
Lukas Appelhans committed
530 531 532
        // That's gonna be long.
        // Query first.
        QString sqlQuery =
533
            "INSERT INTO packages ('name', 'version', 'epoch', 'filename', 'description', 'size', 'installedsize', "
Lukas Appelhans's avatar
Lukas Appelhans committed
534
            "'md5sum', 'url', 'builddate', 'arch', 'packager', "
535
            "'flags', 'screenshoturl', 'installreason', 'installdate', 'gitrepository', 'gitbranch', 'gitfolder') "
Lukas Appelhans's avatar
Lukas Appelhans committed
536
            "VALUES ("
537
            ":Name, :Version, :Epoch, :Filename, :Description, :Size, :InstalledSize, "
Lukas Appelhans's avatar
Lukas Appelhans committed
538
            ":MD5SUM, :URL, :BuildDate, :Arch, :Packager, "
539
            ":Flags, :ScreenShotURL, :InstallReason, :InstallDate, :GitRepo, :GitBranch, :GitFolder)";
Lukas Appelhans's avatar
Lukas Appelhans committed
540

541 542 543 544 545 546 547 548
        dbConnection.bind(QStringLiteral(":Name"), p->name());
        dbConnection.bind(QStringLiteral(":Version"), p->version().toByteArray());
        dbConnection.bind(QStringLiteral(":Epoch"), p->version().epoch());
        dbConnection.bind(QStringLiteral(":Filename"), p->filename());
        dbConnection.bind(QStringLiteral(":Description"), p->description());
        dbConnection.bind(QStringLiteral(":Size"), p->size());
        dbConnection.bind(QStringLiteral(":InstalledSize"), p->installedSize());
        dbConnection.bind(QStringLiteral(":MD5SUM"), p->md5sum());
Lukas Appelhans's avatar
Lukas Appelhans committed
549 550

        if (p->url().isValid()) {
551
            dbConnection.bind(QStringLiteral(":URL"), p->url().toString());
Lukas Appelhans's avatar
Lukas Appelhans committed
552
        } else {
553
            dbConnection.bind(QStringLiteral(":URL"), null);
Lukas Appelhans's avatar
Lukas Appelhans committed
554 555
        }

556 557 558 559
        dbConnection.bind(QStringLiteral(":BuildDate"), p->buildDate().toTime_t());
        dbConnection.bind(QStringLiteral(":Arch"), p->arch());
        dbConnection.bind(QStringLiteral(":Packager"), p->packager());
        dbConnection.bind(QStringLiteral(":Flags"), null); // TODO I still need to decide how to handle flags, bind to null
Lukas Appelhans's avatar
Lukas Appelhans committed
560

561
        if (p->screenshot().isValid()) {
562
            dbConnection.bind(QStringLiteral(":ScreenShotURL"), p->screenshot().toString());
Lukas Appelhans's avatar
Lukas Appelhans committed
563
        } else {
564
            dbConnection.bind(QStringLiteral(":ScreenShotURL"), null);
Lukas Appelhans's avatar
Lukas Appelhans committed
565 566
        }

567 568 569 570 571
        dbConnection.bind(QStringLiteral(":InstallReason"), (int)p->installReason());
        dbConnection.bind(QStringLiteral(":InstallDate"), p->installDate().toTime_t());
        dbConnection.bind(QStringLiteral(":GitRepo"), p->gitRepo());
        dbConnection.bind(QStringLiteral(":GitBranch"), p->gitBranch());
        dbConnection.bind(QStringLiteral(":GitFolder"), p->gitFolder());
Lukas Appelhans's avatar
Lukas Appelhans committed
572 573 574

        dbConnection.query(sqlQuery);
        int id = dbConnection.getLastRowId();
Lukas Appelhans's avatar
Lukas Appelhans committed
575
        QueryHelper::packagePrivateFromPackage(p)->setDatabaseId(id);
Lukas Appelhans's avatar
Lukas Appelhans committed
576 577 578 579

        foreach (Akabei::Group *group, p->groups()) {
            // Can I assume the group already exists?
            //dbConnection.query("INSERT INTO groups ('name') VALUES ('" + group->name() + "');");
580 581 582
            sqlQuery = QStringLiteral("INSERT INTO belongsgroup ('package', 'groupname') VALUES (:Package, :Group);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":Group"), group->name());
583
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
584 585
        }

586
        foreach (const QString& dependency, p->dependencies()) {
587 588 589
            sqlQuery = QStringLiteral("INSERT INTO depends ('package', 'dependency') VALUES (:Package, :Depend);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":Depend"), dependency);
590
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
591
        }
Lukas Appelhans's avatar
Lukas Appelhans committed
592

Lisa's avatar
Lisa committed
593
        foreach (const QString& makedep, p->makeDependencies()) {
594 595 596
            sqlQuery = QStringLiteral("INSERT INTO makedepends ('package', 'dependency') VALUES (:Package, :MakeDepend);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":MakeDepend"), makedep);
Lisa's avatar
Lisa committed
597 598
            dbConnection.query(sqlQuery);
        }
Lukas Appelhans's avatar
Lukas Appelhans committed
599

600
        foreach (const QString& opt, p->optionalDependencies()) {
601 602 603
            sqlQuery = QStringLiteral("INSERT INTO optional ('package', 'dependency') VALUES (:Package, :OptDepend);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":OptDepend"), opt);
604
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
605 606
        }

607
        foreach (const QString& replace, p->replaces()) {
608 609 610
            sqlQuery = QStringLiteral("INSERT INTO replaces ('package', 'replaces') VALUES (:Package, :Replace);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":Replace"), replace);
611
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
612 613
        }

614
        foreach (const QString& provides, p->provides()) {
615 616 617
            sqlQuery = QStringLiteral("INSERT INTO provides ('package', 'provides') VALUES (:Package, :Provides);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":Provides"), provides);
618
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
619 620
        }

621
        foreach (const QString& conflict, p->conflictsWith()) {
622 623 624
            sqlQuery = QStringLiteral("INSERT INTO conflicts ('package', 'conflict') VALUES (:Package, :Conflict);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":Conflict"), conflict);
625
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
626
        }
627

Lukas Appelhans's avatar
Lukas Appelhans committed
628
        foreach (const QString &mimetype, p->mimetypes()) {
629 630 631
            sqlQuery = QStringLiteral("INSERT INTO providesmimetype ('package', 'mimetype') VALUES (:Package, :MimeType);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":MimeType"), mimetype);
632
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
633
        }
634

635
        foreach (const QString& license, p->licenses()) {
636 637 638
            sqlQuery = QStringLiteral("INSERT INTO licensed ('package', 'license') VALUES (:Package, :License);");
            dbConnection.bind(QStringLiteral(":Package"), id);
            dbConnection.bind(QStringLiteral(":License"), license);
639
            dbConnection.query(sqlQuery);
640
        }
641
    } catch (SQLiteException& e) {
Lukas Appelhans's avatar
Lukas Appelhans committed
642
        akabeiDebug() << "Error while inserting package in database failed for " << p->name() << ": " << e.what();
Lukas Appelhans's avatar
Lukas Appelhans committed
643
        return SQLITE_ABORT;
644 645
    }

646
    // Done!
647
    return SQLITE_OK;
648 649
}

650 651
int QueryPerformer::insertGroup(Database *db, Group* g)
{
Lukas Appelhans's avatar
Lukas Appelhans committed
652 653
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return insertGroup(d->dbConnection, g);
654 655
}

656
int QueryPerformer::insertGroup(AbstractSQLiteConnection &dbConnection, Group* g)
657
{
658
    // Query first.
659
    QString sqlQuery =
Lukas Appelhans's avatar
Lukas Appelhans committed
660 661 662
        "INSERT INTO groups ('name', 'description', 'icon') "
        "VALUES ("
        ":Name, :Description, :Iconname)";
663

664
    try {
665 666 667
        dbConnection.bind(QStringLiteral(":Name"), g->name());
        dbConnection.bind(QStringLiteral(":Description"), g->description());
        dbConnection.bind(QStringLiteral(":Iconname"), g->iconName());
Lukas Appelhans's avatar
Lukas Appelhans committed
668

Lukas Appelhans's avatar
Lukas Appelhans committed
669
        dbConnection.query(sqlQuery);
670
    } catch (SQLiteException& e) {
671
        akabeiDebug() << "Error inserting group: " << e.what();
Lukas Appelhans's avatar
Lukas Appelhans committed
672
        return SQLITE_ABORT;
673 674
    }

675
    // Done!
676 677 678
    return SQLITE_OK;
}

679 680 681 682 683 684
int QueryPerformer::removeGroup(Database* db, Group* g)
{
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return removeGroup(d->dbConnection, g);
}

685
int QueryPerformer::removeGroup(AbstractSQLiteConnection& dbConnection, Group* g)
686 687
{
    try {
688 689
        dbConnection.bind(QStringLiteral(":Name"), g->name());
        dbConnection.query(QStringLiteral("DELETE FROM groups WHERE name=:Name"));
690

691 692
        dbConnection.bind(QStringLiteral(":Name"), g->name());
        dbConnection.query(QStringLiteral("DELETE FROM belongsgroup WHERE groupname=:Name"));
693 694 695 696 697 698 699 700

    } catch (SQLiteException& ex) {
        return SQLITE_ABORT;
    }

    return SQLITE_OK;
}

701
int QueryPerformer::removePackage(Database *db, Package* p)
702
{
Lukas Appelhans's avatar
Lukas Appelhans committed
703 704
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return removePackage(d->dbConnection, p);
705 706
}

707
int QueryPerformer::removePackage(AbstractSQLiteConnection &dbConnection, Package* p)
708
{
709
    QString sqlQuery = QStringLiteral("DELETE FROM packages WHERE ID = :id");
710 711

    try {
712
        dbConnection.bind(QStringLiteral(":id"), p->databaseId());
Lukas Appelhans's avatar
Lukas Appelhans committed
713 714
        dbConnection.query(sqlQuery);

Lukas Appelhans's avatar
Lukas Appelhans committed
715 716
        dbConnection.query("DELETE FROM belongsgroup WHERE package=" + QString::number(p->databaseId()));
        dbConnection.query("DELETE FROM depends WHERE package=" + QString::number(p->databaseId()));
717
        dbConnection.query("DELETE FROM makedepends WHERE package=" + QString::number(p->databaseId()));
Lukas Appelhans's avatar
Lukas Appelhans committed
718 719 720 721 722
        dbConnection.query("DELETE FROM optional WHERE package=" + QString::number(p->databaseId()));
        dbConnection.query("DELETE FROM replaces WHERE package=" + QString::number(p->databaseId()));
        dbConnection.query("DELETE FROM provides WHERE package=" + QString::number(p->databaseId()));
        dbConnection.query("DELETE FROM conflicts WHERE package=" + QString::number(p->databaseId()));
        dbConnection.query("DELETE FROM providesmimetype WHERE package=" + QString::number(p->databaseId()));
723
        dbConnection.query("DELETE FROM licensed WHERE package=" + QString::number(p->databaseId()));
Lukas Appelhans's avatar
Lukas Appelhans committed
724 725

    } catch (SQLiteException& e) {
726
        akabeiDebug() << "Error removing package: " << e.what();
Lukas Appelhans's avatar
Lukas Appelhans committed
727
        return SQLITE_ABORT;
728 729
    }

730
    // Done!
731
    return SQLITE_OK;
732 733
}

734
int QueryPerformer::updatePackage(Database *db, Package* from, Package *to)
735
{
Lukas Appelhans's avatar
Lukas Appelhans committed
736 737 738
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    d->packageCache.remove(from->databaseId());
    return updatePackage(d->dbConnection, from, to);
739
}
740

741 742 743 744 745 746
int QueryPerformer::updatePackage(AbstractSQLiteConnection& dbConnection, Package* from, Package* to)
{
    return updatePackage(dbConnection, from->databaseId(), to);
}

int QueryPerformer::updatePackage(AbstractSQLiteConnection &dbConnection, int from, Package* p)
747
{
748
    QString null = QStringLiteral("NULL");
749

Dario Freddi's avatar
Dario Freddi committed
750
    QString sqlQuery =
Lukas Appelhans's avatar
Lukas Appelhans committed
751
        "UPDATE packages SET "
752
        "Name=:Name, Version=:Version, Epoch=:Epoch, Filename=:Filename, Description=:Description, Size=:Size, "
Lukas Appelhans's avatar
Lukas Appelhans committed
753
        "InstalledSize=:InstalledSize, MD5SUM=:MD5SUM, URL=:URL, BuildDate=:BuildDate, Arch=:Arch, Packager=:Packager, "
754
        "Flags=:Flags, ScreenShotURL=:ScreenShotURL, "
755
        "InstallReason=:InstallReason, InstallDate=:InstallDate, GitRepository=:GitRepo, GitBranch=:GitBranch, GitFolder=:GitFolder "
Lukas Appelhans's avatar
Lukas Appelhans committed
756
        "WHERE ID=:PreviousPackageId";
Dario Freddi's avatar
Dario Freddi committed
757

758
    try {
759 760 761 762 763 764 765 766
        dbConnection.bind(QStringLiteral(":Name"), p->name());
        dbConnection.bind(QStringLiteral(":Version"), p->version().toByteArray());
        dbConnection.bind(QStringLiteral(":Epoch"), p->version().epoch());
        dbConnection.bind(QStringLiteral(":Filename"), p->filename());
        dbConnection.bind(QStringLiteral(":Description"), p->description());
        dbConnection.bind(QStringLiteral(":Size"), p->size());
        dbConnection.bind(QStringLiteral(":InstalledSize"), p->installedSize());
        dbConnection.bind(QStringLiteral(":MD5SUM"), p->md5sum());
Lukas Appelhans's avatar
Lukas Appelhans committed
767 768

        if (p->url().isValid()) {
769
            dbConnection.bind(QStringLiteral(":URL"), p->url().toString());
Lukas Appelhans's avatar
Lukas Appelhans committed
770
        } else {
771
            dbConnection.bind(QStringLiteral(":URL"), null);
Lukas Appelhans's avatar
Lukas Appelhans committed
772 773
        }

774 775 776 777
        dbConnection.bind(QStringLiteral(":BuildDate"), p->buildDate().toTime_t());
        dbConnection.bind(QStringLiteral(":Arch"), p->arch());
        dbConnection.bind(QStringLiteral(":Packager"), p->packager());
        dbConnection.bind(QStringLiteral(":Flags"), null); // TODO I still need to decide how to handle flags, bind to null
Lukas Appelhans's avatar
Lukas Appelhans committed
778

Lukas Appelhans's avatar
Lukas Appelhans committed
779
        if (p->screenshot().isValid()) {
780
            dbConnection.bind(QStringLiteral(":ScreenShotURL"), p->screenshot().toString());
Lukas Appelhans's avatar
Lukas Appelhans committed
781
        } else {
782
            dbConnection.bind(QStringLiteral(":ScreenShotURL"), null);
Lukas Appelhans's avatar
Lukas Appelhans committed
783 784
        }

785 786 787 788 789 790
        dbConnection.bind(QStringLiteral(":InstallReason"), (int)p->installReason());
        dbConnection.bind(QStringLiteral(":InstallDate"), p->installDate().toTime_t());
        dbConnection.bind(QStringLiteral(":GitRepo"), p->gitRepo());
        dbConnection.bind(QStringLiteral(":GitBranch"), p->gitBranch());
        dbConnection.bind(QStringLiteral(":GitFolder"), p->gitFolder());
        dbConnection.bind(QStringLiteral(":PreviousPackageId"), from);
Lukas Appelhans's avatar
Lukas Appelhans committed
791 792 793

        dbConnection.query(sqlQuery);

794
        QueryHelper::packagePrivateFromPackage(p)->setDatabaseId(from);
795

Lukas Appelhans's avatar
Lukas Appelhans committed
796 797 798
        /*
         * Delete every relation
         */
799 800 801 802 803 804 805 806
        dbConnection.query("DELETE FROM belongsgroup WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM depends WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM optional WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM replaces WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM provides WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM conflicts WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM providesmimetype WHERE package=" + QString::number(from));
        dbConnection.query("DELETE FROM licensed WHERE package=" + QString::number(from));
807
        dbConnection.query("DELETE FROM makedepends WHERE package=" + QString::number(from));
Lukas Appelhans's avatar
Lukas Appelhans committed
808

809
        foreach (Akabei::Group *group, p->groups()) {
810 811
            // Can I assume the group already exists?
            //dbConnection.query("INSERT INTO groups ('name') VALUES ('" + group->name() + "');");
812 813 814
            sqlQuery = QStringLiteral("INSERT INTO belongsgroup ('package', 'groupname') VALUES (:Package, :Group);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":Group"), group->name());
815
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
816 817
        }

818
        foreach (const QString& dependency, p->dependencies()) {
819 820 821
            sqlQuery = QStringLiteral("INSERT INTO depends ('package', 'dependency') VALUES (:Package, :Depend);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":Depend"), dependency);
822
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
823 824
        }

825
        foreach (const QString& makedep, p->makeDependencies()) {
826 827 828
            sqlQuery = QStringLiteral("INSERT INTO makedepends ('package', 'dependency') VALUES (:Package, :MakeDepend);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":MakeDepend"), makedep);
829 830 831
            dbConnection.query(sqlQuery);
        }

832
        foreach (const QString& opt, p->optionalDependencies()) {
833 834 835
            sqlQuery = QStringLiteral("INSERT INTO optional ('package', 'dependency') VALUES (:Package, :OptDepend);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":OptDepend"), opt);
836
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
837 838
        }

839
        foreach (const QString& replace, p->replaces()) {
840 841 842
            sqlQuery = QStringLiteral("INSERT INTO replaces ('package', 'replaces') VALUES (:Package, :Replace);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":Replace"), replace);
843
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
844 845
        }

846
        foreach (const QString& provides, p->provides()) {
847 848 849
            sqlQuery = QStringLiteral("INSERT INTO provides ('package', 'provides') VALUES (:Package, :Provides);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":Provides"), provides);
850
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
851 852
        }

853
        foreach (const QString& conflict, p->conflictsWith()) {
854 855 856
            sqlQuery = QStringLiteral("INSERT INTO conflicts ('package', 'conflict') VALUES (:Package, :Conflict);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":Conflict"), conflict);
857
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
858
        }
859

Lukas Appelhans's avatar
Lukas Appelhans committed
860
        foreach (const QString &mimetype, p->mimetypes()) {
861 862 863
            sqlQuery = QStringLiteral("INSERT INTO providesmimetype ('package', 'mimetype') VALUES (:Package, :MimeType);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":MimeType"), mimetype);
864
            dbConnection.query(sqlQuery);
865
        }
866

867
        foreach (const QString& license, p->licenses()) {
868 869 870
            sqlQuery = QStringLiteral("INSERT INTO licensed ('package', 'license') VALUES (:Package, :License);");
            dbConnection.bind(QStringLiteral(":Package"), from);
            dbConnection.bind(QStringLiteral(":License"), license);
871
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
872
        }
873

874
    } catch (SQLiteException& e) {
875
        akabeiDebug() << "Error updating package: " << e.what();
Lukas Appelhans's avatar
Lukas Appelhans committed
876
        return SQLITE_ABORT;
Dario Freddi's avatar
Dario Freddi committed
877 878
    }

879
    // Done!
Dario Freddi's avatar
Dario Freddi committed
880
    return SQLITE_OK;
881 882
}

883 884 885 886 887 888
int QueryPerformer::insertDelta(Akabei::Database* db, Akabei::Delta const* delta, int packageId)
{
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return insertDelta(d->dbConnection, delta, packageId);
}

889
int QueryPerformer::insertDelta(AbstractSQLiteConnection& dbConnection, Akabei::Delta const* delta, int packageId)
890 891
{
    try {
892 893 894 895 896
        dbConnection.bind(QStringLiteral(":Package"), packageId);
        dbConnection.bind(QStringLiteral(":Filename"), delta->filename());
        dbConnection.bind(QStringLiteral(":VersionFrom"), QString::fromLatin1(delta->versionFrom()));
        dbConnection.bind(QStringLiteral(":VersionTo"), QString::fromLatin1(delta->versionTo()));
        dbConnection.bind(QStringLiteral(":MD5SUM"), QString::fromLatin1(delta->md5sum()));
Lukas Appelhans's avatar
Lukas Appelhans committed
897
        dbConnection.query(QString("INSERT INTO deltas (`package`, `filename`, `versionfrom`, `versionto`, `md5sum`) "
Lukas Appelhans's avatar
Lukas Appelhans committed
898
                                   "VALUES (:Package, :Filename, :VersionFrom, :VersionTo, :MD5SUM);"));
899
    } catch (SQLiteException& ex) {
900
        akabeiDebug() << "Inserting delta failed with error:" << ex.what();
901 902 903 904 905 906 907 908 909 910 911 912
        return SQLITE_ABORT;
    }

    return SQLITE_OK;
}

int QueryPerformer::removeDeltas(Akabei::Database *db, Package const* p)
{
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return removeDeltas(d->dbConnection, p);
}

913
int QueryPerformer::removeDeltas(AbstractSQLiteConnection &dbConnection, Package const* p)
914 915
{
    try {
916 917
        dbConnection.bind(QStringLiteral(":Package"), p->databaseId());
        dbConnection.query(QStringLiteral("DELETE FROM deltas WHERE package = :Package"));
918 919 920 921 922 923 924
    } catch (SQLiteException& ex) {
        return SQLITE_ABORT;
    }

    return SQLITE_OK;
}

925 926 927 928 929 930
int QueryPerformer::removeDelta(Database* db, const Delta* delta)
{
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
    return removeDelta(d->dbConnection, delta);
}

931
int QueryPerformer::removeDelta(AbstractSQLiteConnection& dbConnection, const Delta* delta)
932 933
{
    try {
934 935
        dbConnection.bind(QStringLiteral(":Delta"), delta->filename());
        dbConnection.query(QStringLiteral("DELETE FROM deltas WHERE filename = :Delta"));
936 937 938 939 940 941 942
    } catch (SQLiteException& ex) {
        return SQLITE_ABORT;
    }

    return SQLITE_OK;
}

Lukas Appelhans's avatar
Lukas Appelhans committed
943
int QueryPerformer::insertFiles(Database *db, Package* p)
944
{
Lukas Appelhans's avatar
Lukas Appelhans committed
945
    DatabasePrivate *d = QueryHelper::databasePrivateFromDatabase(db);
Lukas Appelhans's avatar
Lukas Appelhans committed
946
    return insertFiles(d->dbConnection, p);
947 948
}

949
int QueryPerformer::insertFiles(AbstractSQLiteConnection &dbConnection, Package* p)
950
{
951
    QString sqlQuery = QStringLiteral("INSERT INTO files ('package', 'file', 'backup', 'ultimateowner') VALUES (:Package, :File, :Backup, :UltimateOwner);");
952 953

    try {
954 955 956 957
        QStringList dirs;
        QStringList sortedFiles = p->retrieveFiles(Package::FilepathNoPrefix);
        sortedFiles.sort();
        foreach (const QString &file, sortedFiles) {
958
            if (file == QLatin1String(".PKGINFO") || file == QLatin1String(".INSTALL") || file == QLatin1String(".MTREE") || file == QLatin1String(".Changelog"))
959
                continue;
960
            if (file.endsWith(QLatin1String("/"))) {
961 962 963 964 965 966 967 968
                dirs << file;
                continue;
            }
            foreach (const QString &dir, dirs) {
                if (file.startsWith(dir)) {
                    dirs.removeAll(dir);
                }
            }
969 970
            dbConnection.bind(QStringLiteral(":Package"), p->databaseId());
            dbConnection.bind(QStringLiteral(":File"), file);
971
            if (p->backupFiles().contains(file)) {
972
                dbConnection.bind(QStringLiteral(":Backup"), p->backupFiles().value(file));
973
            } else {
974
                dbConnection.bind(QStringLiteral(":Backup"), QString());
975
            }
976
            if (p->ultimatelyOwnedFiles().contains(file)) {
977
                dbConnection.bind(QStringLiteral(":UltimateOwner"), 1);
978
            } else {
979
                dbConnection.bind(QStringLiteral(":UltimateOwner"), 0);
980
            }
981
            dbConnection.query(sqlQuery);
Lukas Appelhans's avatar
Lukas Appelhans committed
982
        }
983
        foreach (const QString &dir, dirs) {
984 985
            dbConnection.bind(QStringLiteral(":Package"), p->databaseId());
            dbConnection.bind(QStringLiteral(":File"), dir);
986
            if (p->backupFiles().contains(dir)) {
987
                dbConnection.bind(QStringLiteral(":Backup"), p->backupFiles().value(dir));
Lukas Appelhans's avatar