mainwindow.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #include "mainwindow.h"
  2. #include "aboutwindow.h"
  3. #include "networkwidget.h"
  4. #include "ui_mainwindow.h"
  5. #include "installdialog.h"
  6. #include <string>
  7. #include <map>
  8. #include <set>
  9. #include <vector>
  10. #include <stdexcept>
  11. #include <utility>
  12. #include <QClipboard>
  13. #include <QMutex>
  14. #include <QCoreApplication>
  15. #include <QDir>
  16. #include <QFile>
  17. #include <QMessageBox>
  18. #include <QDebug>
  19. #include <QProcess>
  20. #include <QStringList>
  21. #include <QVBoxLayout>
  22. #include <QScrollBar>
  23. #include <QEventLoop>
  24. QNetworkAccessManager *nam;
  25. // Globally visible
  26. ZeroTier::Node::LocalClient *zeroTierClient = (ZeroTier::Node::LocalClient *)0;
  27. // Main window instance for app
  28. static MainWindow *mainWindow = (MainWindow *)0;
  29. static void handleZTMessage(void *arg,unsigned long id,const char *line)
  30. {
  31. static std::map< unsigned long,std::vector<std::string> > ztReplies;
  32. static QMutex ztReplies_m;
  33. ztReplies_m.lock();
  34. if (*line) {
  35. ztReplies[id].push_back(std::string(line));
  36. ztReplies_m.unlock();
  37. } else { // empty lines conclude transmissions
  38. std::map< unsigned long,std::vector<std::string> >::iterator r(ztReplies.find(id));
  39. if (r != ztReplies.end()) {
  40. MainWindow::ZTMessageEvent *event = new MainWindow::ZTMessageEvent(r->second);
  41. ztReplies.erase(r);
  42. ztReplies_m.unlock();
  43. QCoreApplication::postEvent(mainWindow,event); // must post since this may be another thread
  44. } else ztReplies_m.unlock();
  45. }
  46. }
  47. MainWindow::MainWindow(QWidget *parent) :
  48. QMainWindow(parent),
  49. ui(new Ui::MainWindow),
  50. pollServiceTimerId(0)
  51. {
  52. ui->setupUi(this);
  53. #ifdef __APPLE__
  54. if (!QFile::exists("/Library/Application Support/ZeroTier/One/zerotier-one")) {
  55. // If the service is not installed, download the installer and run it
  56. // for the first time.
  57. this->setEnabled(false);
  58. InstallDialog *id = new InstallDialog(this);
  59. id->setModal(true);
  60. id->show();
  61. this->setHidden(true);
  62. return;
  63. }
  64. #endif
  65. this->pollServiceTimerId = this->startTimer(1000);
  66. this->setEnabled(false); // gets enabled when updates are received
  67. mainWindow = this;
  68. this->cyclesSinceResponseFromService = 0;
  69. if (ui->networkListWidget->verticalScrollBar())
  70. ui->networkListWidget->verticalScrollBar()->setSingleStep(8);
  71. QWidgetList widgets = this->findChildren<QWidget*>();
  72. foreach(QWidget* widget, widgets)
  73. widget->setAttribute(Qt::WA_MacShowFocusRect,false);
  74. }
  75. MainWindow::~MainWindow()
  76. {
  77. delete ui;
  78. delete zeroTierClient;
  79. zeroTierClient = (ZeroTier::Node::LocalClient *)0;
  80. mainWindow = (MainWindow *)0;
  81. }
  82. void MainWindow::timerEvent(QTimerEvent *event)
  83. {
  84. event->accept();
  85. if (this->isHidden())
  86. return;
  87. if (!zeroTierClient) {
  88. std::string authToken;
  89. if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) {
  90. #ifdef __APPLE__
  91. if (QFile::exists("/Library/Application Support/ZeroTier/One/zerotier-one")) {
  92. // Run the little AppleScript hack that asks for admin credentials and
  93. // then installs the auth token file in the current user's home.
  94. QMessageBox::information(this,"Authorization Required","You must authenticate to authorize this user to\nadministrate ZeroTier One on this computer.\n\n(This only needs to be done once.)",QMessageBox::Ok,QMessageBox::NoButton);
  95. QString authHelperPath(QCoreApplication::applicationDirPath() + "/../Resources/helpers/mac/ZeroTier One (Authenticate).app/Contents/MacOS/applet");
  96. if (!QFile::exists(authHelperPath)) {
  97. QMessageBox::critical(this,"Unable to Locate Helper","Unable to locate authorization helper, cannot obtain authentication token.",QMessageBox::Ok,QMessageBox::NoButton);
  98. QApplication::exit(1);
  99. return;
  100. }
  101. QProcess::execute(authHelperPath,QStringList());
  102. } else {
  103. // If the service is not installed, download the installer and run it
  104. // for the first time.
  105. this->setEnabled(false);
  106. InstallDialog *id = new InstallDialog(this);
  107. id->setModal(true);
  108. id->show();
  109. this->setHidden(true);
  110. return;
  111. }
  112. #endif
  113. if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) {
  114. QMessageBox::critical(this,"Cannot Authorize","Unable to authorize this user to administrate ZeroTier One.\n\nTo do so manually, copy 'authtoken.secret' from the ZeroTier One home directory to '.zeroTierOneAuthToken' in your home directory and set file modes on this file to only be readable by you (e.g. 0600 on Mac or Linux systems).",QMessageBox::Ok,QMessageBox::NoButton);
  115. QApplication::exit(1);
  116. return;
  117. }
  118. }
  119. zeroTierClient = new ZeroTier::Node::LocalClient(authToken.c_str(),0,&handleZTMessage,this);
  120. }
  121. // TODO: do something more user-friendly here... or maybe try to restart
  122. // the service?
  123. if (++this->cyclesSinceResponseFromService == 4)
  124. QMessageBox::critical(this,"No Response from Service","The ZeroTier One service does not appear to be running.",QMessageBox::Ok,QMessageBox::NoButton);
  125. zeroTierClient->send("info");
  126. zeroTierClient->send("listnetworks");
  127. zeroTierClient->send("listpeers");
  128. }
  129. void MainWindow::customEvent(QEvent *event)
  130. {
  131. ZTMessageEvent *m = (ZTMessageEvent *)event; // only one custom event type so far
  132. if (m->ztMessage.size() == 0)
  133. return;
  134. std::vector<std::string> hdr(ZeroTier::Node::LocalClient::splitLine(m->ztMessage[0]));
  135. if (hdr.size() < 2)
  136. return;
  137. if (hdr[0] != "200")
  138. return;
  139. this->cyclesSinceResponseFromService = 0;
  140. if (hdr[1] == "info") {
  141. if (hdr.size() >= 3)
  142. this->myAddress = hdr[2].c_str();
  143. if (hdr.size() >= 4)
  144. this->myStatus = hdr[3].c_str();
  145. if (hdr.size() >= 5)
  146. this->myVersion = hdr[4].c_str();
  147. } else if (hdr[1] == "listnetworks") {
  148. std::map< std::string,std::vector<std::string> > newNetworks;
  149. for(unsigned long i=1;i<m->ztMessage.size();++i) {
  150. std::vector<std::string> l(ZeroTier::Node::LocalClient::splitLine(m->ztMessage[i]));
  151. // 200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>
  152. if ((l.size() == 9)&&(l[2].length() == 16))
  153. newNetworks[l[2]] = l;
  154. }
  155. if (newNetworks != networks) {
  156. networks = newNetworks;
  157. for (bool removed=true;removed;) {
  158. removed = false;
  159. for(int r=0;r<ui->networkListWidget->count();++r) {
  160. NetworkWidget *nw = (NetworkWidget *)ui->networkListWidget->itemWidget(ui->networkListWidget->item(r));
  161. if (!networks.count(nw->networkId())) {
  162. ui->networkListWidget->setVisible(false); // HACK to prevent an occasional crash here, discovered through hours of shotgun debugging... :P
  163. delete ui->networkListWidget->takeItem(r);
  164. removed = true;
  165. break;
  166. }
  167. }
  168. }
  169. ui->networkListWidget->setVisible(true);
  170. std::set<std::string> alreadyDisplayed;
  171. for(int r=0;r<ui->networkListWidget->count();++r) {
  172. NetworkWidget *nw = (NetworkWidget *)ui->networkListWidget->itemWidget(ui->networkListWidget->item(r));
  173. if (networks.count(nw->networkId()) > 0) {
  174. alreadyDisplayed.insert(nw->networkId());
  175. std::vector<std::string> &l = networks[nw->networkId()];
  176. nw->setNetworkName(l[3]);
  177. nw->setStatus(l[4],l[5]);
  178. nw->setNetworkType(l[6]);
  179. nw->setNetworkDeviceName(l[7]);
  180. nw->setIps(l[8]);
  181. }
  182. }
  183. for(std::map< std::string,std::vector<std::string> >::iterator nwdata(networks.begin());nwdata!=networks.end();++nwdata) {
  184. if (alreadyDisplayed.count(nwdata->first) == 0) {
  185. std::vector<std::string> &l = nwdata->second;
  186. NetworkWidget *nw = new NetworkWidget((QWidget *)0,nwdata->first);
  187. nw->setNetworkName(l[3]);
  188. nw->setStatus(l[4],l[5]);
  189. nw->setNetworkType(l[6]);
  190. nw->setNetworkDeviceName(l[7]);
  191. nw->setIps(l[8]);
  192. QListWidgetItem *item = new QListWidgetItem();
  193. item->setSizeHint(nw->sizeHint());
  194. ui->networkListWidget->addItem(item);
  195. ui->networkListWidget->setItemWidget(item,nw);
  196. }
  197. }
  198. }
  199. } else if (hdr[1] == "listpeers") {
  200. this->numPeers = 0;
  201. for(unsigned long i=1;i<m->ztMessage.size();++i) {
  202. std::vector<std::string> l(ZeroTier::Node::LocalClient::splitLine(m->ztMessage[i]));
  203. if ((l.size() >= 5)&&((l[3] != "-")||(l[4] != "-")))
  204. ++this->numPeers; // number of direct peers online -- check for active IPv4 and/or IPv6 address
  205. }
  206. }
  207. if (this->myAddress.size())
  208. ui->addressButton->setText(this->myAddress);
  209. else ui->addressButton->setText(" ");
  210. QString st(this->myStatus);
  211. st += ", v";
  212. st += this->myVersion;
  213. st += ", ";
  214. st += QString::number(this->numPeers);
  215. st += " direct links to peers";
  216. ui->statusLabel->setText(st);
  217. if (this->myStatus == "ONLINE") {
  218. if (!this->isEnabled())
  219. this->setEnabled(true);
  220. } else {
  221. if (this->isEnabled())
  222. this->setEnabled(false);
  223. }
  224. }
  225. void MainWindow::on_joinNetworkButton_clicked()
  226. {
  227. QString toJoin(ui->networkIdLineEdit->text());
  228. ui->networkIdLineEdit->setText(QString());
  229. if (!zeroTierClient) // sanity check
  230. return;
  231. if (toJoin.size() != 16) {
  232. QMessageBox::information(this,"Invalid Network ID","The network ID you entered was not valid. Enter a 16-digit hexadecimal network ID, like '8056c2e21c000001'.",QMessageBox::Ok,QMessageBox::NoButton);
  233. return;
  234. }
  235. zeroTierClient->send((QString("join ") + toJoin).toStdString());
  236. }
  237. void MainWindow::on_actionAbout_triggered()
  238. {
  239. AboutWindow *about = new AboutWindow(this);
  240. about->show();
  241. }
  242. void MainWindow::on_networkIdLineEdit_textChanged(const QString &text)
  243. {
  244. QString newText;
  245. for(QString::const_iterator i(text.begin());i!=text.end();++i) {
  246. switch(i->toLatin1()) {
  247. case '0': newText.append('0'); break;
  248. case '1': newText.append('1'); break;
  249. case '2': newText.append('2'); break;
  250. case '3': newText.append('3'); break;
  251. case '4': newText.append('4'); break;
  252. case '5': newText.append('5'); break;
  253. case '6': newText.append('6'); break;
  254. case '7': newText.append('7'); break;
  255. case '8': newText.append('8'); break;
  256. case '9': newText.append('9'); break;
  257. case 'a': newText.append('a'); break;
  258. case 'b': newText.append('b'); break;
  259. case 'c': newText.append('c'); break;
  260. case 'd': newText.append('d'); break;
  261. case 'e': newText.append('e'); break;
  262. case 'f': newText.append('f'); break;
  263. case 'A': newText.append('a'); break;
  264. case 'B': newText.append('b'); break;
  265. case 'C': newText.append('c'); break;
  266. case 'D': newText.append('d'); break;
  267. case 'E': newText.append('e'); break;
  268. case 'F': newText.append('f'); break;
  269. default: break;
  270. }
  271. }
  272. if (newText.size() > 16)
  273. newText.truncate(16);
  274. ui->networkIdLineEdit->setText(newText);
  275. }
  276. void MainWindow::on_addressButton_clicked()
  277. {
  278. QApplication::clipboard()->setText(this->myAddress);
  279. }