diff --git a/ZeroTierUI/ZeroTierUI.pro b/ZeroTierUI/ZeroTierUI.pro index 1b7355537..6473fdb66 100644 --- a/ZeroTierUI/ZeroTierUI.pro +++ b/ZeroTierUI/ZeroTierUI.pro @@ -39,7 +39,8 @@ SOURCES += main.cpp\ ../node/Utils.cpp \ ../ext/lz4/lz4.c \ ../ext/lz4/lz4hc.c \ - networkwidget.cpp + networkwidget.cpp \ + installdialog.cpp HEADERS += mainwindow.h \ aboutwindow.h \ @@ -88,11 +89,13 @@ HEADERS += mainwindow.h \ ../node/UdpSocket.hpp \ ../ext/lz4/lz4.h \ ../ext/lz4/lz4hc.h \ - networkwidget.h + networkwidget.h \ + installdialog.h FORMS += mainwindow.ui \ aboutwindow.ui \ - networkwidget.ui + networkwidget.ui \ + installdialog.ui RESOURCES += \ resources.qrc diff --git a/ZeroTierUI/installdialog.cpp b/ZeroTierUI/installdialog.cpp new file mode 100644 index 000000000..ad5757703 --- /dev/null +++ b/ZeroTierUI/installdialog.cpp @@ -0,0 +1,83 @@ +#include "installdialog.h" +#include "mainwindow.h" +#include "ui_installdialog.h" + +#include "../node/Defaults.hpp" + +#include +#include +#include +#include + +InstallDialog::InstallDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::InstallDialog), + nam(new QNetworkAccessManager(this)) +{ + ui->setupUi(this); + QObject::connect(nam,SIGNAL(finished(QNetworkReply*)),this,SLOT(on_networkReply(QNetworkReply*))); + + const char *nfoUrl = ZeroTier::ZT_DEFAULTS.updateLatestNfoURL.c_str(); + if (!*nfoUrl) { + QMessageBox::critical(this,"Download Failed","Download failed: internal error: no update URL configured in build!",QMessageBox::Ok,QMessageBox::NoButton); + QApplication::exit(1); + return; + } + + QNetworkReply *reply = nam->get(QNetworkRequest(QUrl(nfoUrl))); + QObject::connect(reply,SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(on_downloadProgress(qint64,qint64))); +} + +InstallDialog::~InstallDialog() +{ + delete ui; +} + +void InstallDialog::on_networkReply(QNetworkReply *reply) +{ + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) { + QMessageBox::critical(this,"Download Failed",QString("Download failed: ") + reply->errorString(),QMessageBox::Ok,QMessageBox::NoButton); + QApplication::exit(1); + return; + } else { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { + QByteArray installerData(reply->readAll()); + installerData.append((char)0); + printf("%s\n",installerData.data()); + } else { + QMessageBox::critical(this,"Download Failed",QString("Download failed: HTTP status code ") + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString(),QMessageBox::Ok,QMessageBox::NoButton); + QApplication::exit(1); + return; + } + } +} + +void InstallDialog::on_InstallDialog_rejected() +{ + QApplication::exit(); +} + +//((QMainWindow *)this->parent())->setHidden(false); + +void InstallDialog::on_cancelButton_clicked() +{ + QApplication::exit(); +} + +void InstallDialog::on_downloadProgress(qint64 bytesReceived,qint64 bytesTotal) +{ + if (bytesTotal <= 0) { + ui->progressBar->setValue(0); + ui->progressBar->setMinimum(0); + ui->progressBar->setMaximum(0); + } else { + double pct = ((double)bytesReceived / (double)bytesTotal) * 100.0; + if (pct > 100.0) + pct = 100.0; + ui->progressBar->setMinimum(0); + ui->progressBar->setMaximum(100); + ui->progressBar->setValue((int)pct); + } +} diff --git a/ZeroTierUI/installdialog.h b/ZeroTierUI/installdialog.h new file mode 100644 index 000000000..19c7a89a6 --- /dev/null +++ b/ZeroTierUI/installdialog.h @@ -0,0 +1,33 @@ +#ifndef INSTALLDIALOG_H +#define INSTALLDIALOG_H + +#include +#include +#include +#include +#include + +namespace Ui { +class InstallDialog; +} + +class InstallDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InstallDialog(QWidget *parent = 0); + ~InstallDialog(); + +private slots: + void on_networkReply(QNetworkReply *reply); + void on_InstallDialog_rejected(); + void on_cancelButton_clicked(); + void on_downloadProgress(qint64 bytesReceived,qint64 bytesTotal); + +private: + Ui::InstallDialog *ui; + QNetworkAccessManager *nam; +}; + +#endif // INSTALLDIALOG_H diff --git a/ZeroTierUI/installdialog.ui b/ZeroTierUI/installdialog.ui new file mode 100644 index 000000000..15a080276 --- /dev/null +++ b/ZeroTierUI/installdialog.ui @@ -0,0 +1,131 @@ + + + InstallDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 547 + 231 + + + + Install ZeroTier One Service + + + + :/img/zt1icon.png:/img/zt1icon.png + + + true + + + true + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + 14 + + + + Since this is your first time running ZeroTier One on this computer, the virtual Ethernet service must be downloaded and installed. + +Please wait while the service downloads, then you will be prompted to enter an administrator password to install it. + + + Qt::PlainText + + + Qt::AlignCenter + + + true + + + 10 + + + Qt::NoTextInteraction + + + + + + + 0 + + + 0 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel and Exit + + + + + + + + + + + + + diff --git a/ZeroTierUI/mainwindow.cpp b/ZeroTierUI/mainwindow.cpp index b4afcbbb3..1c62d0153 100644 --- a/ZeroTierUI/mainwindow.cpp +++ b/ZeroTierUI/mainwindow.cpp @@ -2,6 +2,7 @@ #include "aboutwindow.h" #include "networkwidget.h" #include "ui_mainwindow.h" +#include "installdialog.h" #include #include @@ -54,16 +55,14 @@ static void handleZTMessage(void *arg,unsigned long id,const char *line) MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), - nam(new QNetworkAccessManager(this)) + pollServiceTimerId(0) { ui->setupUi(this); - this->startTimer(1000); // poll service every second + this->pollServiceTimerId = this->startTimer(1000); this->setEnabled(false); // gets enabled when updates are received mainWindow = this; this->cyclesSinceResponseFromService = 0; - QObject::connect(nam,SIGNAL(finished(QNetworkReply*)),this,SLOT(on_networkReply(QNetworkReply*))); - if (ui->networkListWidget->verticalScrollBar()) ui->networkListWidget->verticalScrollBar()->setSingleStep(8); @@ -84,11 +83,15 @@ void MainWindow::timerEvent(QTimerEvent *event) { event->accept(); + if (this->isHidden()) + return; + if (!zeroTierClient) { std::string authToken; if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) { #ifdef __APPLE__ - if (QFile::exists("/Library/Application Support/ZeroTier/One/zerotier-one")) { + //if (QFile::exists("/Library/Application Support/ZeroTier/One/zerotier-one")) { + if (false) { // Run the little AppleScript hack that asks for admin credentials and // then installs the auth token file in the current user's home. QString authHelperPath(QCoreApplication::applicationDirPath() + "/../Resources/helpers/mac/ZeroTier One (Authenticate).app/Contents/MacOS/applet"); @@ -105,11 +108,18 @@ void MainWindow::timerEvent(QTimerEvent *event) } QProcess::execute(authHelperPath,QStringList()); } else { - // Download the latest version and install it + // If the service is not installed, download the installer and run it + // for the first time. this->setEnabled(false); + InstallDialog *id = new InstallDialog(this); + id->setModal(true); + id->show(); + this->setHidden(true); + return; // Run the little AppleScript hack that asks for admin credentials and // then installs the auth token file in the current user's home. + /* QString installHelperPath(QCoreApplication::applicationDirPath() + "/../Resources/helpers/mac/ZeroTier One (Install).app/Contents/MacOS/applet"); if (!QFile::exists(installHelperPath)) { QMessageBox::critical(this,"Unable to Locate Helper","Unable to locate install helper, cannot install service.",QMessageBox::Ok,QMessageBox::NoButton); @@ -117,6 +127,7 @@ void MainWindow::timerEvent(QTimerEvent *event) return; } QProcess::execute(installHelperPath,QStringList()); + */ } #endif @@ -306,7 +317,3 @@ void MainWindow::on_addressButton_clicked() { QApplication::clipboard()->setText(this->myAddress); } - -void MainWindow::on_networkReply(QNetworkReply *reply) -{ -} diff --git a/ZeroTierUI/mainwindow.h b/ZeroTierUI/mainwindow.h index d3ec15d89..b34f4dcee 100644 --- a/ZeroTierUI/mainwindow.h +++ b/ZeroTierUI/mainwindow.h @@ -4,10 +4,6 @@ #include #include #include -#include -#include -#include -#include #include #include @@ -55,15 +51,14 @@ private slots: void on_actionAbout_triggered(); void on_networkIdLineEdit_textChanged(const QString &text); void on_addressButton_clicked(); - void on_networkReply(QNetworkReply *reply); private: Ui::MainWindow *ui; - QNetworkAccessManager *nam; QString myAddress; QString myStatus; QString myVersion; + int pollServiceTimerId; unsigned int numPeers; unsigned int cyclesSinceResponseFromService; std::map< std::string,std::vector > networks; diff --git a/ext/installfiles/linux/install.tmpl.sh b/ext/installfiles/linux/install.tmpl.sh new file mode 100644 index 000000000..87addba5f --- /dev/null +++ b/ext/installfiles/linux/install.tmpl.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +export PATH=/bin:/usr/bin:/sbin:/usr/sbin +shopt -s expand_aliases + +dryRun=0 + +echo "*** ZeroTier One install/update ***" + +if [ "$UID" -ne 0 ]; then + echo "Not running as root so doing dry run (no modifications to system)..." + dryRun=1 +fi + +if [ $dryRun -gt 0 ]; then + alias ln="echo '>> dry run: ln'" + alias rm="echo '>> dry run: rm'" + alias mv="echo '>> dry run: mv'" + alias chown="echo '>> dry run: chown'" + alias chgrp="echo '>> dry run: chgrp'" + alias launchctl="echo '>> dry run: launchctl'" + alias zerotier-cli="echo '>> dry run: zerotier-cli'" +fi + +zthome="/Library/Application Support/ZeroTier/One" +ztapp=`mdfind kMDItemCFBundleIdentifier == 'com.zerotier.ZeroTierOne'` +if [ ! -d "$ztapp" ]; then + ztapp="/Applications/ZeroTier One.app" +fi + +scriptPath="`dirname "$0"`/`basename "$0"`" +if [ ! -r "$scriptPath" ]; then + scriptPath="$0" + if [ ! -r "$scriptPath" ]; then + echo "Installer cannot determine its own path; $scriptPath is not readable." + exit 2 + fi +fi + +endMarkerIndex=`grep -a -b -E '^################' "$scriptPath" | head -c 16 | cut -d : -f 1` +if [ "$endMarkerIndex" -le 100 ]; then + echo 'Internal error: unable to find end of script / start of binary data marker.' + exit 2 +fi +blobStart=`expr $endMarkerIndex + 17` +if [ "$blobStart" -le "$endMarkerIndex" ]; then + echo 'Internal error: unable to find end of script / start of binary data marker.' + exit 2 +fi + +echo 'Extracting files...' +if [ $dryRun -gt 0 ]; then + echo ">> dry run: tail -c +$blobStart \"$scriptPath\" | bunzip2 -c | tar -xvop -C / -f -" +else + tail -c +$blobStart "$scriptPath" | bunzip2 -c | tar -xvop -C / -f - +fi + +if [ $dryRun -eq 0 -a ! -d "/Applications/ZeroTier One_app.LATEST" ]; then + echo 'Archive extraction failed, cannot find zerotier-one binary.' + exit 2 +fi + +echo 'Installing/updating ZeroTier One.app...' + +if [ -d "$ztapp" ]; then + # Preserve ownership of existing .app and install new version in the + # same location. + currentAppOwner=`stat -f '%u' "$ztapp"` + currentAppGroup=`stat -f '%g' "$ztapp"` + if [ ! -z "$currentAppOwner" -a ! -z "$currentAppGroup" ]; then + rm -rf "$ztapp" + mv -f "/Application/ZeroTier One_app.LATEST" "$ztapp" + chown -R $currentAppOwner "$ztapp" + chgrp -R $currentAppGroup "$ztapp" + else + rm -rf "$ztapp" + mv -f "/Application/ZeroTier One_app.LATEST" "$ztapp" + fi +else + # If there is no existing app, just drop the shipped one into place + mv -f "/Applications/ZeroTier One_app.LATEST" "/Applications/ZeroTier One.app" +fi + +echo 'Installing zerotier-cli command line utility...' + +ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" /usr/bin/zerotier-cli + +if [ ! -f '/Library/Application Support/ZeroTier/One/authtoken.secret' ]; then + echo 'Pre-creating authtoken.secret for ZeroTier service...' + if [ $dryRun -eq 0 ]; then + rm -f '/Library/Application Support/ZeroTier/One/authtoken.secret' + head -c 1024 /dev/urandom | md5 | head -c 24 >'/Library/Application Support/ZeroTier/One/authtoken.secret' + chmod 0600 '/Library/Application Support/ZeroTier/One/authtoken.secret' + fi +fi + +echo 'Installing and (re-)starting zerotier-one service via launchctl...' + +if [ ! -z "`launchctl list | grep -F com.zerotier.one`" ]; then + launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist +fi +launchctl load /Library/LaunchDaemons/com.zerotier.one.plist + +sleep 1 +zerotier-cli info + +exit 0 + +# Do not remove the last line or add a carriage return to it! The installer +# looks for an unterminated line beginning with 16 #'s in itself to find +# the binary blob data, which is appended after it. + +################ \ No newline at end of file