ba596e2f2fd0de7c4f208f6dbb0e00fcf3423c84
[woller] / woller.cpp
1 // Copyright 2010 Ilkka Tengvall
2 //
3 // This file is part of Woller.
4 //
5 // Woller is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Woller is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Woller.  If not, see <http://www.gnu.org/licenses/>.
17
18
19 #ifdef Q_WS_MAEMO_5
20   #include <QtMaemo5>
21 #endif
22 #include "woller.h"
23
24 MainWindow::MainWindow()
25 {
26 #ifdef Q_WS_MAEMO_5
27     this->setAttribute(Qt::WA_Maemo5StackedWindow);
28 #endif
29     woller = new Woller(this);
30     this->setCentralWidget(woller);
31 }
32
33 MainWindow::~MainWindow()
34 {
35     delete woller;
36 }
37
38
39 Woller::Woller(QWidget *parent)
40         : QWidget(parent)
41 {
42     QList<QString> targets;
43     status_orig = new QString(
44             tr("Choose the target or configure and fire it up!"));
45     status_timer = new QTimer(this);
46     hosts_list = new QList<host_s>;
47     settings = new QSettings("woller", "woller");
48     // build the main widget
49     this->setWindowIcon(QIcon::fromTheme("woller", QIcon(":/woller.png")));
50     status_lbl = new QLabel(*status_orig , this);
51     fire_btn = new QPushButton(tr("Wake UP"), this);
52     fire_btn->setToolTip(
53             tr("Press the button to wake up selected target (or config)"));
54     fire_btn->setIcon(QIcon::fromTheme("woller", QIcon(":/woller.png")));
55     targets_cb = new QComboBox(this);
56     targets_cb->setToolTip(
57             tr("Choose host to wake up, or configure hosts option"));
58
59     vlayout = new QVBoxLayout();
60     hlayout = new QHBoxLayout();
61     hlayout->addWidget(targets_cb);
62     hlayout->addWidget(fire_btn);
63     vlayout->addWidget(status_lbl);
64     vlayout->addLayout(hlayout);
65     this->setLayout(vlayout);
66     this->setWindowTitle(tr("Woller-program"));
67 #ifdef Q_WS_MAEMO_5
68     this->setAttribute(Qt::WA_Maemo5StackedWindow);
69 #else
70     this->setWindowModality(Qt::ApplicationModal);
71 #endif
72
73     //populate CB
74     load_config();
75
76     target = new wol_target;
77
78     connect(fire_btn, SIGNAL(clicked()), this, SLOT(send_pkt()));
79     connect(targets_cb, SIGNAL(activated(int)), this, SLOT(targets_act(int)));
80 }
81
82 Woller::~Woller()
83 {
84     int i = targets_cb->currentIndex();
85     qDebug() << "~Woller" << endl;
86     qDebug() << "~Woller last" << i << endl;
87     settings->setValue("last selected", i);
88     settings->setValue("position", pos());
89     delete settings;
90     delete hosts_list;
91     delete status_lbl;
92     delete fire_btn;
93     delete targets_cb;
94     //delete vlayout;
95     //delete hlayout;
96     delete target;
97     delete status_orig;
98     delete status_timer;
99 }
100
101 void Woller::send_pkt()
102 {
103     int t = targets_cb->currentIndex();
104
105     if (t == 0)
106     {
107         targets_act(0);
108     }
109     else
110     {
111         if (!hosts_list->isEmpty() && t-1 <= hosts_list->count())
112         {
113             QString status = "Sent wakeup packet to ";
114             status.append(hosts_list->at(t-1).hostname);
115             target->set_mac(hosts_list->at(t-1).mac);
116             target->set_ip(hosts_list->at(t-1).ip);
117             target->wake_me();
118             status_lbl->setText(status);
119             status_timer->singleShot(5000, this, SLOT(reset_status()));
120         }
121     }
122 }
123
124 void Woller::reset_status()
125 {
126     status_lbl->setText(*status_orig);
127 }
128
129 void Woller::targets_act(int i)
130 {
131     if (i == 0)
132     {
133         // build the config widget
134         qDebug() << "building the config" << endl;
135         config_win = new ConfigWidget(this, hosts_list);
136         connect(config_win, SIGNAL(hosts_changed()), this, SLOT(hosts_changed()));
137         config_win->show();
138     }
139     else
140     {
141         qDebug() << "targets: " << targets_cb->currentText() << endl;
142     }
143 }
144
145 void Woller::add_new(host_s *host)
146 {
147     qDebug() << "add_new:" << host->hostname << host->ip << host->mac << endl;
148 }
149
150 void Woller::do_list()
151 {
152     qDebug() << "do_list" << endl;
153     //populate CB
154     targets_cb->clear();
155     targets_cb->addItem(tr("Configure Targets"));
156     for (int i = 0; i < hosts_list->size(); ++i) {
157         targets_cb->addItem(hosts_list->at(i).hostname);
158      }
159 }
160
161 void Woller::hosts_changed()
162 {
163     qDebug() << "hosts_changed" << endl;
164     do_list();
165     save_config();
166 }
167
168 void Woller::save_config()
169 {
170     qDebug() << "save config" << endl;
171     settings->beginWriteArray("targets");
172     for (int i = 0; i < hosts_list->count(); ++i) {
173         settings->setArrayIndex(i);
174         settings->setValue("hostname", hosts_list->at(i).hostname);
175         settings->setValue("ip", hosts_list->at(i).ip.toString().toAscii());
176         settings->setValue("mac", hosts_list->at(i).mac.toAscii());
177      }
178     settings->endArray();
179 }
180
181 void Woller::load_config()
182 {
183     host_s host;
184     qDebug() << "loading config" << endl;
185     int size = settings->beginReadArray("targets");
186     for (int i = 0; i < size; ++i) {
187         settings->setArrayIndex(i);
188         host.hostname = settings->value("hostname").toString();
189         host.ip.setAddress(settings->value("ip").toString());
190         host.mac = settings->value("mac").toString();
191         hosts_list->append(host);
192     }
193     settings->endArray();
194
195     move(settings->value("position", QPoint(200, 200)).toPoint());
196
197     do_list();
198
199     targets_cb->setCurrentIndex(settings->value("last selected", 0).toInt());
200 }
201
202
203 ConfigWidget::ConfigWidget(QWidget *parent, QList<host_s> *hosts)
204         : QWidget(parent)
205 {
206     qDebug() << "ConfigWidget" << endl;
207     hosts_list = hosts;
208
209     //add buttons and the list
210     this->setWindowTitle(tr("Woller Config"));
211 #ifdef Q_WS_MAEMO_5
212     this->setAttribute(Qt::WA_Maemo5StackedWindow);
213 #else
214     this->setWindowModality(Qt::ApplicationModal);
215 #endif
216     this->setWindowFlags(this->windowFlags() | Qt::Window);
217     add_host = new QPushButton(tr("&Add Host"), this);
218     edit_host = new QPushButton(tr("&Edit Host"), this);
219     del_host = new QPushButton(tr("&Delete Host"), this);
220     save_btn = new QPushButton(tr("&Save"), this);
221     close_btn = new QPushButton(tr("&Close"), this);
222     list = new QListWidget(this);
223     button_layout = new QVBoxLayout();
224     layout = new QHBoxLayout();
225     button_layout->addWidget(add_host);
226     button_layout->addWidget(edit_host);
227     button_layout->addWidget(del_host);
228     button_layout->addWidget(save_btn);
229     button_layout->addWidget(close_btn);
230     layout->addWidget(list);
231     layout->addLayout(button_layout);
232     this->setLayout(layout);
233     close_btn->setDefault(true);
234     edit_host->setDisabled(true);
235     del_host->setDisabled(true);
236     save_btn->setDisabled(true);
237
238     connect(add_host, SIGNAL(clicked()), this, SLOT(add_sig()));
239     connect(edit_host, SIGNAL(clicked()), this, SLOT(edit_sig()));
240     connect(del_host, SIGNAL(clicked()), this, SLOT(del_sig()));
241     connect(save_btn, SIGNAL(clicked()), this, SLOT(save_sig()));
242     connect(close_btn, SIGNAL(clicked()), this, SLOT(close_sig()));
243     connect(list, SIGNAL(itemClicked(QListWidgetItem *)), this,
244             SLOT(select_sig()));
245     connect(list, SIGNAL(itemActivated(QListWidgetItem *)), this,
246             SLOT(edit_sig()));
247
248     for (int i = 0; i < hosts_list->size(); ++i) {
249         list->addItem(hosts_list->at(i).hostname);
250      }
251 }
252
253 ConfigWidget::~ConfigWidget()
254 {
255     delete add_host;
256     delete edit_host;
257     delete del_host;
258     delete save_btn;
259     delete close_btn;
260     delete list;
261     delete button_layout;
262     delete layout;
263 }
264
265
266 void ConfigWidget::add_sig()
267 {
268     qDebug() << "conf add" << endl;
269     host_tmp.hostname.clear();
270     host_tmp.mac.clear();
271     host_tmp.ip.clear();
272     host_widget = new HostWidget(this, &host_tmp);
273
274     connect(host_widget, SIGNAL(change_host()), this, SLOT(host_added()));
275     host_widget->show();
276     qDebug() << "conf add - exit" << endl;
277
278 }
279
280 void ConfigWidget::edit_sig()
281 {
282     qDebug() << "conf edit" << endl;
283     host_tmp.hostname = hosts_list->at(list->currentIndex().row()).hostname;
284     host_tmp.mac = hosts_list->at(list->currentIndex().row()).mac;
285     host_tmp.ip = hosts_list->at(list->currentIndex().row()).ip;
286     host_widget = new HostWidget(this, &host_tmp);
287     connect(host_widget, SIGNAL(change_host()), this, SLOT(host_edited()));
288
289     //hostWidget->hostname->setPlaceholderText("my_host_name");
290     //hostWidget->hostname->setText("edit1");
291     //hostWidget->mac->setPlaceholderText("001133aabbcc");
292     //hostWidget->mac->setText("001133aabbcc");
293     //hostWidget->ip->setPlaceholderText("10.10.11.255");
294     host_widget->show();
295 }
296
297 void ConfigWidget::del_sig()
298 {
299     QMessageBox msg_box;
300     int ret, i = list->currentIndex().row();
301     qDebug() << "conf del: " << i << endl;
302
303     msg_box.setText("Host is to be deleted.");
304     msg_box.setInformativeText("Do you want to save your changes?");
305     msg_box.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
306     msg_box.setDefaultButton(QMessageBox::Save);
307     ret = msg_box.exec();
308
309     if ( ret ==  QMessageBox::Save ) {
310         hosts_list->removeAt(i);
311         list->takeItem(i);
312         emit hosts_changed();
313     }
314 }
315
316 void ConfigWidget::save_sig()
317 {
318     qDebug() << "conf save" << endl;
319     emit hosts_changed();
320     save_btn->setDisabled(true);
321 }
322
323 void ConfigWidget::close_sig()
324 {
325     qDebug() << "conf close" << endl;
326     this->close();
327 }
328
329 void ConfigWidget::select_sig()
330 {
331     qDebug() << "conf sel: " << endl;
332     edit_host->setDisabled(false);
333     del_host->setDisabled(false);
334 }
335
336 void ConfigWidget::host_added()
337 {
338     qDebug() << "host changed" << endl;
339     list->addItem(host_tmp.hostname);
340     qDebug() << "hostsList size:" << hosts_list->size()
341         << "count:" << hosts_list->size() << endl;
342     hosts_list->append(host_tmp);
343     qDebug() << "hostsList size:" << hosts_list->size()
344         << "count:" << hosts_list->size() << endl;
345     save_btn->setDisabled(false);
346     save_btn->setDefault(true);
347 }
348
349 void ConfigWidget::host_edited()
350 {
351     int i = list->currentIndex().row();
352     qDebug() << "host edited: " << i << endl;
353
354     hosts_list->replace(i, host_tmp);
355     list->takeItem(i);
356     list->insertItem(i, hosts_list->at(i).hostname);
357     save_btn->setDisabled(false);
358     save_btn->setDefault(true);
359 }
360
361 HostWidget::HostWidget(QWidget *parent, host_s *host)
362         : QWidget(parent)
363 {
364     //TODO: add setValidator and use QRegExpValidator
365
366     qDebug() << "host widget" << endl;
367     new_host = host;
368     this->setWindowTitle(tr("WOL target details"));
369 #ifdef Q_WS_MAEMO_5
370     this->setAttribute(Qt::WA_Maemo5StackedWindow);
371 #else
372     this->setWindowModality(Qt::ApplicationModal);
373 #endif
374     this->setWindowFlags(this->windowFlags() | Qt::Window);
375     vlayout = new QVBoxLayout(this);
376
377     host_row = new QHBoxLayout;
378     host_lbl = new QLabel(tr("Hostname:"), this);
379     hostname = new QLineEdit(host->hostname, this);
380     hostname->setToolTip(tr("Give target computer's hostname"));
381     host_row->addWidget(host_lbl);
382     host_row->addWidget(hostname);
383     vlayout->addLayout(host_row);
384
385     mac_row = new QHBoxLayout;
386     mac_lbl = new QLabel(tr("Mac:"), this);
387     mac = new QLineEdit(host->mac, this);
388     mac->setToolTip(
389             tr("HW address of target, e.g. 00:01:02:AA:BB:CC. "
390                "Hint, in Linux/Unix: \"ifconfig\","
391                "in Windows \"ipconfig /all\""));
392     mac->setInputMask("HH:HH:HH:HH:HH:HH;0");
393     mac_row->addWidget(mac_lbl);
394     mac_row->addWidget(mac);
395     vlayout->addLayout(mac_row);
396
397     ip_row = new QHBoxLayout;
398     ip_lbl = new QLabel(tr("Bcast ip:"), this);
399     ip = new QLineEdit(host->ip.toString(), this);
400     ip->setToolTip(
401             tr("Use ip broadcast address if you want to wake up target outside"
402                "of local network, or if you have several network interfaces."
403                "E.g. 192.168.1.255. You may leave this empty if unsure."));
404     ip->setMaxLength(MAX_IP_LEN);
405     ip_row->addWidget(ip_lbl);
406     ip_row->addWidget(ip);
407     vlayout->addLayout(ip_row);
408
409     button_row = new QHBoxLayout;
410     cancel = new QPushButton(tr("&Cancel"), this);
411     button_row->addWidget(cancel);
412     ok = new QPushButton(tr("&Ok"), this);
413     ok->setDefault(true);
414     button_row->addWidget(ok);
415     vlayout->addLayout(button_row);
416
417     this->setLayout(vlayout);
418
419     ok->setDefault(true);
420
421     connect(ok, SIGNAL(clicked()), this, SLOT(ok_sig()));
422     connect(cancel, SIGNAL(clicked()), this, SLOT(cancel_sig()));
423
424 }
425
426 void HostWidget::cancel_sig()
427 {
428     qDebug() << "host cancel" << endl;
429     this->close();
430 }
431
432 void HostWidget::ok_sig()
433 {
434     qDebug() << "host ok" << this->hostname->text() << this->mac->text()
435             << this->ip->text() << endl;
436
437     new_host->hostname = this->hostname->text();
438     if (new_host->hostname.isEmpty())
439     {
440         QMessageBox msgBox;
441         msgBox.setText("You must set hostname!");
442         msgBox.exec();
443         return;
444     }
445     new_host->mac= this->mac->text();
446     if (new_host->mac.isEmpty())
447     {
448         QMessageBox msgBox;
449         msgBox.setText("You must set mac!");
450         msgBox.exec();
451         return;
452     }
453     new_host->ip = this->ip->text();
454     emit change_host();
455
456     qDebug() << "host ok" << new_host->hostname << new_host->mac
457             << new_host->ip << endl;
458
459     this->close();
460 }
461
462 HostWidget::~HostWidget()
463 {
464     qDebug() << "host ~widget" << endl;
465     delete host_row;
466     delete host_lbl;
467     delete hostname;
468     delete mac_row;
469     delete mac_lbl;
470     delete mac;
471     delete ip_row;
472     delete ip_lbl;
473     delete ip;
474     delete button_row;
475     delete cancel;
476     delete ok;
477     delete vlayout;
478 }
479
480
481 int wol_target::get_mac( QString *gmac)
482 {
483     if (!gmac)
484         return -1;
485     *gmac = *mac;
486     return 0;
487 }
488
489 int wol_target::set_mac( const QString smac )
490 {
491     if (smac.size() != MAX_MAC_LEN) {
492         qDebug() << "set mac mac size:" << smac.size() << smac << endl;
493         //return -1;
494     }
495     *mac = smac;
496     qDebug() << "set mac mac:" << mac;
497     return 0;
498 }
499
500 int wol_target::get_ip( QHostAddress *gip)
501 {
502     if (!gip)
503         return -1;
504     *gip = *ip;
505     return 0;
506 }
507
508 int wol_target::set_ip( const QHostAddress sip )
509 {
510     if (sip.isNull())
511         return -1;
512     *ip = sip;
513     return 0;
514 }
515
516
517 wol_target::wol_target()
518 {
519     mac = new QString;
520     ip = new QHostAddress;
521     magic_pkt = new QByteArray;
522 }
523
524 wol_target::~wol_target()
525 {
526     delete mac;
527     delete ip;
528     delete magic_pkt;
529 }
530
531 int wol_target::create_magic_pkt(const QString *mac)
532 {
533     const char magic_header[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
534     qDebug() << "create magic pkt" << endl;
535     if (mac->size() != MAX_MAC_LEN)
536     {
537         qDebug() << "create magic pkt size:" << mac->size() << endl;
538         //return -1;
539     }
540     *magic_pkt = QByteArray::fromRawData(magic_header, sizeof(magic_header));
541     for (int i=0; i < WOL_MAGIC_MAC_CNT; i++)
542         magic_pkt->append(QByteArray::fromHex(mac->toAscii()));
543     return 0;
544 }
545
546 int wol_target::wake_me()
547 {
548     QUdpSocket udpSocket;
549
550     if (mac->isEmpty()) {
551         qDebug() << "wakeme: no mac set" << endl;
552         return -1;
553     }
554     create_magic_pkt(mac);
555     qDebug() << "magic:" << endl;
556     qDebug() << magic_pkt->toHex() << endl ;
557
558     if (ip->isNull()) {
559         qDebug() << "sending broadcast to mac" << *mac << endl;
560         udpSocket.writeDatagram(magic_pkt->data(), magic_pkt->size(),
561                                 QHostAddress::Broadcast,
562                                 WOL_MAGIC_UDP_PORT);
563     }
564     else {
565         qDebug() << "sending with ip" << *ip << endl;
566         udpSocket.writeDatagram(magic_pkt->data(), magic_pkt->size(),
567                                 *ip,
568                                 WOL_MAGIC_UDP_PORT);
569     }
570     return 0;
571 }
572