1/*
2 * kPPP: A pppd front end for the KDE project
3 *
4 *
5 * Copyright (C) 1997 Bernd Johannes Wuebben
6 * wuebben@math.cornell.edu
7 * Copyright (C) 1998-2001 Harri Porten <porten@kde.org>
8 *
9 * based on EzPPP:
10 * Copyright (C) 1997 Jay Painter
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free
24 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26
27#include <config-kppp.h>
28
29#include <qlayout.h>
30#include <qregexp.h>
31//Added by qt3to4:
32#include <QLabel>
33#include <QTimerEvent>
34#include <QVBoxLayout>
35#include <QFrame>
36#include <QHBoxLayout>
37#include <Q3CString>
38#include <QCloseEvent>
39
40#include <kdefakes.h> // gethostname
41#include <kdebug.h>
42#include <klocale.h>
43#include <kmessagebox.h>
44#include <kpushbutton.h>
45#include <kapplication.h>
46
47#include <unistd.h>
48#include <stdlib.h>
49#include <string.h>
50#include <fcntl.h>
51#include <netdb.h>
52#include <sys/types.h>
53#include <sys/socket.h>
54#include <arpa/inet.h>
55#include <netinet/in.h>
56#include <sys/ioctl.h>
57#include <assert.h>
58
59#ifdef _XPG4_2
60#define __xnet_connect connect
61#endif
62
63#include <errno.h>
64
65#ifdef HAVE_SYS_PARAM_H
66#include <sys/param.h>
67#endif
68
69#ifdef __linux__
70#include "runtests.h"
71#endif
72
73#include "auth.h"
74#include "connect.h"
75#include "docking.h"
76#include "main.h"
77#include "modem.h"
78#include "kpppconfig.h"
79#include "pppdata.h"
80#include "pppstats.h"
81#include "requester.h"
82#include "utils.h"
83#include "kpppinterface.h"
84
85extern KPPPWidget *p_kppp;
86
87QString old_hostname;
88bool modified_hostname;
89
90
91ConnectWidget::ConnectWidget(QWidget *parent, const char *name, PPPStats *st)
92 : QWidget(parent),
93 // initialize some important variables
94 myreadbuffer(""),
95 main_timer_ID(0),
96 vmain(0),
97 substate(-1),
98 scriptindex(0),
99 loopnest(0),
100 loopend(false),
101 semaphore(false),
102 expecting(false),
103 readbuffer(""),
104 scanvar(""),
105 scanning(false),
106 pausing(false),
107 termwindow(0),
108 dialnumber(0),
109 stats(st)
110{
111 setObjectName(name);
112
113 modified_hostname = false;
114
115 QVBoxLayout *tl = new QVBoxLayout(this);
116 tl->setSpacing(10);
117 tl->setMargin(8);
118 QString tit = i18n("Connecting to: ");
119 setWindowTitle(tit);
120
121 QHBoxLayout *l0 = new QHBoxLayout();
122 l0->setSpacing( 10 );
123 tl->addLayout(l0);
124 l0->addSpacing(10);
125 messg = new QLabel( this );
126 messg->setObjectName( "messg" );
127 messg->setFrameStyle(QFrame::Panel|QFrame::Sunken);
128 messg->setAlignment(Qt::AlignCenter);
129 messg->setText(i18n("Unable to create modem lock file."));
130 messg->setMinimumHeight(messg->sizeHint().height() + 5);
131 int messw = (messg->sizeHint().width() * 12) / 10;
132 messw = qMax(messw,280);
133 messg->setMinimumWidth(messw);
134 messg->setText(i18n("Looking for modem..."));
135 l0->addWidget(messg);
136 l0->addSpacing(10);
137
138 QHBoxLayout *l1 = new QHBoxLayout();
139 l1->setSpacing( 10 );
140 tl->addLayout(l1);
141 l1->addStretch(1);
142
143 debug = new QPushButton(i18n("&Log"), this);
144 debug->setCheckable(true);
145 connect(debug, SIGNAL(clicked()), SIGNAL(toggleDebugWindow()));
146
147 cancel = new KPushButton(KStandardGuiItem::cancel(), this);
148 cancel->setFocus();
149 connect(cancel, SIGNAL(clicked()), SLOT(cancelbutton()));
150
151 int maxw = qMax(cancel->sizeHint().width(),
152 debug->sizeHint().width());
153 maxw = qMax(maxw,65);
154 debug->setFixedWidth(maxw);
155 cancel->setFixedWidth(maxw);
156 l1->addWidget(debug);
157 l1->addWidget(cancel);
158 l1->addSpacing(10);
159
160 setFixedSize(sizeHint());
161
162 pausetimer = new QTimer(this);
163 connect(pausetimer, SIGNAL(timeout()), SLOT(pause()));
164
165 qApp->processEvents();
166
167 timeout_timer = new QTimer(this);
168 connect(timeout_timer, SIGNAL(timeout()), SLOT(script_timed_out()));
169
170 inittimer = new QTimer(this);
171 connect(inittimer, SIGNAL(timeout()), SLOT(init()));
172
173 if_timeout_timer = new QTimer(this);
174 connect(if_timeout_timer, SIGNAL(timeout()), SLOT(if_waiting_timed_out()));
175
176 connect(this,SIGNAL(if_waiting_signal()),this,SLOT(if_waiting_slot()));
177
178 prompt = new PWEntry( this, "pw" );
179 if_timer = new QTimer(this);
180 connect(if_timer,SIGNAL(timeout()), SLOT(if_waiting_slot()));
181}
182
183
184ConnectWidget::~ConnectWidget() {
185}
186
187
188void ConnectWidget::disableButtons()
189{
190 debug->setEnabled(false);
191 cancel->setEnabled(false);
192}
193
194void ConnectWidget::enableButtons()
195{
196 debug->setEnabled(true);
197 cancel->setEnabled(true);
198}
199
200void ConnectWidget::preinit() {
201 // this is all just to keep the GUI nice and snappy ....
202 // you have to see to believe ...
203 messg->setText(i18n("Looking for modem..."));
204 inittimer->start(100);
205}
206
207void ConnectWidget::init() {
208 gpppdata.setpppdError(0);
209 inittimer->stop();
210 vmain = 0;
211 substate = -1;
212 expecting = false;
213 pausing = false;
214 scriptindex = 0;
215 myreadbuffer = "";
216 scanning = false;
217 scanvar = "";
218 firstrunID = true;
219 firstrunPW = true;
220 stats->totalbytes = 0;
221 dialnumber = 0;
222
223 p_kppp->con_speed = "";
224
225 p_kppp->setQuitOnDisconnect (p_kppp->quitOnDisconnect() || gpppdata.quit_on_disconnect());
226
227 comlist = &gpppdata.scriptType();
228 arglist = &gpppdata.script();
229
230 QString tit = i18n("Connecting to: %1", gpppdata.accname());
231 setWindowTitle(tit);
232
233 qApp->processEvents();
234
235 // signal other applications that we are about to get connected
236 emit aboutToConnect();
237 // run the "before-connect" command
238 if (!gpppdata.command_before_connect().isEmpty()) {
239 messg->setText(i18n("Running pre-startup command..."));
240 emit debugMessage(i18n("Running pre-startup command..."));
241
242 qApp->processEvents();
243 QApplication::flush();
244 pid_t id = execute_command(gpppdata.command_before_connect());
245 int i, status;
246
247 do {
248 qApp->processEvents();
249 i = waitpid(id, &status, WNOHANG);
250 usleep(100000);
251 } while (i == 0 && errno == 0);
252 }
253
254 int lock = Modem::modem->lockdevice();
255
256 if (lock == 1) {
257 messg->setText(i18n("Modem device is locked."));
258 vmain = 20; // wait until cancel is pressed
259 return;
260 }
261
262 if (lock == -1) {
263 messg->setText(i18n("Unable to create modem lock file."));
264 vmain = 20; // wait until cancel is pressed
265 return;
266 }
267
268 if(Modem::modem->opentty()) {
269 messg->setText(Modem::modem->modemMessage());
270 qApp->processEvents();
271 if(Modem::modem->hangup()) {
272
273 qApp->processEvents();
274
275 semaphore = false;
276
277 Modem::modem->stop();
278 Modem::modem->notify(this, SLOT(readChar(unsigned char)));
279
280 // if we are stuck anywhere we will time out
281 timeout_timer->start(gpppdata.modemTimeout()*1000);
282
283 // this timer will run the script etc.
284 main_timer_ID = startTimer(10);
285
286 return;
287 }
288 }
289
290 // initialization failed
291 messg->setText(Modem::modem->modemMessage());
292 vmain = 20; // wait until cancel is pressed
293 Modem::modem->unlockdevice();
294}
295
296
297void ConnectWidget::timerEvent(QTimerEvent *) {
298 if (semaphore || pausing)
299 return;
300
301 if(vmain == 0) {
302#ifdef DEBUG_WO_DIALING
303 vmain = 10;
304 return;
305#endif
306
307 assert(PPPData::NumInitStrings > 0);
308 // first init string ?
309 if(substate == -1) {
310 messg->setText(i18n("Initializing modem..."));
311 emit debugMessage(i18n("Initializing modem..."));
312 substate = 0;
313 }
314
315 QString initStr = gpppdata.modemInitStr(substate);
316 if (!initStr.isEmpty()) {
317 timeout_timer->stop();
318
319 // send a carriage return and then wait a bit so that the modem will
320 // let us issue commands.
321 if(gpppdata.modemPreInitDelay() > 0) {
322 usleep(gpppdata.modemPreInitDelay() * 5000);
323 writeline("");
324 usleep(gpppdata.modemPreInitDelay() * 5000);
325 }
326 setExpect(gpppdata.modemInitResp());
327 writeline(initStr);
328 timeout_timer->start(gpppdata.modemTimeout()*1000);
329 usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 sec
330 }
331
332 substate++;
333
334 /*
335 * FIXME after 3.0: Make it possible to disable ATS11 since it
336 * seems to be incompatible with some ISDN adapters (e.g. DataBox
337 * Speed Dragon). Even better would be to detect this when doing
338 * a "Modem Query"
339 */
340 if (MODEM_TONEDURATION != gpppdata.modemToneDuration())
341 vmain = 5;
342 else
343 vmain = 3;
344
345 return;
346 }
347
348 if (vmain == 5) {
349 if(!expecting) {
350 QString sToneDuration = "ATS11=" + QString::number(gpppdata.modemToneDuration());
351 QString msg = i18n("Setting ") + sToneDuration;
352 messg->setText(msg);
353 emit debugMessage(msg);
354 setExpect(gpppdata.modemInitResp());
355 writeline(sToneDuration);
356 }
357 vmain = 3;
358 return;
359 }
360
361 if(vmain == 3) {
362 if(!expecting) {
363 // done with all init strings ?
364 if(substate < PPPData::NumInitStrings) {
365 vmain = 0;
366 return;
367 }
368 substate = -1;
369 // skip setting the volume if command is empty
370 if(gpppdata.volumeInitString().isEmpty()) {
371 vmain = 4;
372 return;
373 }
374 messg->setText(i18n("Setting speaker volume..."));
375 emit debugMessage(i18n("Setting speaker volume..."));
376
377 setExpect(gpppdata.modemInitResp());
378 QString vol("AT");
379 vol += gpppdata.volumeInitString();
380 writeline(vol);
381 usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 sec
382 vmain = 4;
383 return;
384 }
385 }
386
387 if(vmain == 4) {
388 if(!expecting) {
389 if(!gpppdata.waitForDialTone() || gpppdata.waitCallback()) {
390 QString msg = i18n("Turning off dial tone waiting...");
391 messg->setText(msg);
392 emit debugMessage(msg);
393 setExpect(gpppdata.modemInitResp());
394 writeline(gpppdata.modemNoDialToneDetectionStr());
395 }
396 vmain = 1;
397 return;
398 }
399 }
400
401 // dial the number and wait to connect
402 if(vmain == 1) {
403 if(!expecting) {
404
405 timeout_timer->stop();
406 timeout_timer->start(gpppdata.modemTimeout()*1000);
407
408 if(gpppdata.waitCallback()) {
409 QString msg = i18n("Waiting for callback...");
410 messg->setText(msg);
411 emit debugMessage(msg);
412 setExpect(gpppdata.modemRingResp());
413 vmain = 102;
414 return;
415 }
416
417 QStringList &plist = gpppdata.phonenumbers();
418 QString bmarg= gpppdata.dialPrefix();
419 bmarg += plist.at(dialnumber);
420 QString bm = i18n("Dialing %1", bmarg);
421 messg->setText(bm);
422 emit debugMessage(bm);
423
424 QString pn = gpppdata.modemDialStr();
425 pn += gpppdata.dialPrefix();
426 pn += plist.at(dialnumber);
427 if(++dialnumber >= plist.count())
428 dialnumber = 0;
429 writeline(pn);
430
431 setExpect(gpppdata.modemConnectResp());
432 vmain = 100;
433 return;
434 }
435 }
436
437 // wait for connect, but redial if BUSY or wait for user cancel
438 // if NO CARRIER or NO DIALTONE
439 if(vmain == 100) {
440 if(!expecting) {
441 myreadbuffer = gpppdata.modemConnectResp();
442 setExpect("\n");
443 vmain = 101;
444 return;
445 }
446
447 if(readbuffer.contains(gpppdata.modemBusyResp())) {
448 timeout_timer->stop();
449 timeout_timer->start(gpppdata.modemTimeout()*1000);
450
451 messg->setText(i18n("Line busy. Hanging up..."));
452 emit debugPutChar('\n');
453 Modem::modem->hangup();
454
455 if(gpppdata.busyWait() > 0) {
456 QString bm = i18n("Line busy. Waiting: %1 seconds", gpppdata.busyWait());
457 messg->setText(bm);
458 emit debugMessage(bm);
459
460 pausing = true;
461
462 pausetimer->setSingleShot(true);
463 pausetimer->start(gpppdata.busyWait()*1000);
464 timeout_timer->stop();
465 }
466
467 Modem::modem->setDataMode(false);
468 vmain = 0;
469 substate = -1;
470 gpppdata.setWaitCallback(false);
471 return;
472 }
473
474 if(readbuffer.contains(gpppdata.modemNoDialtoneResp())) {
475 timeout_timer->stop();
476
477 messg->setText(i18n("No Dial Tone"));
478 vmain = 20;
479 Modem::modem->unlockdevice();
480 gpppdata.setWaitCallback(false);
481 return;
482 }
483
484 if(readbuffer.contains(gpppdata.modemNoCarrierResp())) {
485 if (gpppdata.get_redial_on_nocarrier()) {
486 timeout_timer->stop();
487 timeout_timer->start(gpppdata.modemTimeout()*1000);
488
489 if(gpppdata.busyWait() > 0) {
490 QString bm = i18n("No carrier. Waiting: %1 seconds", gpppdata.busyWait());
491 messg->setText(bm);
492 emit debugMessage(bm);
493
494 pausing = true;
495
496 pausetimer->setSingleShot(true);
497 pausetimer->start(gpppdata.busyWait()*1000);
498 timeout_timer->stop();
499 }
500
501 Modem::modem->setDataMode(false);
502 vmain = 0;
503 substate = -1;
504 return;
505 } else {
506 timeout_timer->stop();
507
508 messg->setText(i18n("No Carrier"));
509 vmain = 20;
510 Modem::modem->unlockdevice();
511 gpppdata.setWaitCallback(false);
512 }
513 return;
514 }
515
516 if(readbuffer.contains(gpppdata.modemDLPResp())) {
517 timeout_timer->stop();
518
519 messg->setText(i18n("Digital Line Protection Detected."));
520 vmain = 20;
521 Modem::modem->unlockdevice();
522 KMessageBox::error(this,
523 i18n("A Digital Line Protection (DLP) error response "
524 "has been detected.\n"
525 "Please disconnect the phone line.\n\n"
526 "Do NOT connect this modem to a digital phone "
527 "line or the modem could get permanently "
528 "damaged"));
529 gpppdata.setWaitCallback(false);
530 return;
531 }
532
533
534 }
535
536 // wait for newline after CONNECT response (so we get the speed)
537 if(vmain == 101) {
538 if(!expecting) {
539 Modem::modem->setDataMode(true); // modem will no longer respond to AT commands
540
541 emit startAccounting();
542 p_kppp->con_win->startClock();
543
544 vmain = 2;
545 scriptTimeout=gpppdata.modemTimeout()*1000;
546 return;
547 }
548 }
549
550 // send answer on callback phase
551 if(vmain == 102) {
552 if(!expecting) {
553 writeline(gpppdata.modemAnswerStr());
554 setExpect(gpppdata.modemConnectResp());
555 vmain = 100;
556 return;
557 }
558 }
559
560 // execute the script
561 if(vmain == 2) {
562 if(!expecting && !pausing && !scanning) {
563
564 timeout_timer->stop();
565 timeout_timer->start(scriptTimeout);
566
567 if(scriptindex < comlist->count()) {
568 scriptCommand = (comlist->at(scriptindex));
569 scriptArgument = (arglist->at(scriptindex));
570 } else {
571 kDebug(5002) << "End of script";
572 vmain = 10;
573 return;
574 }
575
576 if (scriptCommand == "Scan") {
577 QString bm = i18n("Scanning %1", scriptArgument);
578 messg->setText(bm);
579 emit debugMessage(bm);
580
581 setScan(scriptArgument);
582 scriptindex++;
583 return;
584 }
585
586 if (scriptCommand == "Save") {
587 QString bm = i18n("Saving %1", scriptArgument);
588 messg->setText(bm);
589 emit debugMessage(bm);
590
591 if (scriptArgument.toLower() == "password") {
592 gpppdata.setPassword(scanvar);
593 p_kppp->setPW_Edit(scanvar);
594 if(gpppdata.storePassword())
595 gpppdata.setStoredPassword(scanvar);
596 firstrunPW = true;
597 }
598
599 scriptindex++;
600 return;
601 }
602
603
604 if (scriptCommand == "Send" || scriptCommand == "SendNoEcho") {
605 // replace %USERNAME% and %PASSWORD%
606 QString arg = scriptArgument;
607 QRegExp re1("%USERNAME%");
608 QRegExp re2("%PASSWORD%");
609 arg = arg.replace(re1, gpppdata.storedUsername());
610 arg = arg.replace(re2, gpppdata.storedPassword());
611
612 QString scriptarg;
613 if (scriptCommand == "Send")
614 scriptarg = scriptArgument;
615 else {
616 for(int i = 0; i < scriptArgument.length(); i++)
617 scriptarg += '*';
618 }
619 QString bm = i18n("Sending %1", scriptarg);
620
621 messg->setText(bm);
622 emit debugMessage(bm);
623
624 writeline(scriptArgument);
625 scriptindex++;
626 return;
627 }
628
629 if (scriptCommand == "Expect") {
630 QString bm = i18n("Expecting %1", scriptArgument);
631 messg->setText(bm);
632 emit debugMessage(bm);
633
634 // The incrementing of the scriptindex MUST be before the
635 // call to setExpect otherwise the expect will miss a string that is
636 // already in the buffer.
637 scriptindex++;
638 setExpect(scriptArgument);
639 return;
640 }
641
642
643 if (scriptCommand == "Pause") {
644 QString bm = i18n("Pause %1 seconds", scriptArgument);
645 messg->setText(bm);
646 emit debugMessage(bm);
647
648 pausing = true;
649
650 pausetimer->setSingleShot(true);
651 pausetimer->start(scriptArgument.toInt()*1000);
652 timeout_timer->stop();
653
654 scriptindex++;
655 return;
656 }
657
658 if (scriptCommand == "Timeout") {
659
660 timeout_timer->stop();
661
662 QString bm = i18n("Timeout %1 seconds", scriptArgument);
663 messg->setText(bm);
664 emit debugMessage(bm);
665
666 scriptTimeout=scriptArgument.toInt()*1000;
667 timeout_timer->start(scriptTimeout);
668
669 scriptindex++;
670 return;
671 }
672
673 if (scriptCommand == "Hangup") {
674 messg->setText(i18n("Hangup"));
675 emit debugMessage(i18n("Hangup"));
676
677 writeline(gpppdata.modemHangupStr());
678 setExpect(gpppdata.modemHangupResp());
679
680 scriptindex++;
681 return;
682 }
683
684 if (scriptCommand == "Answer") {
685
686 timeout_timer->stop();
687
688 messg->setText(i18n("Answer"));
689 emit debugMessage(i18n("Answer"));
690
691 setExpect(gpppdata.modemRingResp());
692 vmain = 150;
693 return;
694 }
695
696 if (scriptCommand == "ID") {
697 QString bm = i18n("ID %1", scriptArgument);
698 messg->setText(bm);
699 emit debugMessage(bm);
700
701 QString idstring = gpppdata.storedUsername();
702
703 if(!idstring.isEmpty() && firstrunID) {
704 // the user entered an Id on the main kppp dialog
705 writeline(idstring);
706 firstrunID = false;
707 scriptindex++;
708 }
709 else {
710 // the user didn't enter and Id on the main kppp dialog
711 // let's query for an ID
712 /* if not around yet, then post window... */
713 if (prompt->Consumed()) {
714 if (!(prompt->isVisible())) {
715 prompt->setPrompt(scriptArgument);
716 prompt->setEchoModeNormal();
717 prompt->show();
718 }
719 } else {
720 /* if prompt withdrawn ... then, */
721 if(!(prompt->isVisible())) {
722 writeline(prompt->text());
723 prompt->setConsumed();
724 scriptindex++;
725 return;
726 }
727 /* replace timeout value */
728 }
729 }
730 }
731
732 if (scriptCommand == "Password") {
733 QString bm = i18n("Password %1", scriptArgument);
734 messg->setText(bm);
735 emit debugMessage(bm);
736
737 QString pwstring = gpppdata.password();
738
739 if(!pwstring.isEmpty() && firstrunPW) {
740 // the user entered a password on the main kppp dialog
741 writeline(pwstring);
742 firstrunPW = false;
743 scriptindex++;
744 }
745 else {
746 // the user didn't enter a password on the main kppp dialog
747 // let's query for a password
748 /* if not around yet, then post window... */
749 if (prompt->Consumed()) {
750 if (!(prompt->isVisible())) {
751 prompt->setPrompt(scriptArgument);
752 prompt->setEchoModePassword();
753 prompt->show();
754 }
755 } else {
756 /* if prompt withdrawn ... then, */
757 if(!(prompt->isVisible())) {
758 p_kppp->setPW_Edit(prompt->text());
759 writeline(prompt->text());
760 prompt->setConsumed();
761 scriptindex++;
762 return;
763 }
764 /* replace timeout value */
765 }
766 }
767 }
768
769 if (scriptCommand == "Prompt") {
770 // if the scriptindex (aka the prompt text) includes a ## marker
771 // this marker should get substituted with the contents of our stored
772 // variable (from the subsequent scan).
773
774 QString ts = scriptArgument;
775 int vstart = ts.indexOf( "##" );
776 if( vstart != -1 ) {
777 ts.remove( vstart, 2 );
778 ts.insert( vstart, scanvar );
779 }
780
781 QString bm = i18n("Prompting %1", ts);
782 messg->setText(bm);
783 emit debugMessage(bm);
784
785 /* if not around yet, then post window... */
786 if (prompt->Consumed()) {
787 if (!(prompt->isVisible())) {
788 prompt->setPrompt( ts );
789 prompt->setEchoModeNormal();
790 prompt->show();
791 }
792 } else {
793 /* if prompt withdrawn ... then, */
794 if (!(prompt->isVisible())) {
795 writeline(prompt->text());
796 prompt->setConsumed();
797 scriptindex++;
798 return;
799 }
800 /* replace timeout value */
801 }
802 }
803
804 if (scriptCommand == "PWPrompt") {
805 QString bm = i18n("PW Prompt %1", scriptArgument);
806 messg->setText(bm);
807 emit debugMessage(bm);
808
809 /* if not around yet, then post window... */
810 if (prompt->Consumed()) {
811 if (!(prompt->isVisible())) {
812 prompt->setPrompt(scriptArgument);
813 prompt->setEchoModePassword();
814 prompt->show();
815 }
816 } else {
817 /* if prompt withdrawn ... then, */
818 if (!(prompt->isVisible())) {
819 writeline(prompt->text());
820 prompt->setConsumed();
821 scriptindex++;
822 return;
823 }
824 /* replace timeout value */
825 }
826 }
827
828 if (scriptCommand == "LoopStart") {
829
830 QString bm = i18n("Loop Start %1", scriptArgument);
831
832 // The incrementing of the scriptindex MUST be before the
833 // call to setExpect otherwise the expect will miss a string that is
834 // already in the buffer.
835 scriptindex++;
836
837 if ( loopnest > (MAXLOOPNEST-2) ) {
838 bm += i18n("ERROR: Nested too deep, ignored.");
839 vmain=20;
840 cancelbutton();
841 KMessageBox::error(0, i18n("Loops nested too deeply."));
842 } else {
843 setExpect(scriptArgument);
844 loopstartindex[loopnest] = scriptindex;
845 loopstr[loopnest] = scriptArgument;
846 loopend = false;
847 loopnest++;
848 }
849 messg->setText(bm);
850 emit debugMessage(bm);
851
852 }
853
854 if (scriptCommand == "LoopEnd") {
855 QString bm = i18n("Loop End %1", scriptArgument);
856 if ( loopnest <= 0 ) {
857 bm = i18n("LoopEnd without matching Start. Line: %1", bm);
858 vmain=20;
859 cancelbutton();
860 KMessageBox::error(0, bm);
861 return;
862 } else {
863 // NB! The incrementing of the scriptindex MUST be before the
864 // call to setExpect otherwise the expect will miss a string
865 // that is already in the buffer.
866 scriptindex++;
867 setExpect(scriptArgument);
868 loopnest--;
869 loopend = true;
870 }
871 messg->setText(bm);
872 emit debugMessage(bm);
873
874 }
875 }
876 }
877
878 // this is a subroutine for the "Answer" script option
879
880 if(vmain == 150) {
881 if(!expecting) {
882 writeline(gpppdata.modemAnswerStr());
883 setExpect(gpppdata.modemAnswerResp());
884
885 vmain = 2;
886 scriptindex++;
887 return;
888 }
889 }
890
891 if(vmain == 30) {
892 if (termwindow->isVisible())
893 return;
894 if (termwindow->pressedContinue())
895 vmain = 10;
896 else
897 cancelbutton();
898 }
899
900 if(vmain == 10) {
901 if(!expecting) {
902
903 int result;
904
905 timeout_timer->stop();
906 if_timeout_timer->stop(); // better be sure.
907
908 // stop reading of data
909 Modem::modem->stop();
910
911 if(gpppdata.authMethod() == AUTH_TERMINAL) {
912 if (termwindow) {
913 delete termwindow;
914 termwindow = 0L;
915 show();
916 } else {
917 termwindow = new LoginTerm(0L, 0L);
918 hide();
919 termwindow->show();
920 vmain = 30;
921 return;
922 }
923 }
924
925 // Close the tty. This prevents the QTimer::singleShot() in
926 // Modem::readtty() from re-enabling the socket notifier.
927 // The port is still held open by the helper process.
928 Modem::modem->closetty();
929
930 killTimer( main_timer_ID );
931
932 if_timeout_timer->start(gpppdata.pppdTimeout()*1000);
933 kDebug(5002) << "started if timeout timer with " << gpppdata.pppdTimeout()*1000;
934
935 // find out PPP interface and notify the stats module
936 stats->setUnit(pppInterfaceNumber());
937
938 kapp->flush();
939 semaphore = true;
940 result = execppp();
941
942 emit debugMessage(i18n("Starting pppd..."));
943 kDebug(5002) << "execppp() returned with return-code " << result;
944
945 if(result) {
946 if(!gpppdata.autoDNS())
947 adddns();
948
949 // O.K we are done here, let's change over to the if_waiting loop
950 // where we wait for the ppp if (interface) to come up.
951
952 emit if_waiting_signal();
953 } else {
954
955 // starting pppd wasn't successful. Error messages were
956 // handled by execppp();
957 if_timeout_timer->stop();
958
959 hide();
960 messg->setText("");
961 p_kppp->quit_b->setFocus();
962 p_kppp->show();
963 qApp->processEvents();
964 Modem::modem->hangup();
965 emit stopAccounting();
966 p_kppp->con_win->stopClock();
967 Modem::modem->closetty();
968 Modem::modem->unlockdevice();
969
970 }
971
972 return;
973 }
974 }
975
976 // this is a "wait until cancel" entry
977
978 if(vmain == 20) {
979 gpppdata.setWaitCallback(false);
980 }
981}
982
983
984void ConnectWidget::set_con_speed_string() {
985 // Here we are trying to determine the speed at which we are connected.
986 // Usually the modem responds after connect with something like
987 // CONNECT 115200, so all we need to do is find the number after CONNECT
988 // or whatever the modemConnectResp() is.
989 p_kppp->con_speed = Modem::modem->parseModemSpeed(myreadbuffer);
990}
991
992
993
994void ConnectWidget::readChar(unsigned char c) {
995 if(semaphore)
996 return;
997
998 readbuffer += c;
999 myreadbuffer += c;
1000
1001 // While in scanning mode store each char to the scan buffer
1002 // for use in the prompt command
1003 if( scanning )
1004 scanbuffer += c;
1005
1006 // add to debug window
1007 emit debugPutChar(c);
1008
1009 checkBuffers();
1010}
1011
1012
1013void ConnectWidget::checkBuffers() {
1014 // Let's check if we are finished with scanning:
1015 // The scanstring have to be in the buffer and the latest character
1016 // was a carriage return or an linefeed (depending on modem setup)
1017 if( scanning && scanbuffer.contains(scanstr) &&
1018 ( scanbuffer.right(1) == "\n" || scanbuffer.right(1) == "\r") ) {
1019 scanning = false;
1020
1021 int vstart = scanbuffer.indexOf( scanstr ) + scanstr.length();
1022 scanvar = scanbuffer.mid( vstart, scanbuffer.length() - vstart);
1023 scanvar = scanvar.trimmed();
1024
1025 // Show the Variabel content in the debug window
1026 QString sv = i18n("Scan Var: %1", scanvar);
1027 emit debugMessage(sv);
1028 }
1029
1030 if(expecting) {
1031 if(readbuffer.contains(expectstr)) {
1032 expecting = false;
1033 // keep everything after the expected string
1034 readbuffer.remove(0, readbuffer.indexOf(expectstr) + expectstr.length());
1035
1036 QString ts = i18n("Found: %1", expectstr);
1037 emit debugMessage(ts);
1038
1039 if (loopend) {
1040 loopend=false;
1041 }
1042 }
1043
1044 if (loopend && readbuffer.contains(loopstr[loopnest])) {
1045 expecting = false;
1046 readbuffer = "";
1047 QString ts = i18n("Looping: %1", loopstr[loopnest]);
1048 emit debugMessage(ts);
1049 scriptindex = loopstartindex[loopnest];
1050 loopend = false;
1051 loopnest++;
1052 }
1053 // notify event loop if expected string was found
1054 if(!expecting)
1055 timerEvent((QTimerEvent *) 0);
1056 }
1057}
1058
1059
1060
1061void ConnectWidget::pause() {
1062 pausing = false;
1063 pausetimer->stop();
1064}
1065
1066
1067void ConnectWidget::cancelbutton() {
1068 gpppdata.setWaitCallback(false);
1069 Modem::modem->stop();
1070 killTimer(main_timer_ID);
1071 timeout_timer->stop();
1072 if_timer->stop();
1073 if_timeout_timer->stop();
1074
1075 if (termwindow) {
1076 delete termwindow;
1077 termwindow = 0L;
1078 show();
1079 }
1080
1081 messg->setText(i18n("One moment please..."));
1082
1083 // just to be sure
1084 Requester::rq->removeSecret(AUTH_PAP);
1085 Requester::rq->removeSecret(AUTH_CHAP);
1086 removedns();
1087
1088 qApp->processEvents();
1089
1090 Requester::rq->killPPPDaemon();
1091 Modem::modem->hangup();
1092
1093 hide();
1094 messg->setText("");
1095 p_kppp->quit_b->setFocus();
1096 p_kppp->show();
1097 emit stopAccounting(); // just to be sure
1098 p_kppp->con_win->stopClock();
1099 Modem::modem->closetty();
1100 Modem::modem->unlockdevice();
1101
1102 //abort prompt window...
1103 if (prompt->isVisible()) {
1104 prompt->hide();
1105 }
1106 prompt->setConsumed();
1107
1108 if(p_kppp->quitOnDisconnect())
1109 kapp->exit(0);
1110}
1111
1112
1113void ConnectWidget::script_timed_out() {
1114 if(vmain == 20) { // we are in the 'wait for the user to cancel' state
1115 timeout_timer->stop();
1116 emit stopAccounting();
1117 p_kppp->con_win->stopClock();
1118 return;
1119 }
1120
1121 if (prompt->isVisible())
1122 prompt->hide();
1123
1124 prompt->setConsumed();
1125 messg->setText(i18n("Script timed out."));
1126 Modem::modem->hangup();
1127 emit stopAccounting();
1128 p_kppp->con_win->stopClock();
1129
1130 vmain = 0; // let's try again.
1131 substate = -1;
1132}
1133
1134
1135void ConnectWidget::setScan(const QString &n) {
1136 scanning = true;
1137 scanstr = n;
1138 scanbuffer = "";
1139
1140 QString ts = i18n("Scanning: %1", n);
1141 emit debugMessage(ts);
1142}
1143
1144
1145void ConnectWidget::setExpect(const QString &n) {
1146 expecting = true;
1147 expectstr = n;
1148
1149 QString ts = i18n("Expecting: %1", n);
1150 ts.replace(QRegExp("\n"), "<LF>");
1151 emit debugMessage(ts);
1152
1153 // check if the expected string is in the read buffer already.
1154 checkBuffers();
1155}
1156
1157
1158void ConnectWidget::if_waiting_timed_out() {
1159 if_timer->stop();
1160 if_timeout_timer->stop();
1161 kDebug(5002) << "if_waiting_timed_out()";
1162
1163 gpppdata.setpppdError(E_IF_TIMEOUT);
1164
1165 // let's kill the stuck pppd
1166 Requester::rq->killPPPDaemon();
1167
1168 emit stopAccounting();
1169 p_kppp->con_win->stopClock();
1170
1171
1172 // killing ppp will generate a SIGCHLD which will be caught in pppdie()
1173 // in main.cpp what happens next will depend on the boolean
1174 // reconnect_on_disconnect which is set in ConnectWidget::init();
1175}
1176
1177void ConnectWidget::pppdDied()
1178{
1179 if_timer->stop();
1180 if_timeout_timer->stop();
1181}
1182
1183void ConnectWidget::if_waiting_slot() {
1184 messg->setText(i18n("Logging on to network..."));
1185
1186 if(!stats->ifIsUp()) {
1187
1188 if(gpppdata.pppdError() != 0) {
1189 // we are here if pppd died immediately after starting it.
1190 pppdDied();
1191 // error message handled in main.cpp: sigPPPDDied()
1192 return;
1193 }
1194
1195 if_timer->setSingleShot(true);
1196 if_timer->start(100);
1197 return;
1198 }
1199
1200 // O.K the ppp interface is up and running
1201 // give it a few time to come up completely (0.2 seconds)
1202 if_timeout_timer->stop();
1203 if_timer->stop();
1204 usleep(200000);
1205
1206 if(gpppdata.autoDNS())
1207 addpeerdns();
1208
1209 // Close the debugging window. If we are connected, we
1210 // are not really interested in debug output
1211 emit closeDebugWindow();
1212 p_kppp->statdlg->take_stats(); // start taking ppp statistics
1213 auto_hostname();
1214
1215 // signal other applications that we are connected now
1216 emit connected();
1217
1218 if(!gpppdata.command_on_connect().isEmpty()) {
1219 messg->setText(i18n("Running startup command..."));
1220
1221 // make sure that we don't get any async errors
1222 kapp->flush();
1223 execute_command(gpppdata.command_on_connect());
1224 messg->setText(i18n("Done"));
1225 }
1226
1227 // remove the authentication file
1228 Requester::rq->removeSecret(AUTH_PAP);
1229 Requester::rq->removeSecret(AUTH_CHAP);
1230
1231 emit debugMessage(i18n("Done"));
1232 set_con_speed_string();
1233
1234 p_kppp->con_win->setConnectionSpeed(p_kppp->con_speed);
1235 hide();
1236 messg->setText("");
1237
1238 // prepare the con_win so as to have the right size for
1239 // accounting / non-accounting mode
1240 if(p_kppp->acct != 0)
1241 p_kppp->con_win->accounting(p_kppp->acct->running());
1242 else
1243 p_kppp->con_win->accounting(false);
1244
1245 if (gpppdata.get_dock_into_panel()) {
1246 DockWidget::dock_widget->show();
1247 DockWidget::dock_widget->take_stats();
1248 hide();
1249 }
1250 else {
1251 p_kppp->con_win->show();
1252
1253 if(gpppdata.get_iconify_on_connect()) {
1254 p_kppp->con_win->showMinimized();
1255 }
1256 }
1257
1258 Modem::modem->closetty();
1259}
1260
1261
1262bool ConnectWidget::execppp() {
1263 QString command;
1264
1265 command = "pppd";
1266
1267 // as of version 2.3.6 pppd falls back to the real user rights when
1268 // opening a device given in a command line. To avoid permission conflicts
1269 // we'll simply leave this argument away. pppd will then use the default tty
1270 // which is the serial port we connected stdin/stdout to in opener.cpp.
1271 // command += " ";
1272 // command += gpppdata.modemDevice();
1273
1274 command += ' ' + gpppdata.speed();
1275
1276 command += " -detach";
1277
1278 if(gpppdata.ipaddr() != "0.0.0.0" ||
1279 gpppdata.gateway() != "0.0.0.0") {
1280 if(gpppdata.ipaddr() != "0.0.0.0") {
1281 command += ' ';
1282 command += gpppdata.ipaddr();
1283 command += ':';
1284 }
1285 else {
1286 command += ' ';
1287 command += ':';
1288 }
1289
1290 if(gpppdata.gateway() != "0.0.0.0")
1291 command += gpppdata.gateway();
1292 }
1293
1294 if(gpppdata.subnetmask() != "0.0.0.0")
1295 command += " netmask " + gpppdata.subnetmask();
1296
1297 // the english/i18n mix below is ugly but we want to keep working
1298 // after someone changed the code to use i18n'ed config values
1299 QString flowCtrl = gpppdata.flowcontrol();
1300 if(flowCtrl != "None" && flowCtrl != i18n("None")) {
1301 if(flowCtrl == "CRTSCTS" || flowCtrl == "Hardware [CRTSCTS]" ||
1302 flowCtrl == i18n("Hardware [CRTSCTS]"))
1303 command += " crtscts";
1304 else
1305 command += " xonxoff";
1306 }
1307
1308 if(gpppdata.defaultroute())
1309 command += " defaultroute";
1310
1311 if(gpppdata.autoDNS())
1312 command += " usepeerdns";
1313
1314 QStringList &arglist = gpppdata.pppdArgument();
1315 for ( QStringList::Iterator it = arglist.begin();
1316 it != arglist.end();
1317 ++it )
1318 {
1319 command += ' ' + *it;
1320 }
1321
1322 // Callback settings
1323 if(gpppdata.callbackType() && !gpppdata.waitCallback()) {
1324 if(!gpppdata.pppdVersionMin(2, 4, 2)) {
1325 command += " +callback";
1326 if(gpppdata.callbackType() == CBTYPE_USER)
1327 command += " callback " + gpppdata.callbackPhone();
1328 } else {
1329 command += " callback ";
1330 command += gpppdata.callbackType() == CBTYPE_ADMIN ?
1331 QString("0") : gpppdata.callbackPhone();
1332 }
1333 } else
1334 gpppdata.setWaitCallback(false);
1335
1336 // PAP settings
1337 if(gpppdata.authMethod() == AUTH_PAP) {
1338 command += " -chap user ";
1339 command = command + "\"" + gpppdata.storedUsername() + "\"";
1340 }
1341
1342 // CHAP settings
1343 if(gpppdata.authMethod() == AUTH_CHAP) {
1344 command += " -pap user ";
1345 command = command + "\"" + gpppdata.storedUsername() + "\"";
1346 }
1347
1348 // PAP/CHAP settings
1349 if(gpppdata.authMethod() == AUTH_PAPCHAP) {
1350 command += " user ";
1351 command = command + "\"" + gpppdata.storedUsername() + "\"";
1352 }
1353
1354 // check for debug
1355 if(gpppdata.getPPPDebug())
1356 command += " debug";
1357
1358 if (command.length() > MAX_CMDLEN) {
1359 KMessageBox::error(this, i18n(
1360 "pppd command + command-line arguments exceed "
1361 "2024 characters in length."
1362 ));
1363
1364 return false; // nonsensically long command which would bust my buffer buf.
1365 }
1366
1367 kapp->flush();
1368
1369 return Requester::rq->execPPPDaemon(command);
1370}
1371
1372
1373void ConnectWidget::closeEvent( QCloseEvent *e ) {
1374 e->ignore();
1375 emit cancelbutton();
1376}
1377
1378
1379void ConnectWidget::setMsg(const QString &msg) {
1380 messg->setText(msg);
1381}
1382
1383void ConnectWidget::writeline(const QString &s) {
1384 Modem::modem->writeLine(s.toLocal8Bit());
1385}
1386
1387// Set the hostname and domain from DNS Server
1388void auto_hostname() {
1389 struct in_addr local_ip;
1390 struct hostent *hostname_entry;
1391 QString new_hostname;
1392 int dot;
1393 char tmp_str[100]; // buffer overflow safe
1394
1395 gethostname(tmp_str, sizeof(tmp_str));
1396 tmp_str[sizeof(tmp_str)-1]=0; // panic
1397 old_hostname=tmp_str; // copy to QString
1398
1399 if (!p_kppp->stats->local_ip_address.isEmpty() && gpppdata.autoname()) {
1400 local_ip.s_addr=inet_addr(p_kppp->stats->local_ip_address.toAscii());
1401 hostname_entry=gethostbyaddr((const char *)&local_ip,sizeof(in_addr),AF_INET);
1402
1403 if (hostname_entry != 0L) {
1404 new_hostname=hostname_entry->h_name;
1405 dot=new_hostname.indexOf('.');
1406 new_hostname=new_hostname.remove(dot,new_hostname.length()-dot);
1407 Requester::rq->setHostname(new_hostname);
1408 modified_hostname = true;
1409
1410 new_hostname=hostname_entry->h_name;
1411 new_hostname.remove(0,dot+1);
1412
1413 add_domain(new_hostname);
1414 }
1415 }
1416
1417}
1418
1419// Replace the DNS domain entry in the /etc/resolv.conf file and
1420// disable the nameserver entries if option is enabled
1421void add_domain(const QString &domain) {
1422
1423 int fd;
1424 char c;
1425 QString resolv[MAX_RESOLVCONF_LINES];
1426
1427 if (domain.isEmpty())
1428 return;
1429
1430 if((fd = Requester::rq->openResolv(O_RDONLY)) >= 0) {
1431
1432 int i=0;
1433 while((read(fd, &c, 1) == 1) && (i < MAX_RESOLVCONF_LINES)) {
1434 if(c == '\n') {
1435 i++;
1436 }
1437 else {
1438 resolv[i] += c;
1439 }
1440 }
1441 close(fd);
1442 if ((c != '\n') && (i < MAX_RESOLVCONF_LINES)) i++;
1443
1444 if((fd = Requester::rq->openResolv(O_WRONLY|O_TRUNC)) >= 0) {
1445 Q3CString tmp = "domain " + domain.toLocal8Bit() +
1446 " \t\t#kppp temp entry\n";
1447 write(fd, tmp.data(), tmp.length());
1448
1449 for(int j=0; j < i; j++) {
1450 if((resolv[j].contains("domain") ||
1451 ( resolv[j].contains("nameserver")
1452 && !resolv[j].contains("#kppp temp entry")
1453 && gpppdata.exDNSDisabled()))
1454 && !resolv[j].contains("#entry disabled by kppp")) {
1455 Q3CString tmp = "# " + resolv[j].toLocal8Bit() +
1456 " \t#entry disabled by kppp\n";
1457 write(fd, tmp, tmp.length());
1458 }
1459 else {
1460 Q3CString tmp = resolv[j].toLocal8Bit() + '\n';
1461 write(fd, tmp, tmp.length());
1462 }
1463 }
1464 }
1465 close(fd);
1466 }
1467}
1468
1469
1470// adds the DNS entries in the /etc/resolv.conf file
1471void adddns()
1472{
1473 int fd;
1474
1475 if ((fd = Requester::rq->openResolv(O_WRONLY|O_APPEND)) >= 0) {
1476 QStringList &dnslist = gpppdata.dns();
1477 for ( QStringList::Iterator it = dnslist.begin();
1478 it != dnslist.end();
1479 ++it )
1480 {
1481 Q3CString dns = "nameserver " + (*it).toLocal8Bit() +
1482 " \t#kppp temp entry\n";
1483 write(fd, dns.data(), dns.length());
1484 }
1485 close(fd);
1486 }
1487 add_domain(gpppdata.domain());
1488}
1489
1490void addpeerdns() {
1491 int fd, fd2;
1492
1493 if((fd = Requester::rq->openResolv(O_WRONLY|O_APPEND)) >= 0) {
1494 if((fd2 = open("/etc/ppp/resolv.conf", O_RDONLY)) >= 0) {
1495 char c;
1496 int i = 0;
1497 while(i++ < 100 && read(fd2, &c, 1) == 1) {
1498 if(c == '\n')
1499 write(fd, "\t#kppp temp entry\n", 18);
1500 else
1501 write(fd, &c, 1);
1502 }
1503 close(fd2);
1504 } else
1505 fprintf(stderr, "failed to read from /etc/ppp/resolv.conf\n");
1506 close(fd);
1507 }
1508 add_domain(gpppdata.domain());
1509}
1510
1511// remove the dns entries from the /etc/resolv.conf file
1512void removedns() {
1513
1514 int fd;
1515 char c;
1516 QString resolv[MAX_RESOLVCONF_LINES];
1517
1518 if((fd = Requester::rq->openResolv(O_RDONLY)) >= 0) {
1519
1520 int i=0;
1521 while(read(fd, &c, 1) == 1 && i < MAX_RESOLVCONF_LINES) {
1522 if(c == '\n') {
1523 i++;
1524 }
1525 else {
1526 resolv[i] += c;
1527 }
1528 }
1529 close(fd);
1530
1531 if((fd = Requester::rq->openResolv(O_WRONLY|O_TRUNC)) >= 0) {
1532 for(int j=0; j < i; j++) {
1533 if(resolv[j].contains("#kppp temp entry")) continue;
1534 if(resolv[j].contains("#entry disabled by kppp")) {
1535 Q3CString tmp = resolv[j].toLocal8Bit();
1536 write(fd, tmp.data()+2, tmp.length() - 27);
1537 write(fd, "\n", 1);
1538 }
1539 else {
1540 Q3CString tmp = resolv[j].toLocal8Bit() + '\n';
1541 write(fd, tmp, tmp.length());
1542 }
1543 }
1544 }
1545 close(fd);
1546
1547 }
1548
1549 if ( modified_hostname ) {
1550 Requester::rq->setHostname(old_hostname);
1551 modified_hostname = false;
1552 }
1553
1554}
1555
1556#include "connect.moc"
1557