summaryrefslogtreecommitdiff
path: root/core/launcher/transferserver.cpp
Unidiff
Diffstat (limited to 'core/launcher/transferserver.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--core/launcher/transferserver.cpp213
1 files changed, 156 insertions, 57 deletions
diff --git a/core/launcher/transferserver.cpp b/core/launcher/transferserver.cpp
index 7294f9c..a6dab07 100644
--- a/core/launcher/transferserver.cpp
+++ b/core/launcher/transferserver.cpp
@@ -1,120 +1,239 @@
1/********************************************************************** 1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved. 2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3** 3**
4** This file is part of Qtopia Environment. 4** This file is part of the Qtopia Environment.
5** 5**
6** This file may be distributed and/or modified under the terms of the 6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software 7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the 8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file. 9** packaging of this file.
10** 10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13** 13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information. 14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15** 15**
16** Contact info@trolltech.com if any conditions of this licensing are 16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you. 17** not clear to you.
18** 18**
19**********************************************************************/ 19**********************************************************************/
20#define _XOPEN_SOURCE 20#define _XOPEN_SOURCE
21#include <pwd.h> 21#include <pwd.h>
22#include <sys/types.h> 22#include <sys/types.h>
23#include <unistd.h> 23#include <unistd.h>
24#include <stdlib.h> 24#include <stdlib.h>
25#include <time.h>
26#include <shadow.h>
27
28extern "C" {
29#include <uuid/uuid.h>
30#define UUID_H_INCLUDED
31}
25 32
26#if defined(_OS_LINUX_) 33#if defined(_OS_LINUX_)
27#include <shadow.h> 34#include <shadow.h>
28#endif 35#endif
29 36
30#include <qdir.h> 37#include <qdir.h>
31#include <qfile.h> 38#include <qfile.h>
32#include <qtextstream.h> 39#include <qtextstream.h>
33#include <qdatastream.h> 40#include <qdatastream.h>
34#include <qmessagebox.h> 41#include <qmessagebox.h>
35#include <qstringlist.h> 42#include <qstringlist.h>
36#include <qfileinfo.h> 43#include <qfileinfo.h>
37#include <qregexp.h> 44#include <qregexp.h>
38//#include <qpe/qcopchannel_qws.h> 45//#include <qpe/qcopchannel_qws.h>
39#include <qpe/process.h> 46#include <qpe/process.h>
47#include <qpe/global.h>
40#include <qpe/config.h> 48#include <qpe/config.h>
49#include <qpe/contact.h>
50#include <qpe/quuid.h>
51#include <qpe/version.h>
52#ifdef QWS
41#include <qpe/qcopenvelope_qws.h> 53#include <qpe/qcopenvelope_qws.h>
54#endif
42 55
43#include "transferserver.h" 56#include "transferserver.h"
44#include "qprocess.h" 57#include "qprocess.h"
45 58
46const int block_size = 51200; 59const int block_size = 51200;
47 60
48TransferServer::TransferServer( Q_UINT16 port, QObject *parent, 61TransferServer::TransferServer( Q_UINT16 port, QObject *parent ,
49 const char* name ) 62 const char* name )
50 : QServerSocket( port, 1, parent, name ) 63 : QServerSocket( port, 1, parent, name )
51{ 64{
52 if ( !ok() ) 65 if ( !ok() )
53 qWarning( "Failed to bind to port %d", port ); 66 qWarning( "Failed to bind to port %d", port );
54} 67}
55 68
56TransferServer::~TransferServer() 69TransferServer::~TransferServer()
57{ 70{
58 71
59} 72}
60 73
61void TransferServer::newConnection( int socket ) 74void TransferServer::newConnection( int socket )
62{ 75{
63 (void) new ServerPI( socket, this ); 76 (void) new ServerPI( socket, this );
64} 77}
65 78
66bool accessAuthorized(QHostAddress peeraddress) 79QString SyncAuthentication::serverId()
67{ 80{
68 Config cfg("Security"); 81 Config cfg("Security");
69 cfg.setGroup("Sync"); 82 cfg.setGroup("Sync");
70 uint auth_peer = cfg.readNumEntry("auth_peer",0xc0a80100); 83 QString r=cfg.readEntry("serverid");
84 if ( r.isEmpty() ) {
85 uuid_t uuid;
86 uuid_generate( uuid );
87 cfg.writeEntry("serverid",(r = QUuid( uuid ).toString()));
88 }
89 return r;
90}
91
92QString SyncAuthentication::ownerName()
93{
94 QString vfilename = Global::applicationFileName("addressbook",
95 "businesscard.vcf");
96 if (QFile::exists(vfilename)) {
97 Contact c;
98 c = Contact::readVCard( vfilename )[0];
99 return c.fullName();
100 }
101
102 return "";
103}
104
105QString SyncAuthentication::loginName()
106{
107 struct passwd *pw;
108 pw = getpwuid( geteuid() );
109 return QString::fromLocal8Bit( pw->pw_name );
110}
111
112int SyncAuthentication::isAuthorized(QHostAddress peeraddress)
113{
114 Config cfg("Security");
115 cfg.setGroup("Sync");
116 QString allowedstr = cfg.readEntry("auth_peer","192.168.1.0");
117 QHostAddress allowed;
118 allowed.setAddress(allowedstr);
119 uint auth_peer = allowed.ip4Addr();
71 uint auth_peer_bits = cfg.readNumEntry("auth_peer_bits",24); 120 uint auth_peer_bits = cfg.readNumEntry("auth_peer_bits",24);
72 bool ok = (peeraddress.ip4Addr() & (((1<<auth_peer_bits)-1)<<(32-auth_peer_bits))) 121 uint mask = auth_peer_bits >= 32 // shifting by 32 is not defined
73 == auth_peer; 122 ? 0xffffffff : (((1<<auth_peer_bits)-1)<<(32-auth_peer_bits));
74 /* Allows denial-of-service attack. 123 return (peeraddress.ip4Addr() & mask) == auth_peer;
75 if ( !ok ) { 124}
76 QMessageBox::warning(0,tr("Security"), 125
77 tr("<p>An attempt to access this device from %1 has been denied.") 126bool SyncAuthentication::checkUser( const QString& user )
78 .arg(peeraddress.toString())); 127{
79 } 128 if ( user.isEmpty() ) return FALSE;
80 */ 129 QString euser = loginName();
81 return ok; 130 return user == euser;
82} 131}
83 132
84ServerPI::ServerPI( int socket, QObject *parent, const char* name ) 133bool SyncAuthentication::checkPassword( const QString& password )
134{
135#ifdef ALLOW_UNIX_USER_FTP
136 // First, check system password...
137
138 struct passwd *pw = 0;
139 struct spwd *spw = 0;
140
141 pw = getpwuid( geteuid() );
142 spw = getspnam( pw->pw_name );
143
144 QString cpwd = QString::fromLocal8Bit( pw->pw_passwd );
145 if ( cpwd == "x" && spw )
146 cpwd = QString::fromLocal8Bit( spw->sp_pwdp );
147
148 // Note: some systems use more than crypt for passwords.
149 QString cpassword = QString::fromLocal8Bit( crypt( password.local8Bit(), cpwd.local8Bit() ) );
150 if ( cpwd == cpassword )
151 return TRUE;
152#endif
153
154 static int lastdenial=0;
155 static int denials=0;
156 int now = time(0);
157
158 // Detect old Qtopia Desktop (no password)
159 if ( password.isEmpty() ) {
160 if ( denials < 1 || now > lastdenial+600 ) {
161 QMessageBox::warning( 0,tr("Sync Connection"),
162 tr("<p>An unauthorized system is requesting access to this device."
163 "<p>If you are using a version of Qtopia Desktop older than 1.5.1, "
164 "please upgrade."),
165 tr("Deny") );
166 denials++;
167 lastdenial=now;
168 }
169 return FALSE;
170 }
171
172 // Second, check sync password...
173 if ( password.left(6) == "Qtopia" ) {
174 QString cpassword = QString::fromLocal8Bit( crypt( password.mid(8).local8Bit(), "qp" ) );
175 Config cfg("Security");
176 cfg.setGroup("Sync");
177 QString pwds = cfg.readEntry("Passwords");
178 if ( QStringList::split(QChar(' '),pwds).contains(cpassword) )
179 return TRUE;
180
181 // Unrecognized system. Be careful...
182
183 if ( (denials > 2 && now < lastdenial+600)
184 || QMessageBox::warning(0,tr("Sync Connection"),
185 tr("<p>An unrecognized system is requesting access to this device."
186 "<p>If you have just initiated a Sync for the first time, this is normal."),
187 tr("Allow"),tr("Deny"))==1 )
188 {
189 denials++;
190 lastdenial=now;
191 return FALSE;
192 } else {
193 denials=0;
194 cfg.writeEntry("Passwords",pwds+" "+cpassword);
195 return TRUE;
196 }
197 }
198
199 return FALSE;
200}
201
202
203ServerPI::ServerPI( int socket, QObject *parent , const char* name )
85 : QSocket( parent, name ) , dtp( 0 ), serversocket( 0 ), waitsocket( 0 ) 204 : QSocket( parent, name ) , dtp( 0 ), serversocket( 0 ), waitsocket( 0 )
86{ 205{
87 state = Connected; 206 state = Connected;
88 207
89 setSocket( socket ); 208 setSocket( socket );
90 209
91 peerport = peerPort(); 210 peerport = peerPort();
92 peeraddress = peerAddress(); 211 peeraddress = peerAddress();
93 212
94#ifndef INSECURE 213#ifndef INSECURE
95 if ( !accessAuthorized(peeraddress) ) { 214 if ( !SyncAuthentication::isAuthorized(peeraddress) ) {
96 state = Forbidden; 215 state = Forbidden;
97 startTimer( 0 ); 216 startTimer( 0 );
98 } else 217 } else
99 #endif 218 #endif
100 { 219 {
101 connect( this, SIGNAL( readyRead() ), SLOT( read() ) ); 220 connect( this, SIGNAL( readyRead() ), SLOT( read() ) );
102 connect( this, SIGNAL( connectionClosed() ), SLOT( connectionClosed() ) ); 221 connect( this, SIGNAL( connectionClosed() ), SLOT( connectionClosed() ) );
103 222
104 passiv = FALSE; 223 passiv = FALSE;
105 for( int i = 0; i < 4; i++ ) 224 for( int i = 0; i < 4; i++ )
106 wait[i] = FALSE; 225 wait[i] = FALSE;
107 226
108 send( "220 Qtopia transfer service ready!" ); 227 send( "220 Qtopia " QPE_VERSION " FTP Server" );
109 state = Wait_USER; 228 state = Wait_USER;
110 229
111 dtp = new ServerDTP( this ); 230 dtp = new ServerDTP( this );
112 connect( dtp, SIGNAL( completed() ), SLOT( dtpCompleted() ) ); 231 connect( dtp, SIGNAL( completed() ), SLOT( dtpCompleted() ) );
113 connect( dtp, SIGNAL( failed() ), SLOT( dtpFailed() ) ); 232 connect( dtp, SIGNAL( failed() ), SLOT( dtpFailed() ) );
114 connect( dtp, SIGNAL( error( int ) ), SLOT( dtpError( int ) ) ); 233 connect( dtp, SIGNAL( error( int ) ), SLOT( dtpError( int ) ) );
115 234
116 235
117 directory = QDir::currentDirPath(); 236 directory = QDir::currentDirPath();
118 237
119 static int p = 1024; 238 static int p = 1024;
120 239
@@ -142,55 +261,24 @@ void ServerPI::send( const QString& msg )
142{ 261{
143 QTextStream os( this ); 262 QTextStream os( this );
144 os << msg << endl; 263 os << msg << endl;
145 //qDebug( "Reply: %s", msg.latin1() ); 264 //qDebug( "Reply: %s", msg.latin1() );
146} 265}
147 266
148void ServerPI::read() 267void ServerPI::read()
149{ 268{
150 while ( canReadLine() ) 269 while ( canReadLine() )
151 process( readLine().stripWhiteSpace() ); 270 process( readLine().stripWhiteSpace() );
152} 271}
153 272
154bool ServerPI::checkUser( const QString& user )
155{
156 if ( user.isEmpty() ) return FALSE;
157
158 struct passwd *pw;
159 pw = getpwuid( geteuid() );
160 QString euser = QString::fromLocal8Bit( pw->pw_name );
161 return user == euser;
162}
163
164bool ServerPI::checkPassword( const QString& /* password */ )
165{
166 // ### HACK for testing on local host
167 return true;
168
169 /*
170 struct passwd *pw = 0;
171 struct spwd *spw = 0;
172
173 pw = getpwuid( geteuid() );
174 spw = getspnam( pw->pw_name );
175
176 QString cpwd = QString::fromLocal8Bit( pw->pw_passwd );
177 if ( cpwd == "x" && spw )
178 cpwd = QString::fromLocal8Bit( spw->sp_pwdp );
179
180 QString cpassword = QString::fromLocal8Bit( crypt( password.local8Bit(), cpwd.local8Bit() ) );
181 return cpwd == cpassword;
182*/
183}
184
185bool ServerPI::checkReadFile( const QString& file ) 273bool ServerPI::checkReadFile( const QString& file )
186{ 274{
187 QString filename; 275 QString filename;
188 276
189 if ( file[0] != "/" ) 277 if ( file[0] != "/" )
190 filename = directory.path() + "/" + file; 278 filename = directory.path() + "/" + file;
191 else 279 else
192 filename = file; 280 filename = file;
193 281
194 QFileInfo fi( filename ); 282 QFileInfo fi( filename );
195 return ( fi.exists() && fi.isReadable() ); 283 return ( fi.exists() && fi.isReadable() );
196} 284}
@@ -245,38 +333,37 @@ void ServerPI::process( const QString& message )
245 send( "211 Good bye!" ); 333 send( "211 Good bye!" );
246 delete this; 334 delete this;
247 return; 335 return;
248 } 336 }
249 337
250 // connected to client 338 // connected to client
251 if ( Connected == state ) 339 if ( Connected == state )
252 return; 340 return;
253 341
254 // waiting for user name 342 // waiting for user name
255 if ( Wait_USER == state ) { 343 if ( Wait_USER == state ) {
256 344
257 if ( cmd != "USER" || msg.count() < 2 || !checkUser( arg ) ) { 345 if ( cmd != "USER" || msg.count() < 2 || !SyncAuthentication::checkUser( arg ) ) {
258 send( "530 Please login with USER and PASS" ); 346 send( "530 Please login with USER and PASS" );
259 return; 347 return;
260 } 348 }
261 send( "331 User name ok, need password" ); 349 send( "331 User name ok, need password" );
262 state = Wait_PASS; 350 state = Wait_PASS;
263 return; 351 return;
264 } 352 }
265 353
266 // waiting for password 354 // waiting for password
267 if ( Wait_PASS == state ) { 355 if ( Wait_PASS == state ) {
268 356
269 if ( cmd != "PASS" || !checkPassword( arg ) ) { 357 if ( cmd != "PASS" || !SyncAuthentication::checkPassword( arg ) ) {
270 //if ( cmd != "PASS" || msg.count() < 2 || !checkPassword( arg ) ) {
271 send( "530 Please login with USER and PASS" ); 358 send( "530 Please login with USER and PASS" );
272 return; 359 return;
273 } 360 }
274 send( "230 User logged in, proceed" ); 361 send( "230 User logged in, proceed" );
275 state = Ready; 362 state = Ready;
276 return; 363 return;
277 } 364 }
278 365
279 // ACCESS CONTROL COMMANDS 366 // ACCESS CONTROL COMMANDS
280 367
281 368
282 // account (ACCT) 369 // account (ACCT)
@@ -445,28 +532,31 @@ void ServerPI::process( const QString& message )
445 if ( dtp->dtpMode() != ServerDTP::Idle ) 532 if ( dtp->dtpMode() != ServerDTP::Idle )
446 send( "426 Connection closed; transfer aborted" ); 533 send( "426 Connection closed; transfer aborted" );
447 else 534 else
448 send( "226 Closing data connection" ); 535 send( "226 Closing data connection" );
449 } 536 }
450 537
451 // delete (DELE) 538 // delete (DELE)
452 else if ( cmd == "DELE" ) { 539 else if ( cmd == "DELE" ) {
453 if ( args.isEmpty() ) 540 if ( args.isEmpty() )
454 send( "500 Syntax error, command unrecognized" ); 541 send( "500 Syntax error, command unrecognized" );
455 else { 542 else {
456 QFile file( absFilePath( args ) ) ; 543 QFile file( absFilePath( args ) ) ;
457 if ( file.remove() ) 544 if ( file.remove() ) {
458 send( "250 Requested file action okay, completed" ); 545 send( "250 Requested file action okay, completed" );
459 else 546 QCopEnvelope e("QPE/System", "linkChanged(QString)" );
547 e << file.name();
548 } else {
460 send( "550 Requested action not taken" ); 549 send( "550 Requested action not taken" );
550 }
461 } 551 }
462 } 552 }
463 553
464 // remove directory (RMD) 554 // remove directory (RMD)
465 else if ( cmd == "RMD" ) { 555 else if ( cmd == "RMD" ) {
466 if ( args.isEmpty() ) 556 if ( args.isEmpty() )
467 send( "500 Syntax error, command unrecognized" ); 557 send( "500 Syntax error, command unrecognized" );
468 else { 558 else {
469 QDir dir; 559 QDir dir;
470 if ( dir.rmdir( absFilePath( args ), TRUE ) ) 560 if ( dir.rmdir( absFilePath( args ), TRUE ) )
471 send( "250 Requested file action okay, completed" ); 561 send( "250 Requested file action okay, completed" );
472 else 562 else
@@ -625,27 +715,34 @@ bool ServerPI::parsePort( const QString& pp )
625 QStringList p = QStringList::split( ",", pp ); 715 QStringList p = QStringList::split( ",", pp );
626 if ( p.count() != 6 ) return FALSE; 716 if ( p.count() != 6 ) return FALSE;
627 717
628 // h1,h2,h3,h4,p1,p2 718 // h1,h2,h3,h4,p1,p2
629 peeraddress = QHostAddress( ( p[0].toInt() << 24 ) + ( p[1].toInt() << 16 ) + 719 peeraddress = QHostAddress( ( p[0].toInt() << 24 ) + ( p[1].toInt() << 16 ) +
630 ( p[2].toInt() << 8 ) + p[3].toInt() ); 720 ( p[2].toInt() << 8 ) + p[3].toInt() );
631 peerport = ( p[4].toInt() << 8 ) + p[5].toInt(); 721 peerport = ( p[4].toInt() << 8 ) + p[5].toInt();
632 return TRUE; 722 return TRUE;
633} 723}
634 724
635void ServerPI::dtpCompleted() 725void ServerPI::dtpCompleted()
636{ 726{
637 dtp->close();
638 waitsocket = 0;
639 send( "226 Closing data connection, file transfer successful" ); 727 send( "226 Closing data connection, file transfer successful" );
728 if ( dtp->dtpMode() == ServerDTP::RetrieveFile ) {
729 QString fn = dtp->fileName();
730 if ( fn.right(8)==".desktop" && fn.find("/Documents/")>=0 ) {
731 QCopEnvelope e("QPE/System", "linkChanged(QString)" );
732 e << fn;
733 }
734 }
735 waitsocket = 0;
736 dtp->close();
640} 737}
641 738
642void ServerPI::dtpFailed() 739void ServerPI::dtpFailed()
643{ 740{
644 dtp->close(); 741 dtp->close();
645 waitsocket = 0; 742 waitsocket = 0;
646 send( "451 Requested action aborted: local error in processing" ); 743 send( "451 Requested action aborted: local error in processing" );
647} 744}
648 745
649void ServerPI::dtpError( int ) 746void ServerPI::dtpError( int )
650{ 747{
651 dtp->close(); 748 dtp->close();
@@ -843,25 +940,25 @@ QString ServerPI::absFilePath( const QString& file )
843 filepath = directory.path() + "/" + file; 940 filepath = directory.path() + "/" + file;
844 941
845 return filepath; 942 return filepath;
846} 943}
847 944
848 945
849void ServerPI::timerEvent( QTimerEvent * ) 946void ServerPI::timerEvent( QTimerEvent * )
850{ 947{
851 connectionClosed(); 948 connectionClosed();
852} 949}
853 950
854 951
855ServerDTP::ServerDTP( QObject *parent, const char* name ) 952ServerDTP::ServerDTP( QObject *parent = 0, const char* name = 0)
856 : QSocket( parent, name ), mode( Idle ), createTargzProc( 0 ), 953 : QSocket( parent, name ), mode( Idle ), createTargzProc( 0 ),
857retrieveTargzProc( 0 ), gzipProc( 0 ) 954retrieveTargzProc( 0 ), gzipProc( 0 )
858{ 955{
859 956
860 connect( this, SIGNAL( connected() ), SLOT( connected() ) ); 957 connect( this, SIGNAL( connected() ), SLOT( connected() ) );
861 connect( this, SIGNAL( connectionClosed() ), SLOT( connectionClosed() ) ); 958 connect( this, SIGNAL( connectionClosed() ), SLOT( connectionClosed() ) );
862 connect( this, SIGNAL( bytesWritten( int ) ), SLOT( bytesWritten( int ) ) ); 959 connect( this, SIGNAL( bytesWritten( int ) ), SLOT( bytesWritten( int ) ) );
863 connect( this, SIGNAL( readyRead() ), SLOT( readyRead() ) ); 960 connect( this, SIGNAL( readyRead() ), SLOT( readyRead() ) );
864 961
865 gzipProc = new QProcess( this, "gzipProc" ); 962 gzipProc = new QProcess( this, "gzipProc" );
866 gzipProc->setCommunication( QProcess::Stdin | QProcess::Stdout ); 963 gzipProc->setCommunication( QProcess::Stdin | QProcess::Stdout );
867 964
@@ -882,26 +979,28 @@ retrieveTargzProc( 0 ), gzipProc( 0 )
882} 979}
883 980
884ServerDTP::~ServerDTP() 981ServerDTP::~ServerDTP()
885{ 982{
886 buf.close(); 983 buf.close();
887 file.close(); 984 file.close();
888 createTargzProc->kill(); 985 createTargzProc->kill();
889} 986}
890 987
891void ServerDTP::extractTarDone() 988void ServerDTP::extractTarDone()
892{ 989{
893 qDebug("extract done"); 990 qDebug("extract done");
991#ifndef QT_NO_COP
894 QCopEnvelope e( "QPE/Desktop", "restoreDone(QString)" ); 992 QCopEnvelope e( "QPE/Desktop", "restoreDone(QString)" );
895 e << file.name(); 993 e << file.name();
994#endif
896} 995}
897 996
898void ServerDTP::connected() 997void ServerDTP::connected()
899{ 998{
900 // send file mode 999 // send file mode
901 switch ( mode ) { 1000 switch ( mode ) {
902 case SendFile : 1001 case SendFile :
903 if ( !file.exists() || !file.open( IO_ReadOnly) ) { 1002 if ( !file.exists() || !file.open( IO_ReadOnly) ) {
904 emit failed(); 1003 emit failed();
905 mode = Idle; 1004 mode = Idle;
906 return; 1005 return;
907 } 1006 }