830 lines
24 KiB
C++
830 lines
24 KiB
C++
/* Berryboot -- add distribution dialog
|
|
*
|
|
* Copyright (c) 2012, Floris Bos
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "adddialog.h"
|
|
#include "ui_adddialog.h"
|
|
#include "installer.h"
|
|
#include "downloaddialog.h"
|
|
#include "downloadthread.h"
|
|
#include "networksettingsdialog.h"
|
|
#include "twoiconsdelegate.h"
|
|
#include <QProgressDialog>
|
|
#include <QMessageBox>
|
|
#include <QPushButton>
|
|
#include <QSettings>
|
|
#include <QFile>
|
|
#include <QDir>
|
|
#include <QListWidgetItem>
|
|
#include <QPushButton>
|
|
#include <QCryptographicHash>
|
|
#include <QDebug>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/xattr.h>
|
|
#include <QScreen>
|
|
#include <QSettings>
|
|
|
|
#include <openssl/pkcs7.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/pem.h>
|
|
|
|
|
|
/* Keep downloaded data global. */
|
|
QByteArray AddDialog::_data;
|
|
bool AddDialog::_openSSLinitialized = false;
|
|
|
|
#define DEFAULT_REPO_SERVER "http://dl.berryboot.com/distro.zsmime"
|
|
|
|
|
|
AddDialog::AddDialog(Installer *i, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::AddDialog),
|
|
_qpd(NULL), _i(i), _cachedir("/mnt/tmp/cache"), _ini(NULL), _reposerver(DEFAULT_REPO_SERVER), _download(NULL)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
#ifdef Q_WS_QWS
|
|
/* Make dialog smaller if using a small screen (e.g. SDTV) */
|
|
QScreen *scr = QScreen::instance();
|
|
if (scr->height() < 600)
|
|
resize(width(), 400);
|
|
#endif
|
|
|
|
/* Check if we have any special proxy server or repo settings */
|
|
if (_i->hasSettings())
|
|
{
|
|
setProxy();
|
|
}
|
|
|
|
/* Detect if we are running on a rPi or some ARMv7 device
|
|
Information is used to decide which operating systems to show */
|
|
QFile f("/proc/cpuinfo");
|
|
f.open(f.ReadOnly);
|
|
QByteArray cpuinfo = f.readAll();
|
|
f.close();
|
|
|
|
if (cpuinfo.contains("ARMv7"))
|
|
_device = "armv7";
|
|
else if (cpuinfo.contains("BCM2708"))
|
|
_device = "rpi";
|
|
else
|
|
_device = "other";
|
|
|
|
f.setFileName("/proc/version");
|
|
/* 'Linux version 3.6.2' -> 36 */
|
|
f.open(f.ReadOnly);
|
|
QByteArray versioninfo = f.readAll();
|
|
f.close();
|
|
_kernelversion = versioninfo.mid(14,4).replace(".", "");
|
|
|
|
/* Disable OK button until an image is selected */
|
|
ui->buttonBox->button(ui->buttonBox->Ok)->setEnabled(false);
|
|
|
|
if (!QFile::exists("/boot/iscsi.sh"))
|
|
{
|
|
QPushButton *button = new QPushButton(QIcon(":/icons/server.png"), tr("Network settings"), this);
|
|
connect(button, SIGNAL(clicked()), this, SLOT(onProxySettings()));
|
|
ui->buttonBox->addButton(button, QDialogButtonBox::ActionRole);
|
|
}
|
|
|
|
if (!_i->networkReady())
|
|
{
|
|
_qpd = new QProgressDialog(tr("Enabling network interface"), QString(), 0,0, this);
|
|
_qpd->show();
|
|
connect(_i, SIGNAL(networkInterfaceUp()), this, SLOT(networkUp()));
|
|
_i->startNetworking();
|
|
}
|
|
else
|
|
{
|
|
if (_data.isEmpty())
|
|
downloadList();
|
|
else
|
|
processIni();
|
|
}
|
|
}
|
|
|
|
AddDialog::~AddDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void AddDialog::networkUp()
|
|
{
|
|
if (_qpd)
|
|
_qpd->hide();
|
|
|
|
/* if (!_i->networkReady())
|
|
{
|
|
QMessageBox::critical(this, tr("No network"), tr("No network connection available (or no DHCP server)"), QMessageBox::Close);
|
|
return;
|
|
} */
|
|
|
|
if (_data.isEmpty())
|
|
downloadList();
|
|
}
|
|
|
|
void AddDialog::downloadList()
|
|
{
|
|
_data.clear();
|
|
|
|
if (_qpd)
|
|
_qpd->deleteLater();
|
|
_qpd = new QProgressDialog(tr("Downloading list of available distributions"), tr("Cancel"), 0,0, this);
|
|
_qpd->show();
|
|
|
|
if (_reposerver.startsWith("cifs:") || _reposerver.startsWith("nfs:"))
|
|
{
|
|
generateListFromShare(_reposerver, _repouser, _repopass);
|
|
}
|
|
else
|
|
{
|
|
_downloadCancelled = false;
|
|
_download = new DownloadThread(_reposerver);
|
|
_download->setCacheDirectory(_cachedir);
|
|
connect(_download, SIGNAL(finished()), this, SLOT(downloadComplete()));
|
|
connect(_qpd, SIGNAL(canceled()), this, SLOT(cancelDownload()));
|
|
|
|
if (!_reposerver2.isEmpty())
|
|
{
|
|
_download2 = new DownloadThread(_reposerver2);
|
|
_download2->setCacheDirectory(_cachedir);
|
|
connect(_download2, SIGNAL(finished()), this, SLOT(download2Complete()));
|
|
connect(_download, SIGNAL(finished()), _download2, SLOT(start()));
|
|
}
|
|
|
|
if (QFile::exists("/mnt/preloaded"))
|
|
{
|
|
connect(_download, SIGNAL(finished()), this, SLOT(generatePreloadedTab()));
|
|
}
|
|
|
|
_download->start();
|
|
}
|
|
}
|
|
|
|
void AddDialog::downloadComplete()
|
|
{
|
|
if (!_download || _downloadCancelled)
|
|
return; /* Cancelled */
|
|
|
|
if (_qpd)
|
|
{
|
|
_qpd->hide();
|
|
_qpd->deleteLater();
|
|
_qpd = NULL;
|
|
}
|
|
|
|
if (!_download->successfull())
|
|
{
|
|
QMessageBox::critical(this, tr("Download error"), tr("Error downloading distribution list from Internet"), QMessageBox::Ok);
|
|
}
|
|
else
|
|
{
|
|
_data = _download->data();
|
|
|
|
time_t localTime = time(NULL);
|
|
time_t serverTime = _download->serverTime();
|
|
qDebug() << "Date from server: " << serverTime << "local time:" << localTime;
|
|
|
|
if (serverTime > localTime)
|
|
{
|
|
qDebug() << "Setting time to server time";
|
|
struct timeval tv;
|
|
tv.tv_sec = serverTime;
|
|
tv.tv_usec = 0;
|
|
settimeofday(&tv, NULL);
|
|
}
|
|
|
|
processData();
|
|
}
|
|
|
|
_download->deleteLater();
|
|
_download = NULL;
|
|
}
|
|
|
|
void AddDialog::download2Complete()
|
|
{
|
|
if (!_download2 || _downloadCancelled)
|
|
return; /* Cancelled */
|
|
|
|
if (_download2->successfull())
|
|
{
|
|
QFile f("/tmp/distro.ini");
|
|
f.open(f.Append);
|
|
f.write(_download2->data());
|
|
f.close();
|
|
processIni();
|
|
}
|
|
else
|
|
{
|
|
/* Do not treat it as fatal if secondary repository is unreachable */
|
|
}
|
|
|
|
_download2->deleteLater();
|
|
_download2 = NULL;
|
|
}
|
|
|
|
void AddDialog::cancelDownload()
|
|
{
|
|
_downloadCancelled = true;
|
|
if (_qpd)
|
|
{
|
|
_qpd->deleteLater();
|
|
_qpd = NULL;
|
|
}
|
|
if (_download)
|
|
{
|
|
_download->cancelDownload();
|
|
_download->deleteLater();
|
|
_download = NULL;
|
|
}
|
|
}
|
|
|
|
bool AddDialog::verifyData()
|
|
{
|
|
bool verified = false;
|
|
|
|
if (!_openSSLinitialized)
|
|
{
|
|
OpenSSL_add_all_algorithms();
|
|
// _openSSLinitialized = true;
|
|
}
|
|
QByteArray data_uncompressed;
|
|
if (_reposerver.endsWith(".smime"))
|
|
data_uncompressed = _data;
|
|
else
|
|
data_uncompressed = qUncompress(_data);
|
|
QString certfilename;
|
|
|
|
if (QFile::exists("/boot/berryboot.crt"))
|
|
certfilename = "/boot/berryboot.crt";
|
|
else
|
|
certfilename = ":/berryboot.crt";
|
|
|
|
QFile f(certfilename);
|
|
f.open(f.ReadOnly);
|
|
QByteArray cert = f.readAll();
|
|
f.close();
|
|
X509 *scert = NULL;
|
|
STACK_OF(X509) *scerts = sk_X509_new_null();
|
|
PKCS7 *p7 = NULL;
|
|
BIO *certbio = BIO_new_mem_buf(cert.data(), cert.size());
|
|
BIO *inbio = BIO_new_mem_buf(data_uncompressed.data(), data_uncompressed.size());
|
|
BIO *cont = NULL, *outbio = NULL;
|
|
X509_STORE *st = X509_STORE_new();
|
|
int flags = PKCS7_NOINTERN | PKCS7_NOVERIFY;
|
|
scert = PEM_read_bio_X509(certbio, NULL, 0, NULL);
|
|
|
|
sk_X509_push(scerts, scert);
|
|
p7 = SMIME_read_PKCS7(inbio, &cont);
|
|
|
|
if (p7)
|
|
{
|
|
outbio = BIO_new_file("/tmp/distro.ini", "w");
|
|
|
|
if (PKCS7_verify(p7, scerts, st, cont, outbio, flags))
|
|
verified = true;
|
|
}
|
|
|
|
if (p7)
|
|
PKCS7_free(p7);
|
|
if (scerts)
|
|
sk_X509_pop_free(scerts, X509_free);
|
|
if (inbio)
|
|
BIO_free(inbio);
|
|
if (outbio)
|
|
BIO_free(outbio);
|
|
if (cont)
|
|
BIO_free(cont);
|
|
if (st)
|
|
X509_STORE_free(st);
|
|
|
|
return verified;
|
|
}
|
|
|
|
void AddDialog::processData()
|
|
{
|
|
if (!verifyData())
|
|
{
|
|
if (!_cachedir.isEmpty())
|
|
QProcess::execute("rm -rf "+_cachedir);
|
|
|
|
_data.clear();
|
|
QFile::remove("/tmp/distro.ini");
|
|
QMessageBox::critical(this, tr("Data corrupt"), tr("Downloaded data corrupt. Signature does not match"), QMessageBox::Close);
|
|
return;
|
|
}
|
|
|
|
processIni();
|
|
}
|
|
|
|
void AddDialog::processIni()
|
|
{
|
|
_ini = new QSettings("/tmp/distro.ini", QSettings::IniFormat, this);
|
|
QStringList sections = _ini->childGroups();
|
|
QIcon installedIcon(":/icons/hdd.png");
|
|
ui->groupTabs->clear();
|
|
ui->buttonBox->button(ui->buttonBox->Ok)->setEnabled(false);
|
|
|
|
foreach (QString section, sections)
|
|
{
|
|
if (section == "berryboot")
|
|
continue;
|
|
|
|
_ini->beginGroup(section);
|
|
if (_ini->contains("device") && _ini->value("device").toString() != _device && _ini->value("device").toString() != _device+_kernelversion)
|
|
{
|
|
_ini->endGroup();
|
|
continue;
|
|
}
|
|
|
|
QString group = _ini->value("group", "Others").toString();
|
|
int groupOrder = _ini->value("grouporder", -1).toInt();
|
|
QString name = _ini->value("name").toString();
|
|
QString description = _ini->value("description").toString();
|
|
QString sizeinmb = QString::number(_ini->value("size", 0).toLongLong()/1024/1024);
|
|
QString localfilename = "/mnt/images/"+name+".img"+_ini->value("memsplit", "").toString();
|
|
localfilename.replace(" ", "_");
|
|
|
|
QIcon icon;
|
|
if (_ini->contains("icon_b64"))
|
|
{
|
|
_ini->setValue("icon", QByteArray::fromBase64(_ini->value("icon_b64").toByteArray() ));
|
|
}
|
|
if (_ini->contains("icon"))
|
|
{
|
|
QPixmap pix;
|
|
pix.loadFromData(_ini->value("icon").toByteArray());
|
|
icon = pix;
|
|
}
|
|
_ini->endGroup();
|
|
|
|
/* Search tab corresponding to group */
|
|
QListWidget *osList = NULL;
|
|
|
|
for (int i = 0; i < ui->groupTabs->count(); i++)
|
|
{
|
|
if (ui->groupTabs->tabText(i) == group)
|
|
{
|
|
osList = qobject_cast<QListWidget *>(ui->groupTabs->widget(i));
|
|
break;
|
|
}
|
|
}
|
|
if (!osList)
|
|
{
|
|
/* No tab for group yet, create one */
|
|
osList = new QListWidget();
|
|
osList->setIconSize(QSize(128,128));
|
|
osList->setSpacing(2);
|
|
QFont f = osList->font();
|
|
f.setPointSize(16);
|
|
osList->setFont(f);
|
|
osList->setItemDelegate(new TwoIconsDelegate(this));
|
|
connect(osList, SIGNAL(itemSelectionChanged()), this, SLOT(onSelectionChanged()));
|
|
|
|
if (groupOrder != -1)
|
|
{
|
|
ui->groupTabs->insertTab(groupOrder, osList, group);
|
|
if (groupOrder == 0)
|
|
{
|
|
ui->groupTabs->setCurrentIndex(0);
|
|
}
|
|
}
|
|
else
|
|
ui->groupTabs->addTab(osList, group);
|
|
}
|
|
|
|
QListWidgetItem *item = new QListWidgetItem(icon, name+" ("+sizeinmb+" MB)\n"+description, osList);
|
|
item->setData(Qt::UserRole, section);
|
|
|
|
if (QFile::exists(localfilename))
|
|
{
|
|
item->setData(SecondIconRole, installedIcon);
|
|
item->setData(Qt::BackgroundColorRole, QColor(0xef,0xff,0xef));
|
|
}
|
|
}
|
|
|
|
if (sections.contains("berryboot"))
|
|
{
|
|
_ini->beginGroup("berryboot");;
|
|
QString newest_version = _ini->value("version").toString();
|
|
if (newest_version > QString(BERRYBOOT_VERSION))
|
|
{
|
|
QString changelog = _ini->value("description").toString();
|
|
|
|
if (QMessageBox::question(this, tr("New BerryBoot version"), changelog+"\n\n"+tr("Would you like to upgrade?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
|
{
|
|
if (_i->availableDiskSpace() < 50000000)
|
|
QMessageBox::critical(this, tr("Low disk space"), tr("Less than 50 MB available. Refusing to update"), QMessageBox::Close);
|
|
else
|
|
selfUpdate(_ini->value("url").toString(), _ini->value("sha1").toString() );
|
|
}
|
|
}
|
|
_ini->endGroup();
|
|
}
|
|
}
|
|
|
|
QByteArray AddDialog::sha1file(const QString &filename)
|
|
{
|
|
QFile f(filename);
|
|
QByteArray buf;
|
|
QCryptographicHash h(QCryptographicHash::Sha1);
|
|
|
|
f.open(f.ReadOnly);
|
|
while ( f.bytesAvailable() )
|
|
{
|
|
buf = f.read(4096);
|
|
h.addData(buf);
|
|
}
|
|
|
|
f.close();
|
|
return h.result().toHex();
|
|
}
|
|
|
|
void AddDialog::selfUpdate(const QString &updateurl, const QString &sha1)
|
|
{
|
|
setEnabled(false);
|
|
QString localfile = "/mnt/tmp/berryupdate.tgz";
|
|
|
|
DownloadDialog dd(updateurl, "", "berryupdate.tgz", DownloadDialog::Update, sha1, this);
|
|
|
|
if ( dd.exec() == dd.Accepted)
|
|
{
|
|
QProgressDialog qpd(tr("Recalculating SHA1"), QString(),0,0, this);
|
|
qpd.show();
|
|
|
|
/* Recalculate SHA1 one more time to rule out SD card corruption */
|
|
if (sha1file(localfile) != sha1)
|
|
{
|
|
QMessageBox::critical(this, tr("Error"), tr("Data on SD card corrupt"), QMessageBox::Close);
|
|
QFile::remove(localfile);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
qpd.setLabelText(tr("Mounting system partition"));
|
|
QApplication::processEvents();
|
|
|
|
_i->mountSystemPartition();
|
|
QString sha1shared = sha1file("/boot/shared.tgz");
|
|
|
|
qpd.setLabelText(tr("Extracting update to boot partition"));
|
|
QApplication::processEvents();
|
|
|
|
if (system("gzip -dc /mnt/tmp/berryupdate.tgz | tar x -C /boot") != 0)
|
|
{
|
|
QMessageBox::critical(this, tr("Error"), tr("Error extracting update"), QMessageBox::Close);
|
|
return;
|
|
}
|
|
QFile::remove(localfile);
|
|
|
|
if (sha1file("/boot/shared.tgz") != sha1shared)
|
|
{
|
|
qpd.setLabelText(tr("Extracting updated shared.tgz"));
|
|
QApplication::processEvents();
|
|
|
|
/* Normalize shared after workarounds for Arch/Fedora */
|
|
if (QFile::exists("/mnt/shared/usr/lib/modules"))
|
|
{
|
|
if (system("mv /mnt/shared/usr/lib /mnt/shared/lib") != 0 || system("mv /mnt/shared/usr/sbin /mnt/shared/sbin") != 0)
|
|
{
|
|
}
|
|
QDir dir;
|
|
dir.remove("/mnt/shared/usr");
|
|
}
|
|
|
|
/* Shared.tgz has changed. Extract it to /mnt */
|
|
if (system("/bin/gzip -dc /boot/shared.tgz | /bin/tar x -C /mnt/shared") != 0)
|
|
{
|
|
QMessageBox::critical(this, tr("Error"), tr("Error extracting updated shared.tgz"), QMessageBox::Close);
|
|
}
|
|
}
|
|
|
|
if (QFile::exists("/boot/post-update.sh"))
|
|
{
|
|
qpd.setLabelText(tr("Running post-update script"));
|
|
QProcess::execute("/bin/sh /boot/post-update.sh");
|
|
QFile::remove("/boot/post-update.sh");
|
|
}
|
|
|
|
qpd.setLabelText(tr("Unmounting boot partition"));
|
|
QApplication::processEvents();
|
|
|
|
_i->umountSystemPartition();
|
|
|
|
qpd.setLabelText(tr("Finish writing to disk (sync)"));
|
|
QApplication::processEvents();
|
|
|
|
sync();
|
|
qpd.hide();
|
|
QMessageBox::information(this, tr("Update complete"), tr("Press 'close' to reboot"), QMessageBox::Close);
|
|
_i->reboot();
|
|
}
|
|
}
|
|
|
|
setEnabled(true);
|
|
}
|
|
|
|
void AddDialog::onSelectionChanged()
|
|
{
|
|
ui->buttonBox->button(ui->buttonBox->Ok)->setEnabled(true);
|
|
}
|
|
|
|
void AddDialog::on_groupTabs_currentChanged(int)
|
|
{
|
|
QListWidget *osList = qobject_cast<QListWidget *>(ui->groupTabs->currentWidget());
|
|
bool selected = osList && !osList->selectedItems().isEmpty();
|
|
ui->buttonBox->button(ui->buttonBox->Ok)->setEnabled(selected);
|
|
}
|
|
|
|
void AddDialog::accept()
|
|
{
|
|
if (ui->groupTabs->currentIndex() == -1)
|
|
return;
|
|
QListWidget *osList = qobject_cast<QListWidget *>(ui->groupTabs->currentWidget());
|
|
if (!osList->currentItem())
|
|
return;
|
|
|
|
QString url, alternateUrl, filename;
|
|
QByteArray description, icon_b64, sha1;
|
|
double size, availablespace = _i->availableDiskSpace();
|
|
|
|
if (_ini)
|
|
{
|
|
QString imagesection = osList->currentItem()->data(Qt::UserRole).toString();
|
|
_ini->beginGroup(imagesection);
|
|
url = _ini->value("url").toString();
|
|
sha1 = _ini->value("sha1").toByteArray();
|
|
filename = _ini->value("name").toString() + ".img" + _ini->value("memsplit", "").toString();
|
|
filename.replace(" ", "_");
|
|
size = _ini->value("size").toDouble() + 10000000; /* Add 10 MB extra for overhead */
|
|
description = _ini->value("description").toByteArray();
|
|
icon_b64 = _ini->value("icon_b64").toByteArray();
|
|
|
|
/* If mirrors are available, select a random one */
|
|
QStringList mirrors;
|
|
QStringList keys = _ini->childKeys();
|
|
foreach (QString key, keys)
|
|
{
|
|
if (key.startsWith("mirror"))
|
|
{
|
|
mirrors.append(_ini->value(key).toString());
|
|
}
|
|
}
|
|
if (!mirrors.isEmpty())
|
|
{
|
|
/* Try a random mirror first, and the main site if downloading from mirror fails */
|
|
alternateUrl = url;
|
|
qsrand(QTime::currentTime().msec());
|
|
url = mirrors.at(qrand() % mirrors.count());
|
|
}
|
|
|
|
_ini->endGroup();
|
|
}
|
|
else
|
|
{
|
|
/* File on network share */
|
|
filename = osList->currentItem()->data(Qt::UserRole).toString();
|
|
QFileInfo fi(filename);
|
|
size = fi.size() + 10000000; /* Add 10 MB extra for overhead */
|
|
url = "file://"+filename;
|
|
filename = fi.fileName();
|
|
}
|
|
|
|
if (size > availablespace)
|
|
{
|
|
QMessageBox::critical(this, tr("Low disk space"), tr("Not enough disk space available to install this OS"), QMessageBox::Close);
|
|
return;
|
|
}
|
|
|
|
DownloadDialog dd(url, alternateUrl, filename, DownloadDialog::Image, sha1, this);
|
|
if (!description.isEmpty())
|
|
{
|
|
dd.setAttr("user.description", description);
|
|
dd.setAttr("user.sha1", sha1);
|
|
dd.setAttr("user.icon_b64", icon_b64);
|
|
}
|
|
hide();
|
|
dd.exec();
|
|
|
|
QDialog::accept();
|
|
}
|
|
|
|
void AddDialog::onProxySettings()
|
|
{
|
|
NetworkSettingsDialog ns(_i, this);
|
|
|
|
if (ns.exec() == ns.Accepted)
|
|
{
|
|
setProxy();
|
|
downloadList();
|
|
}
|
|
}
|
|
|
|
void AddDialog::setProxy()
|
|
{
|
|
QSettings *s = _i->settings();
|
|
s->beginGroup("proxy");
|
|
if (s->contains("type"))
|
|
{
|
|
QByteArray proxy, user, pass;
|
|
proxy = s->value("hostname").toByteArray()+":"+s->value("port").toByteArray();
|
|
user = s->value("user").toByteArray();
|
|
pass = QByteArray::fromBase64(s->value("password").toByteArray());
|
|
if (!user.isEmpty() && !pass.isEmpty())
|
|
proxy = user+":"+pass+"@"+proxy;
|
|
|
|
DownloadThread::setProxy(proxy);
|
|
}
|
|
else
|
|
{
|
|
DownloadThread::setProxy("");
|
|
}
|
|
s->endGroup();
|
|
|
|
s->beginGroup("repo");
|
|
if (s->contains("url"))
|
|
{
|
|
_reposerver = s->value("url").toByteArray();
|
|
_repouser = s->value("user").toByteArray();
|
|
_repopass = QByteArray::fromBase64(s->value("password").toByteArray());
|
|
}
|
|
else
|
|
{
|
|
_reposerver = DEFAULT_REPO_SERVER;
|
|
_repouser = _repopass = "";
|
|
}
|
|
_reposerver2 = s->value("url2").toByteArray();
|
|
s->endGroup();
|
|
}
|
|
|
|
void AddDialog::generateListFromShare(const QByteArray &url, QByteArray username, QByteArray password)
|
|
{
|
|
QApplication::processEvents();
|
|
QByteArray shareType, share;
|
|
|
|
if (url.startsWith("cifs:"))
|
|
{
|
|
shareType = "cifs";
|
|
share = url.mid(5);
|
|
if (username.isEmpty())
|
|
username = "guest";
|
|
}
|
|
else if (url.startsWith("nfs:"))
|
|
{
|
|
shareType = "nfs";
|
|
share = url.mid(4);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
_i->loadFilesystemModule(shareType);
|
|
|
|
QDir dir("/share");
|
|
if (dir.exists())
|
|
{
|
|
QProcess::execute("umount /share");
|
|
}
|
|
else
|
|
{
|
|
dir.mkdir("/share");
|
|
}
|
|
|
|
QStringList args;
|
|
args << "-t" << shareType << share << "/share";
|
|
|
|
if (!username.isEmpty())
|
|
{
|
|
args << "-o" << "username="+username;
|
|
if (!password.isEmpty())
|
|
args << "-o" << "password="+password;
|
|
}
|
|
if (QProcess::execute("mount", args) != 0)
|
|
{
|
|
dir.rmdir("/share");
|
|
QMessageBox::critical(this, tr("Mount error"), tr("Error mounting network share %1").arg(QString(share)), QMessageBox::Ok);
|
|
}
|
|
else
|
|
{
|
|
_ini = NULL;
|
|
ui->groupTabs->clear();
|
|
|
|
/* Create tab */
|
|
QListWidget *osList = new QListWidget();
|
|
osList->setIconSize(QSize(128,128));
|
|
osList->setSpacing(2);
|
|
QFont f = osList->font();
|
|
f.setPointSize(16);
|
|
osList->setFont(f);
|
|
connect(osList, SIGNAL(itemSelectionChanged()), this, SLOT(onSelectionChanged()));
|
|
ui->groupTabs->addTab(osList, tr("Network share"));
|
|
|
|
QStringList namefilters;
|
|
namefilters << "*.img*";
|
|
|
|
QFileInfoList list = dir.entryInfoList(namefilters, QDir::Files, QDir::Name);
|
|
foreach (QFileInfo fi, list)
|
|
{
|
|
QString name = fi.fileName().replace('_',' ');
|
|
QString sizeinmb = QString::number(fi.size()/1024/1024);
|
|
QListWidgetItem *item = new QListWidgetItem(name+" ("+sizeinmb+" MB)", osList);
|
|
item->setData(Qt::UserRole, fi.absoluteFilePath() );
|
|
}
|
|
}
|
|
|
|
if (_qpd)
|
|
{
|
|
_qpd->hide();
|
|
_qpd->deleteLater();
|
|
_qpd = NULL;
|
|
}
|
|
}
|
|
|
|
QByteArray AddDialog::getXattr(const QByteArray &filename, const QByteArray &key)
|
|
{
|
|
char buf[32*1024];
|
|
int len;
|
|
QByteArray result;
|
|
|
|
len = ::getxattr(filename.constData(), key.constData(), buf, sizeof(buf));
|
|
if (len > 0)
|
|
result = QByteArray(buf, len);
|
|
|
|
return result;
|
|
}
|
|
|
|
void AddDialog::generatePreloadedTab()
|
|
{
|
|
QByteArray ini;
|
|
int nr = 1;
|
|
|
|
QDir dir("/mnt/preloaded");
|
|
QStringList namefilters;
|
|
namefilters << "*.img*";
|
|
|
|
QFileInfoList list = dir.entryInfoList(namefilters, QDir::Files, QDir::Name);
|
|
foreach (QFileInfo fi, list)
|
|
{
|
|
QByteArray name = fi.fileName().replace('_',' ').toLatin1();
|
|
QByteArray memsplit;
|
|
QByteArray absfilename = fi.absoluteFilePath().toLatin1();
|
|
QByteArray description = getXattr(absfilename, "user.description");
|
|
QByteArray icon_b64 = getXattr(absfilename, "user.icon_b64");
|
|
QByteArray sha1 = getXattr(absfilename, "user.sha1");
|
|
|
|
int imgpos = name.lastIndexOf(".img");
|
|
if (imgpos != -1 && imgpos+5 < name.size())
|
|
{
|
|
memsplit = name.mid(imgpos+4);
|
|
}
|
|
name = name.left(imgpos);
|
|
|
|
ini += "[preloaded"+QByteArray::number(nr++)+"]\n";
|
|
ini += "name="+name+"\n";
|
|
if (!memsplit.isEmpty())
|
|
ini += "memsplit="+memsplit+"\n";
|
|
ini += "description="+description.replace("\n", "\\n")+"\n";
|
|
ini += "group=Preloaded\n";
|
|
ini += "size="+QByteArray::number(fi.size())+"\n";
|
|
if (!sha1.isEmpty())
|
|
ini += "sha1="+sha1+"\n";
|
|
ini += "url=file://"+absfilename+"\n";
|
|
if (!icon_b64.isEmpty())
|
|
ini += "icon_b64="+icon_b64.replace("\n", "\\n")+"\n";
|
|
ini += "\n";
|
|
}
|
|
qDebug() << ini;
|
|
|
|
QFile f("/tmp/distro.ini");
|
|
f.open(f.Append);
|
|
f.write(ini);
|
|
f.close();
|
|
processIni();
|
|
}
|