fixed some compiler-warnings
[opensuse:qinternet.git] / src / server.cpp
1
2 /***************************************************************************
3  *                                                                         *
4  *   Copyright: SuSE Linux AG, Nuernberg                                   *
5  *                                                                         *
6  *   Authors: Arvin Schnell <arvin@suse.de>                                *
7  *            Stefan Rauch <srauch@suse.de>                                *
8  *                                                                         *
9  ***************************************************************************/
10
11 /***************************************************************************
12  *                                                                         *
13  *   This program is free software; you can redistribute it and/or modify  *
14  *   it under the terms of the GNU General Public License as published by  *
15  *   the Free Software Foundation; either version 2 of the License, or     *
16  *   (at your option) any later version.                                   *
17  *                                                                         *
18  ***************************************************************************/
19
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <openssl/md5.h>
32
33 #include <qfile.h>
34 #include <qtimer.h>
35 #include <qregexp.h>
36 #include <Q3TextStream>
37
38 #include "main.h"
39 #include "server.h"
40 #include "route.h"
41 #include "utils.h"
42 #include "myslp.h"
43 #include "parse.h"
44 #include "config.h"
45 #include "debug.h"
46
47
48 #define FRONTEND_SOCKET "/var/run/smpppd/control"
49
50 #define FRONTEND_PORT 3185
51
52 #define FRONTEND_CONF "/etc/smpppd-c.conf"
53
54
55 SiteConfig::SiteConfig (site_t site)
56     : site (site),
57       host (""),
58       port (FRONTEND_PORT),
59       password ("")
60 {
61     if (site != LOCAL)
62         if (struct servent* se = getservbyname ("smpppd", "tcp"))
63             port = ntohs (se->s_port);
64 }
65
66
67 Server::Server (bool quiet, const SiteConfig& siteconfig, QObject* parent,
68                 const char* name)
69     : QObject (parent, name),
70       SiteConfig (siteconfig),
71       quiet (quiet),
72       status (BUILDUP),
73       once_connected (false),
74       smpppd_version (0)
75 {
76     debug(3, "");
77
78     connect_timer = NULL;
79
80     connect (&socket, SIGNAL (readyRead ()), this, SLOT (slot_read ()));
81     connect (&socket, SIGNAL (error (int)), this, SLOT (slot_error (int)));
82     connect (&socket, SIGNAL (connectionClosed ()), this, SLOT (slot_closed ()));
83
84     pingpong_timer = new QTimer (this);
85     connect (pingpong_timer, SIGNAL (timeout ()), this, SLOT (pingpong_slot ()));
86
87     QTimer::singleShot (0, this, SLOT (start_connect ()));
88 }
89
90
91 Server::~Server ()
92 {
93     debug(3, "");
94
95     if(connect_timer)
96         delete connect_timer;
97
98     if(pingpong_timer)
99         delete pingpong_timer;
100
101     if (socket.state () == Q3Socket::Connection) {
102         write_line ("quit");
103         socket.close ();
104     }
105 }
106
107
108 void
109 Server::set_status (status_t s)
110 {
111     debug(3, "status = %d");
112
113     if (s != status) {
114         status = s;
115         emit new_status (status);
116     }
117 }
118
119
120 void
121 Server::start_connect ()
122 {
123     debug_enter(3, "");
124
125     set_status (BUILDUP);
126
127     if (site == LOCAL)
128     {
129         debug (3, "try to connect %s\n", FRONTEND_SOCKET);
130
131         int sockfd = ::socket (PF_LOCAL, SOCK_STREAM, 0);
132         if (sockfd > 0)
133         {
134             struct sockaddr_un name;
135             name.sun_family = AF_LOCAL;
136             strncpy (name.sun_path, FRONTEND_SOCKET, sizeof (name.sun_path));
137             size_t size = SUN_LEN (&name);
138
139             if (::connect (sockfd, (struct sockaddr*) &name, size) == 0) {
140                 socket.setSocket (sockfd);
141                 return;
142             }
143         }
144
145         set_status (NOTHING);
146     }
147     else
148     {
149         debug(3, "try to connect %s, %d\n", host.ascii (), port);
150
151         socket.connectToHost (host, port);
152     }
153
154     connect_timer = new QTimer;
155     connect (connect_timer, SIGNAL (timeout ()), this, SLOT (slot_connecttimeout ()));
156     connect_timer->setSingleShot(true);
157     connect_timer->start(30000); // 30 seconds
158 }
159
160
161 void
162 Server::close ()
163 {
164     debug(3, "");
165
166     pingpong_timer->stop ();
167
168     socket.close ();
169     set_status (FAILURE);
170 }
171
172
173 QString
174 Server::make_response (const QString& chex) const
175 {
176     int size = chex.length ();
177     if (size & 1)
178         return "error";
179     size >>= 1;
180
181     // convert challenge from hex to bin
182     QString cbin;
183     for (int i = 0; i < size; i++) {
184         QString tmp = chex.mid (2 * i, 2);
185         cbin.append ((char) strtol (tmp.ascii (), 0, 16));
186     }
187
188     // calculate response
189     unsigned char rbin[MD5_DIGEST_LENGTH];
190     MD5state_st md5;
191     MD5_Init (&md5);
192     MD5_Update (&md5, cbin.ascii (), size);
193     MD5_Update (&md5, (const char*) password, password.length ());
194     MD5_Final (rbin, &md5);
195
196     // convert response from bin to hex
197     QString rhex;
198     for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
199         char buffer[3];
200         snprintf (buffer, 3, "%02x", rbin[i]);
201         rhex.append (buffer);
202     }
203
204     return rhex;
205 }
206
207
208 static QString
209 get_word (const QString& str, int num)
210 {
211     QStringList tmp = QStringList::split (" ", str);
212     return tmp.size () > (QStringList::size_type) num ? tmp[num] : "";
213 }
214
215
216 void
217 Server::slot_read ()
218 {
219     if(connect_timer) {
220         delete connect_timer;
221         connect_timer = NULL;
222     }
223
224     while (socket.canReadLine ())
225     {
226         QString line = socket.readLine ();
227
228         // strip trailing "\r\n"
229         line.truncate (line.length () - 2);
230
231         debug(3, "-> %s\n", line.ascii ());
232
233         if (line.startsWith ("challenge ="))
234         {
235             QString c = line.mid (11).stripWhiteSpace ();
236             QString r = make_response (c);
237             write_line ("response = %s", r.ascii ());
238             continue;
239         }
240
241         if (line == "error: failed to authenticated" || line.startsWith ("error: unprivileged"))
242         {
243             if (!quiet)
244                 emit error (i18n ("Authentication with the server failed."));
245             set_status (NOTHING); // set to nothing so the auto search continues
246             continue;
247         }
248
249         if (line.startsWith ("SuSE Meta pppd (smpppd)"))
250         {
251             QRegExp rx ("Version (\\d+)\\.(\\d+)");
252             smpppd_version = rx.search (line) > 0 ? 100 * rx.cap (1).toInt () +
253                 rx.cap (2).toInt () : 0;
254
255             if (smpppd_version >= 100)
256             {
257 #ifndef NOKDE
258                 write_line ("frontend %s %s", "kinternet", VERSION);
259 #else
260                 write_line ("frontend %s %s", "qinternet", VERSION);
261 #endif
262             }
263
264             if (smpppd_version >= 75)
265             {
266                 int tmp = myconfig->readNum ("Server", "ping-pong-time", 60);
267                 if (tmp < 1)
268                     tmp = 1;
269                 pingpong_count = 0;
270                 pingpong_timer->start (1000 * tmp);
271             }
272
273             once_connected = true;
274             set_status (SUCCESS);
275             continue;
276         }
277
278         if (line == "pong")
279         {
280             buffer.clear ();
281             pingpong_count = 0;
282             continue;
283         }
284
285         if (smpppd_version >= 100)
286         {
287             // QString::ascii seems to be 8 bit save
288             line = QString::fromUtf8 (line.ascii ());
289
290             if (line == "ok") {
291                 buffer.clear ();
292                 continue;
293             }
294
295             if (line.startsWith ("error:")) {
296                 buffer.clear ();
297                 emit error (i18n ("Server returned an error: ")+line.mid(7));
298                 continue;
299             }
300         }
301
302         if (line.startsWith ("BEGIN"))
303             buffer.clear ();
304
305         buffer.append (line);
306
307         if (line.startsWith ("END"))
308         {
309             // check, what kind of message we have
310             msg_t msg = NEW_UNKNOWN;
311             QString tmp = buffer.first ();
312
313             if (smpppd_version >= 100)
314             {
315                 if (tmp.startsWith ("BEGIN MAIN CONFIG")) {
316                     msg = NEW_MAINCONFIG;
317                     buffer_ifcfg = "";
318                 } else if (tmp.startsWith ("BEGIN MAIN LOG")) {
319                     msg = NEW_MAINLOG0;
320                     buffer_ifcfg = "";
321                 } else if (tmp.startsWith ("BEGIN APPEND MAIN LOG")) {
322                     msg = NEW_MAINLOG1;
323                     buffer_ifcfg = "";
324                 } else if (tmp.startsWith ("BEGIN IFCFGS")) {
325                     msg = NEW_IFCFGS;
326                     buffer_ifcfg = "";
327                 } else if (tmp.startsWith ("BEGIN PROVIDERS")) {
328                     msg = NEW_PROVIDERS;
329                     buffer_ifcfg = get_word (tmp, 2);
330                 } else if (tmp.startsWith ("BEGIN MAIN STATUS")) {
331                     msg = NEW_MAIN_STATUS;
332                     buffer_ifcfg = "";
333                 } else if (tmp.startsWith ("BEGIN STATUS")) {
334                     msg = NEW_STATUS;
335                     buffer_ifcfg = get_word (tmp, 2);
336                 } else if (tmp.startsWith ("BEGIN CONFIG")) {
337                     msg = NEW_CONFIG;
338                     buffer_ifcfg = get_word (tmp, 2);
339                 } else if (tmp.startsWith ("BEGIN LOG")) {
340                     msg = NEW_LOG0;
341                     buffer_ifcfg = get_word (tmp, 2);
342                 } else if (tmp.startsWith ("BEGIN APPEND LOG")) {
343                     msg = NEW_LOG1;
344                     buffer_ifcfg = get_word (tmp, 3);
345                 } else if (tmp.startsWith ("BEGIN RXTX BYTES")) {
346                     msg = NEW_RXTXBYTES;
347                     buffer_ifcfg = get_word (tmp, 3);
348                 } else if (tmp.startsWith ("BEGIN SCAN RESULTS")) {
349                    msg = NEW_SCAN_RESULTS;
350                    buffer_ifcfg = get_word (tmp, 3);
351                 } else if (tmp.startsWith ("BEGIN CONNECTION STATUS")) {
352                    msg = IW_CONNECTION_STATUS;
353                    buffer_ifcfg = get_word (tmp, 3);
354                 }
355
356                 if(get_debug_level() > 3) {
357                     QStringList tmp2 = QStringList::split (" ", tmp);
358                     int msg_len = tmp2[tmp2.size () - 1].toInt ();
359                     if (buffer.size () - 2 != msg_len)
360                     {
361                         fprintf (stderr, "warning: malformed block\n");
362                         for (QStringList::ConstIterator iter = buffer.constBegin ();
363                                 iter != buffer.constEnd (); ++iter)
364                             fprintf (stderr, "<%s>\n", (const char*) (*iter));
365                     }
366                 }
367             }
368             else
369             {
370                 if (tmp.startsWith ("BEGIN LIST PROVIDERS"))
371                     msg = NEW_PROVIDERS;
372                 else if (tmp.startsWith ("BEGIN LIST INTERFACES"))
373                     msg = NEW_IFCFGS;
374                 else if (tmp.startsWith ("BEGIN STATUS"))
375                     msg = NEW_STATUS;
376                 else if (tmp.startsWith ("BEGIN LIST LOG"))
377                     msg = NEW_LOG0;
378                 else if (tmp.startsWith ("BEGIN APPEND LOG"))
379                     msg = NEW_LOG1;
380                 else if (tmp.startsWith ("BEGIN RXTX BYTES"))
381                     msg = NEW_RXTXBYTES;
382                 else if (tmp.startsWith ("BEGIN CONFIG"))
383                     msg = NEW_CONFIG;
384             }
385
386             // remove the BEGIN/END tags
387             buffer.remove (buffer.begin ());
388             buffer.remove (buffer.fromLast ());
389
390             // send the signal that data can be delivered
391             if (msg != NEW_UNKNOWN)
392                 emit new_data (msg);
393         }
394     }
395 }
396
397
398 void
399 Server::slot_closed ()
400 {
401     debug(3, "");
402
403     pingpong_timer->stop ();
404
405     set_status (FAILURE);
406
407     if (!quiet && once_connected)
408         emit error (i18n ("Connection closed by server."));
409 }
410
411
412 void
413 Server::slot_error (int err)
414 {
415     debug(3, "");
416
417     if(connect_timer) {
418         delete connect_timer;
419         connect_timer = NULL;
420     }
421
422     pingpong_timer->stop ();
423
424     if (err == Q3Socket::ErrSocketRead)
425     {
426         set_status (FAILURE);
427
428         if (!quiet && once_connected)
429             emit error (i18n ("Connection to server lost."));
430     }
431     else
432     {
433         set_status (NOTHING);
434     }
435 }
436
437 void
438 Server::slot_connecttimeout ()
439 {
440     debug(3, "");
441     delete connect_timer;
442     connect_timer = NULL;
443     socket.clearPendingData();
444     socket.close();
445     set_status (NOTHING);
446 }
447
448 void
449 Server::write_line (const char* format, ...)
450 {
451     if (socket.state () != Q3Socket::Connection)
452         return;
453
454     char* result;
455
456     va_list ap;
457     va_start (ap, format);
458     vasprintf (&result, format, ap);
459     va_end (ap);
460
461     debug(3, "<- %s\n", result);
462
463     QString text = result;
464     text += "\r\n";
465
466     free (result);
467
468     if (socket.writeBlock (text, text.length ()) < 0) {
469         debug(3, "writeBlock failed: %s\n", strerror (errno));
470     }
471 }
472
473
474 void
475 Server::pingpong_slot ()
476 {
477     debug(3, "");
478
479     if (pingpong_count > 1) {
480         close ();
481         if (!quiet && once_connected)
482             emit error (i18n ("Connection to server lost."));
483     } else {
484         write_line ("ping");
485         pingpong_count++;
486     }
487 }
488
489
490 QStringList
491 Server::get_buffer () const
492 {
493     return buffer;
494 }
495
496
497 QString
498 Server::get_buffer_ifcfg () const
499 {
500     return buffer_ifcfg;
501 }
502
503
504 AutoServer::AutoServer (bool quiet)
505     : quiet (quiet),
506       status (BUILDUP),
507       once_connected (false),
508       configfile (SiteConfig::CONFIGFILE),
509       server (0)
510 {
511     debug(3, "");
512
513     QTimer::singleShot (0, this, SLOT (start_connect ()));
514 }
515
516
517 AutoServer::~AutoServer ()
518 {
519     debug(3, "");
520
521     if (server)
522         delete server;
523 }
524
525
526 bool
527 AutoServer::read_config_file ()
528 {
529     QFile fin (FRONTEND_CONF);
530     if (!fin.open (QIODevice::ReadOnly))
531         return false;
532
533     Q3TextStream sin (&fin);
534     sin.setEncoding (Q3TextStream::UnicodeUTF8);
535
536     QString line, key;
537     QStringList values;
538     while (!sin.atEnd ())
539     {
540         line = sin.readLine ();
541
542         if (parse_conf_keyvalues (line, &key, &values) != 1)
543             continue;
544
545         if (key == "sites")
546         {
547             sites.clear ();
548             for (QStringList::ConstIterator iter = values.constBegin ();
549                  iter != values.constEnd (); ++iter)
550             {
551                 if (*iter == "local")
552                     sites.append (SiteConfig::LOCAL);
553                 else if (*iter == "gateway")
554                     sites.append (SiteConfig::GATEWAY);
555                 else if (*iter == "slp")
556                     sites.append (SiteConfig::SLP);
557                 else if (*iter == "config-file")
558                     sites.append (SiteConfig::CONFIGFILE);
559             }
560             continue;
561         }
562
563         if (key == "server" && values.size () == 1) {
564             configfile.host = values[0];
565             continue;
566         }
567
568         if (key == "port" && values.size () == 1) {
569             configfile.port = atoi (values[0].ascii ());
570             continue;
571         }
572
573         if (key == "password" && values.size () == 0) {
574             configfile.password = "";
575             continue;
576         }
577
578         if (key == "password" && values.size () == 1) {
579             configfile.password = values[0].utf8 ();
580             continue;
581         }
582     }
583
584     fin.close ();
585
586     return true;
587 }
588
589
590 bool
591 AutoServer::fill_siteconfig (SiteConfig* siteconfig)
592 {
593     switch (siteconfig->site)
594     {
595         case SiteConfig::LOCAL: {
596
597             debug(3, "fill_config (LOCAL)\n");
598
599             return true;
600
601         } break;
602
603         case SiteConfig::CONFIGFILE: {
604
605             debug(3, "fill_config (CONFIGFILE)\n");
606
607             siteconfig->host = configfile.host;
608             siteconfig->port = configfile.port;
609             siteconfig->password = configfile.password;
610
611             return !siteconfig->host.isEmpty ();
612
613         } break;
614
615         case SiteConfig::USERCONFIG: {
616
617             debug(3, "fill_config (USERCONFIG)\n");
618
619             siteconfig->host = myconfig->readString ("Server", "server", "");
620             siteconfig->port = myconfig->readNum ("Server", "port", FRONTEND_PORT);
621             siteconfig->password = myconfig->readString ("Server", "password", "").utf8 ();
622
623             return !siteconfig->host.isEmpty ();
624
625         } break;
626
627         case SiteConfig::GATEWAY: {
628
629             debug(3, "fill_config (GATEWAY)\n");
630
631             struct rtentry def_rt;
632             if (!get_defaultroute (&def_rt))
633                 return false;
634
635             u_int32_t ip = ntohl (SIN_ADDR (def_rt.rt_gateway));
636             siteconfig->host.sprintf ("%d.%d.%d.%d", (ip >> 24) & 0xff,
637                                       (ip >> 16) & 0xff, (ip >> 8) & 0xff,
638                                       ip & 0xff);
639
640             return true;
641
642         } break;
643
644         case SiteConfig::SLP: {
645
646             debug(3, "fill_config (SLP)\n");
647
648             if (!MySLP::find (&siteconfig->host, &siteconfig->port))
649                 return false;
650
651             return true;
652
653         } break;
654     }
655
656     return false;
657 }
658
659
660 void
661 AutoServer::start_connect ()
662 {
663     debug(3, "");
664
665     set_status (BUILDUP);
666
667     sites.clear ();
668
669     const QString location = myconfig->readString ("Server", "location",
670                                                    "automatic");
671
672     if (location == "local")
673     {
674         sites.append (SiteConfig::LOCAL);
675     }
676     else if (location == "manual")
677     {
678         sites.append (SiteConfig::USERCONFIG);
679     }
680     else
681     {
682         sites.append (SiteConfig::LOCAL);
683         sites.append (SiteConfig::GATEWAY);
684         read_config_file ();
685     }
686
687     site = sites.constBegin ();
688
689     next_connect ();
690 }
691
692
693 void
694 AutoServer::next_connect ()
695 {
696     debug(3, "");
697
698     while (site != sites.constEnd ())
699     {
700         SiteConfig tmp (*site);
701         if (!fill_siteconfig (&tmp)) {
702             site++;
703             continue;
704         }
705
706         if (server)
707             server->deleteLater ();
708
709         server = new Server (quiet, tmp);
710
711         connect (server, SIGNAL (new_status (Server::status_t)),
712                  this, SLOT (new_server_status (Server::status_t)));
713
714         return;
715     }
716
717     if (site == sites.constEnd ())
718     {
719         if (!quiet)
720         {
721             bool tl = false, tr = false;
722
723             for (sites_t::ConstIterator iter = sites.constBegin ();
724                  iter != sites.constEnd (); ++iter)
725             {
726                 if (*iter == SiteConfig::LOCAL)
727                     tl = true;
728                 else
729                     tr = true;
730             }
731
732             if (tl && !tr)
733                 emit error( i18n (
734                         "Could not connect to local server. Maybe\n"
735                         "smpppd is not running or you do not\n"
736                         "have sufficient privileges.\n"
737                         "Also check the server settings in\n"
738                         "the dialog \"Various Settings\"."));
739             else if (!tl && tr)
740                 emit error( i18n (
741                         "Connection to remote server refused.\n"
742                         "Maybe smpppd is not running.\n"
743                         "Also check the server settings in\n"
744                         "the dialog \"Various Settings\"."));
745
746             else
747                 emit error( i18n (
748                         "Connection to local and remote server\n"
749                         "refused. Maybe smpppd is not running\n"
750                         "or you do not have sufficient privileges.\n"
751                         "Also check the server settings in\n"
752                         "the dialog \"Various Settings\"."));
753
754         }
755
756         set_status (NOTHING);
757     }
758 }
759
760
761 bool
762 AutoServer::can_start_connect () const
763 {
764     switch (status)
765     {
766         case BUILDUP:
767             return false;
768
769         case NOTHING:
770         case FAILURE:
771             return true;
772
773         case SUCCESS:
774             switch (server->get_status ())
775             {
776                 case BUILDUP:
777                     return false;
778
779                 case Server::NOTHING:
780                 case Server::FAILURE:
781                     return true;
782
783                 case Server::SUCCESS:
784                     return false;
785             }
786     }
787
788     return false;
789 }
790
791
792 void
793 AutoServer::new_server_status (Server::status_t s)
794 {
795     debug(3, "");
796
797     switch (s)
798     {
799         case Server::BUILDUP:
800             break;
801
802         case Server::NOTHING:
803
804             disconnect (server, SIGNAL (new_status (Server::status_t)),
805                         this, SLOT (new_server_status (Server::status_t)));
806
807             server->deleteLater ();
808             server = 0;
809
810             site++;
811             next_connect ();
812
813             break;
814
815         case Server::FAILURE:
816
817             disconnect (server, SIGNAL (new_status (Server::status_t)),
818                         this, SLOT (new_server_status (Server::status_t)));
819
820             server->deleteLater ();
821             server = 0;
822
823             set_status (FAILURE);
824
825             break;
826
827         case Server::SUCCESS:
828
829             disconnect (server, SIGNAL (new_status (Server::status_t)),
830                         this, SLOT (new_server_status (Server::status_t)));
831
832             once_connected = true;
833
834             set_status (SUCCESS);
835
836             break;
837     }
838 }
839
840
841 void
842 AutoServer::set_status (status_t s)
843 {
844     debug(3, "status = %d\n", s);
845
846     if (s != status) {
847         status = s;
848         emit new_status (status);
849     }
850 }
851
852
853 #include "server.moc"