updated packaging changelogs
[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;_");
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
446     new_host->mac = this->mac->text();
447     if (new_host->mac.length() != MAX_MAC_STR_LEN)
448     {
449         QMessageBox msgBox;
450         msgBox.setText("You must set mac to full 12 digits!\n" \
451                        "E.g. 00:11:22:33:44:55:66");
452         msgBox.exec();
453         return;
454     }
455     new_host->ip = this->ip->text();
456     emit change_host();
457
458     qDebug() << "host ok" << new_host->hostname << new_host->mac
459             << new_host->ip << endl;
460
461     this->close();
462 }
463
464 HostWidget::~HostWidget()
465 {
466     qDebug() << "host ~widget" << endl;
467     delete host_row;
468     delete host_lbl;
469     delete hostname;
470     delete mac_row;
471     delete mac_lbl;
472     delete mac;
473     delete ip_row;
474     delete ip_lbl;
475     delete ip;
476     delete button_row;
477     delete cancel;
478     delete ok;
479     delete vlayout;
480 }
481
482
483 int wol_target::get_mac( QString *gmac)
484 {
485     if (!gmac)
486         return -1;
487     *gmac = *mac;
488     return 0;
489 }
490
491 int wol_target::set_mac( const QString smac )
492 {
493     if (smac.size() != MAX_MAC_LEN) {
494         qDebug() << "set mac mac size:" << smac.size() << smac << endl;
495         //return -1;
496     }
497     *mac = smac;
498     qDebug() << "set mac mac:" << mac;
499     return 0;
500 }
501
502 int wol_target::get_ip( QHostAddress *gip)
503 {
504     if (!gip)
505         return -1;
506     *gip = *ip;
507     return 0;
508 }
509
510 int wol_target::set_ip( const QHostAddress sip )
511 {
512     if (sip.isNull())
513         return -1;
514     *ip = sip;
515     return 0;
516 }
517
518
519 wol_target::wol_target()
520 {
521     mac = new QString;
522     ip = new QHostAddress;
523     magic_pkt = new QByteArray;
524 }
525
526 wol_target::~wol_target()
527 {
528     delete mac;
529     delete ip;
530     delete magic_pkt;
531 }
532
533 int wol_target::create_magic_pkt(const QString *mac)
534 {
535     const char magic_header[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
536     qDebug() << "create magic pkt" << endl;
537     if (mac->size() != MAX_MAC_LEN)
538     {
539         qDebug() << "create magic pkt size:" << mac->size() << endl;
540         //return -1;
541     }
542     *magic_pkt = QByteArray::fromRawData(magic_header, sizeof(magic_header));
543     for (int i=0; i < WOL_MAGIC_MAC_CNT; i++)
544         magic_pkt->append(QByteArray::fromHex(mac->toAscii()));
545     return 0;
546 }
547
548 int wol_target::wake_me()
549 {
550     QUdpSocket udpSocket;
551
552     if (mac->isEmpty()) {
553         qDebug() << "wakeme: no mac set" << endl;
554         return -1;
555     }
556     create_magic_pkt(mac);
557     qDebug() << "magic:" << endl;
558     qDebug() << magic_pkt->toHex() << endl ;
559
560     if (ip->isNull()) {
561         qDebug() << "sending broadcast to mac" << *mac << endl;
562         udpSocket.writeDatagram(magic_pkt->data(), magic_pkt->size(),
563                                 QHostAddress::Broadcast,
564                                 WOL_MAGIC_UDP_PORT);
565     }
566     else {
567         qDebug() << "sending with ip" << *ip << endl;
568         udpSocket.writeDatagram(magic_pkt->data(), magic_pkt->size(),
569                                 *ip,
570                                 WOL_MAGIC_UDP_PORT);
571     }
572     return 0;
573 }
574