00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "transferserver.h"
00021
00022
00023 #include <opie2/odebug.h>
00024 #include <opie2/oglobal.h>
00025 #include <qtopia/qprocess.h>
00026 #include <qtopia/process.h>
00027 #include <qtopia/private/contact.h>
00028 #include <qtopia/version.h>
00029 #ifdef Q_WS_QWS
00030 #include <qtopia/qcopenvelope_qws.h>
00031 #endif
00032 using namespace Opie::Core;
00033
00034
00035 #include <qtextstream.h>
00036 #include <qmessagebox.h>
00037
00038
00039 #include <pwd.h>
00040 #include <sys/types.h>
00041 #include <unistd.h>
00042 #include <fcntl.h>
00043 #include <stdlib.h>
00044 #include <time.h>
00045
00046 #ifndef Q_OS_MACX
00047 #include <shadow.h>
00048 #include <crypt.h>
00049 #endif
00050
00051 const int block_size = 51200;
00052
00053 TransferServer::TransferServer( Q_UINT16 port, QObject *parent,
00054 const char* name)
00055 : QServerSocket( port, 1, parent, name )
00056 {
00057 connections.setAutoDelete( TRUE );
00058 if ( !ok() )
00059 owarn << "Failed to bind to port " << port << "" << oendl;
00060 else
00061 ::fcntl( socket(), F_SETFD, FD_CLOEXEC );
00062 }
00063
00064 void TransferServer::authorizeConnections()
00065 {
00066 QListIterator<ServerPI> it(connections);
00067 while ( it.current() ) {
00068 if ( !it.current()->verifyAuthorised() ) {
00069 disconnect( it.current(), SIGNAL(connectionClosed(ServerPI*)), this, SLOT( closed(ServerPI*)) );
00070 connections.removeRef( it.current() );
00071 } else
00072 ++it;
00073 }
00074 }
00075
00076 void TransferServer::closed(ServerPI *item)
00077 {
00078 connections.removeRef(item);
00079 }
00080
00081 TransferServer::~TransferServer()
00082 {
00083 }
00084
00085 void TransferServer::newConnection( int socket )
00086 {
00087 ServerPI *ptr = new ServerPI( socket, this );
00088 connect( ptr, SIGNAL(connectionClosed(ServerPI*)), this, SLOT( closed(ServerPI*)) );
00089 connections.append( ptr );
00090 }
00091
00092 QString SyncAuthentication::serverId()
00093 {
00094 Config cfg("Security");
00095 cfg.setGroup("Sync");
00096 QString r = cfg.readEntry("serverid");
00097
00098 if ( r.isEmpty() ) {
00099 r = OGlobal::generateUuid();
00100 cfg.writeEntry("serverid", r );
00101 }
00102 return r;
00103 }
00104
00105 QString SyncAuthentication::ownerName()
00106 {
00107 QString vfilename = Global::applicationFileName("addressbook",
00108 "businesscard.vcf");
00109 if (QFile::exists(vfilename)) {
00110 Contact c;
00111 c = Contact::readVCard( vfilename )[0];
00112 return c.fullName();
00113 }
00114
00115 return QString::null;
00116 }
00117
00118 QString SyncAuthentication::loginName()
00119 {
00120 struct passwd *pw = 0L;
00121 #ifndef Q_OS_WIN32
00122 pw = getpwuid( geteuid() );
00123 return QString::fromLocal8Bit( pw->pw_name );
00124 #else
00125
00126 return QString();
00127 #endif
00128 }
00129
00130 int SyncAuthentication::isAuthorized(QHostAddress peeraddress)
00131 {
00132 Config cfg("Security");
00133 cfg.setGroup("Sync");
00134
00135 uint auth_peer = cfg.readNumEntry("auth_peer", 0xc0a80100);
00136
00137
00138
00139
00140 uint auth_peer_bits = cfg.readNumEntry("auth_peer_bits", 24);
00141 uint mask = auth_peer_bits >= 32
00142 ? 0xffffffff : (((1 << auth_peer_bits) - 1) << (32 - auth_peer_bits));
00143
00144 return (peeraddress.ip4Addr() & mask) == auth_peer;
00145 }
00146
00147 bool SyncAuthentication::checkUser( const QString& user )
00148 {
00149 if ( user.isEmpty() ) return FALSE;
00150 QString euser = loginName();
00151 return user == euser;
00152 }
00153
00154 bool SyncAuthentication::checkPassword( const QString& password )
00155 {
00156 #ifdef ALLOW_UNIX_USER_FTP
00157
00158
00159 struct passwd *pw = 0;
00160 struct spwd *spw = 0;
00161
00162 pw = getpwuid( geteuid() );
00163 spw = getspnam( pw->pw_name );
00164
00165 QString cpwd = QString::fromLocal8Bit( pw->pw_passwd );
00166 if ( cpwd == "x" && spw )
00167 cpwd = QString::fromLocal8Bit( spw->sp_pwdp );
00168
00169
00170 QString cpassword = QString::fromLocal8Bit( crypt( password.local8Bit(), cpwd.local8Bit() ) );
00171 if ( cpwd == cpassword )
00172 return TRUE;
00173 #endif
00174
00175 static int lastdenial=0;
00176 static int denials=0;
00177 int now = time(0);
00178
00179 Config cfg("Security");
00180 cfg.setGroup("SyncMode");
00181 int mode = cfg.readNumEntry("Mode", 0x02 );
00182
00183
00184 if (mode & 0x04) {
00185 QMessageBox unauth(
00186 tr("Sync Connection"),
00187 tr("<qt><p>An unauthorized system is requesting access to this device."
00188 "<p>You chose IntelliSync so you may I allow or deny this connection.</qt>" ),
00189 QMessageBox::Warning,
00190 QMessageBox::Ok, QMessageBox::Cancel|QMessageBox::Default, QMessageBox::NoButton,
00191 0, QString::null, TRUE, WStyle_StaysOnTop);
00192 unauth.setButtonText(QMessageBox::Ok, tr("Allow" ) );
00193 unauth.setButtonText(QMessageBox::Cancel, tr("Deny"));
00194 switch( unauth.exec() ) {
00195 case QMessageBox::Ok:
00196 return TRUE;
00197 break;
00198 case QMessageBox::Cancel:
00199 default:
00200 denials++;
00201 lastdenial=now;
00202 return FALSE;
00203 }
00204 }
00205
00206
00207 if ( password.isEmpty() ) {
00208 if ( denials < 3 || now > lastdenial+600 ) {
00209 QMessageBox unauth(
00210 tr("Sync Connection"),
00211 tr("<p>An unauthorized system is requesting access to this device."
00212 "<p>If you are using a version of Qtopia Desktop older than 1.5.1, "
00213 "please upgrade or change the security setting to use IntelliSync." ),
00214 QMessageBox::Warning,
00215 QMessageBox::Cancel, QMessageBox::NoButton, QMessageBox::NoButton,
00216 0, QString::null, TRUE, WStyle_StaysOnTop);
00217 unauth.setButtonText(QMessageBox::Cancel, tr("Deny"));
00218 unauth.exec();
00219
00220 denials++;
00221 lastdenial=now;
00222 }
00223 return FALSE;
00224
00225 }
00226
00227
00228
00229 static int lock=0;
00230 if ( lock ) return FALSE;
00231
00232 ++lock;
00233
00234
00235
00236
00237 if ( password.left(6) == "Qtopia" || password.left(6) == "rootme" ) {
00238 cfg.setGroup("Sync");
00239 QStringList pwds = cfg.readListEntry("Passwords",' ');
00240 for (QStringList::ConstIterator it=pwds.begin(); it!=pwds.end(); ++it) {
00241 #ifndef Q_OS_WIN32
00242 QString cpassword = QString::fromLocal8Bit(
00243 crypt( password.mid(8).local8Bit(), (*it).left(2).latin1() ) );
00244 #else
00245
00246 QString cpassword("");
00247 #endif
00248 if ( *it == cpassword ) {
00249 lock--;
00250 return TRUE;
00251 }
00252 }
00253
00254
00255 QMessageBox unrecbox(
00256 tr("Sync Connection"),
00257 tr( "<p>An unrecognized system is requesting access to this device."
00258 "<p>If you have just initiated a Sync for the first time, this is normal."),
00259 QMessageBox::Warning,
00260 QMessageBox::Cancel, QMessageBox::Yes, QMessageBox::NoButton,
00261 0, QString::null, TRUE, WStyle_StaysOnTop);
00262 unrecbox.setButtonText(QMessageBox::Cancel, tr("Deny"));
00263 unrecbox.setButtonText(QMessageBox::Yes, tr("Allow"));
00264
00265 if ( (denials > 2 && now < lastdenial+600)
00266 || unrecbox.exec() != QMessageBox::Yes)
00267 {
00268 denials++;
00269 lastdenial=now;
00270 lock--;
00271 return FALSE;
00272 } else {
00273 const char salty[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/.";
00274 char salt[2];
00275 salt[0]= salty[rand() % (sizeof(salty)-1)];
00276 salt[1]= salty[rand() % (sizeof(salty)-1)];
00277 #ifndef Q_OS_WIN32
00278 QString cpassword = QString::fromLocal8Bit(
00279 crypt( password.mid(8).local8Bit(), salt ) );
00280 #else
00281
00282 QString cpassword("");
00283 #endif
00284 denials=0;
00285 pwds.prepend(cpassword);
00286 cfg.writeEntry("Passwords",pwds,' ');
00287 lock--;
00288 return TRUE;
00289 }
00290 }
00291 lock--;
00292
00293 return FALSE;
00294 }
00295
00296
00297 ServerPI::ServerPI( int socket, QObject *parent, const char* name )
00298 : QSocket( parent, name ) , dtp( 0 ), serversocket( 0 ), waitsocket( 0 ),
00299 storFileSize(-1)
00300 {
00301 state = Connected;
00302
00303 setSocket( socket );
00304
00305 peerport = peerPort();
00306 peeraddress = peerAddress();
00307
00308 #ifndef INSECURE
00309 if ( !SyncAuthentication::isAuthorized(peeraddress) ) {
00310 state = Forbidden;
00311 startTimer( 0 );
00312 } else
00313 #endif
00314 {
00315 connect( this, SIGNAL( readyRead() ), SLOT( read() ) );
00316 connect( this, SIGNAL( connectionClosed() ), SLOT( connectionClosed() ) );
00317
00318 passiv = FALSE;
00319 for( int i = 0; i < 4; i++ )
00320 wait[i] = FALSE;
00321
00322 send( "220 Qtopia " QPE_VERSION " FTP Server" );
00323 state = Wait_USER;
00324
00325 dtp = new ServerDTP( this );
00326 connect( dtp, SIGNAL( completed() ), SLOT( dtpCompleted() ) );
00327 connect( dtp, SIGNAL( failed() ), SLOT( dtpFailed() ) );
00328 connect( dtp, SIGNAL( error(int) ), SLOT( dtpError(int) ) );
00329
00330
00331 directory = QDir::currentDirPath();
00332
00333 static int p = 1024;
00334
00335 while ( !serversocket || !serversocket->ok() ) {
00336 delete serversocket;
00337 serversocket = new ServerSocket( ++p, this );
00338 }
00339 connect( serversocket, SIGNAL( newIncomming(int) ),
00340 SLOT( newConnection(int) ) );
00341 }
00342 }
00343
00344 ServerPI::~ServerPI()
00345 {
00346 close();
00347
00348 if ( dtp )
00349 dtp->close();
00350 delete dtp;
00351 delete serversocket;
00352 }
00353
00354 bool ServerPI::verifyAuthorised()
00355 {
00356 if ( !SyncAuthentication::isAuthorized(peerAddress()) ) {
00357 state = Forbidden;
00358 return FALSE;
00359 }
00360 return TRUE;
00361 }
00362
00363 void ServerPI::connectionClosed()
00364 {
00365
00366 emit connectionClosed(this);
00367 }
00368
00369 void ServerPI::send( const QString& msg )
00370 {
00371 QTextStream os( this );
00372 os << msg << endl;
00373
00374 }
00375
00376 void ServerPI::read()
00377 {
00378 while ( canReadLine() )
00379 process( readLine().stripWhiteSpace() );
00380 }
00381
00382 bool ServerPI::checkReadFile( const QString& file )
00383 {
00384 QString filename;
00385
00386 if ( file.length() == 1 && file[0] == "/" )
00387 filename = file;
00388 else if ( file[0] != "/" )
00389 filename = directory.path() + "/" + file;
00390 else
00391 filename = file;
00392
00393 QFileInfo fi( filename );
00394 return ( fi.exists() && fi.isReadable() );
00395 }
00396
00397 bool ServerPI::checkWriteFile( const QString& file )
00398 {
00399 QString filename;
00400
00401 if ( file[0] != "/" )
00402 filename = directory.path() + "/" + file;
00403 else
00404 filename = file;
00405
00406 QFileInfo fi( filename );
00407
00408 if ( fi.exists() )
00409 if ( !QFile( filename ).remove() )
00410 return FALSE;
00411 return TRUE;
00412 }
00413
00414 void ServerPI::process( const QString& message )
00415 {
00416
00417
00418
00419 QStringList msg = QStringList::split( " ", message );
00420 if ( msg.isEmpty() ) return;
00421
00422
00423 QString cmd = msg[0].upper();
00424
00425
00426 QString arg;
00427 if ( msg.count() >= 2 )
00428 arg = msg[1];
00429
00430
00431 QString args;
00432 if ( msg.count() >= 2 ) {
00433 QStringList copy( msg );
00434
00435
00436 copy.remove( copy.begin() );
00437 args = copy.join( " " );
00438 }
00439
00440
00441
00442
00443 if ( cmd == "QUIT" ) {
00444 send( "211 Good bye!" );
00445 close();
00446 return;
00447 }
00448
00449
00450 if ( Connected == state )
00451 return;
00452
00453
00454 if ( Wait_USER == state ) {
00455
00456 if ( cmd != "USER" || msg.count() < 2 || !SyncAuthentication::checkUser( arg ) ) {
00457 send( "530 Please login with USER and PASS" );
00458 return;
00459 }
00460 send( "331 User name ok, need password" );
00461 state = Wait_PASS;
00462 return;
00463 }
00464
00465
00466 if ( Wait_PASS == state ) {
00467
00468 if ( cmd != "PASS" || !SyncAuthentication::checkPassword( arg ) ) {
00469 send( "530 Please login with USER and PASS" );
00470 return;
00471 }
00472 send( "230 User logged in, proceed" );
00473 state = Ready;
00474 return;
00475 }
00476
00477
00478
00479
00480 if ( cmd != "STOR" )
00481 storFileSize = -1;
00482
00483
00484 if ( cmd == "ACCT" ) {
00485
00486 send( "502 Command not implemented" );
00487 }
00488
00489
00490 else if ( cmd == "CWD" ) {
00491
00492 if ( !args.isEmpty() ) {
00493 if ( directory.cd( args, TRUE ) )
00494 send( "250 Requested file action okay, completed" );
00495 else
00496 send( "550 Requested action not taken" );
00497 }
00498 else
00499 send( "500 Syntax error, command unrecognized" );
00500 }
00501
00502
00503 else if ( cmd == "CDUP" ) {
00504 if ( directory.cdUp() )
00505 send( "250 Requested file action okay, completed" );
00506 else
00507 send( "550 Requested action not taken" );
00508 }
00509
00510
00511 else if ( cmd == "SMNT" ) {
00512
00513 send( "502 Command not implemented" );
00514 }
00515
00516
00517 else if ( cmd == "REIN" ) {
00518
00519 send( "502 Command not implemented" );
00520 }
00521
00522
00523
00524
00525
00526
00527 else if ( cmd == "PORT" ) {
00528 if ( parsePort( arg ) )
00529 send( "200 Command okay" );
00530 else
00531 send( "500 Syntax error, command unrecognized" );
00532 }
00533
00534
00535 else if ( cmd == "PASV" ) {
00536 passiv = TRUE;
00537 send( "227 Entering Passive Mode ("
00538 + address().toString().replace( QRegExp( "\\." ), "," ) + ","
00539 + QString::number( ( serversocket->port() ) >> 8 ) + ","
00540 + QString::number( ( serversocket->port() ) & 0xFF ) +")" );
00541 }
00542
00543
00544 else if ( cmd == "TYPE" ) {
00545 if ( arg.upper() == "A" || arg.upper() == "I" )
00546 send( "200 Command okay" );
00547 else
00548 send( "504 Command not implemented for that parameter" );
00549 }
00550
00551
00552 else if ( cmd == "STRU" ) {
00553 if ( arg.upper() == "F" )
00554 send( "200 Command okay" );
00555 else
00556 send( "504 Command not implemented for that parameter" );
00557 }
00558
00559
00560 else if ( cmd == "MODE" ) {
00561 if ( arg.upper() == "S" )
00562 send( "200 Command okay" );
00563 else
00564 send( "504 Command not implemented for that parameter" );
00565 }
00566
00567
00568
00569
00570
00571
00572 else if ( cmd == "RETR" )
00573 if ( !args.isEmpty() && checkReadFile( absFilePath( args ) )
00574 || backupRestoreGzip( absFilePath( args ) ) ) {
00575 send( "150 File status okay" );
00576 sendFile( absFilePath( args ) );
00577 }
00578 else {
00579 odebug << "550 Requested action not taken" << oendl;
00580 send( "550 Requested action not taken" );
00581 }
00582
00583
00584 else if ( cmd == "STOR" )
00585 if ( !args.isEmpty() && checkWriteFile( absFilePath( args ) ) ) {
00586 send( "150 File status okay" );
00587 retrieveFile( absFilePath( args ) );
00588 }
00589 else
00590 send( "550 Requested action not taken" );
00591
00592
00593 else if ( cmd == "STOU" ) {
00594 send( "502 Command not implemented" );
00595 }
00596
00597
00598 else if ( cmd == "APPE" ) {
00599 send( "502 Command not implemented" );
00600 }
00601
00602
00603 else if ( cmd == "ALLO" ) {
00604 storFileSize = args.toInt();
00605 send( "200 Command okay" );
00606 }
00607
00608
00609 else if ( cmd == "REST" ) {
00610 send( "502 Command not implemented" );
00611 }
00612
00613
00614 else if ( cmd == "RNFR" ) {
00615 renameFrom = QString::null;
00616 if ( args.isEmpty() )
00617 send( "500 Syntax error, command unrecognized" );
00618 else {
00619 QFile file( absFilePath( args ) );
00620 if ( file.exists() ) {
00621 send( "350 File exists, ready for destination name" );
00622 renameFrom = absFilePath( args );
00623 }
00624 else
00625 send( "550 Requested action not taken" );
00626 }
00627 }
00628
00629
00630 else if ( cmd == "RNTO" ) {
00631 if ( lastCommand != "RNFR" )
00632 send( "503 Bad sequence of commands" );
00633 else if ( args.isEmpty() )
00634 send( "500 Syntax error, command unrecognized" );
00635 else {
00636 QDir dir( absFilePath( args ) );
00637 if ( dir.rename( renameFrom, absFilePath( args ), TRUE ) )
00638 send( "250 Requested file action okay, completed." );
00639 else
00640 send( "550 Requested action not taken" );
00641 }
00642 }
00643
00644
00645 else if ( cmd.contains( "ABOR" ) ) {
00646 dtp->close();
00647 if ( dtp->dtpMode() != ServerDTP::Idle )
00648 send( "426 Connection closed; transfer aborted" );
00649 else
00650 send( "226 Closing data connection" );
00651 }
00652
00653
00654 else if ( cmd == "DELE" ) {
00655 if ( args.isEmpty() )
00656 send( "500 Syntax error, command unrecognized" );
00657 else {
00658 QFile file( absFilePath( args ) ) ;
00659 if ( file.remove() ) {
00660 send( "250 Requested file action okay, completed" );
00661 QCopEnvelope e("QPE/System", "linkChanged(QString)" );
00662 e << file.name();
00663 } else {
00664 send( "550 Requested action not taken" );
00665 }
00666 }
00667 }
00668
00669
00670 else if ( cmd == "RMD" ) {
00671 if ( args.isEmpty() )
00672 send( "500 Syntax error, command unrecognized" );
00673 else {
00674 QDir dir;
00675 if ( dir.rmdir( absFilePath( args ), TRUE ) )
00676 send( "250 Requested file action okay, completed" );
00677 else
00678 send( "550 Requested action not taken" );
00679 }
00680 }
00681
00682
00683 else if ( cmd == "MKD" ) {
00684 if ( args.isEmpty() ) {
00685 odebug << " Error: no arg" << oendl;
00686 send( "500 Syntax error, command unrecognized" );
00687 }
00688 else {
00689 QDir dir;
00690 if ( dir.mkdir( absFilePath( args ), TRUE ) )
00691 send( "250 Requested file action okay, completed." );
00692 else
00693 send( "550 Requested action not taken" );
00694 }
00695 }
00696
00697
00698 else if ( cmd == "PWD" ) {
00699 send( "257 \"" + directory.path() +"\"" );
00700 }
00701
00702
00703 else if ( cmd == "LIST" ) {
00704 if ( args == "-la" )
00705 args = QString::null;
00706
00707 if ( sendList( absFilePath( args ) ) )
00708 send( "150 File status okay" );
00709 else
00710 send( "500 Syntax error, command unrecognized" );
00711 }
00712
00713
00714 else if ( cmd == "SIZE" ) {
00715 QString filePath = absFilePath( args );
00716 QFileInfo fi( filePath );
00717 bool gzipfile = backupRestoreGzip( filePath );
00718 if ( !fi.exists() && !gzipfile )
00719 send( "500 Syntax error, command unrecognized" );
00720 else {
00721 if ( !gzipfile )
00722 send( "213 " + QString::number( fi.size() ) );
00723 else {
00724 Process duproc( QString("du") );
00725 duproc.addArgument("-s");
00726 QString in, out;
00727 if ( !duproc.exec(in, out) ) {
00728 odebug << "du process failed; just sending back 1K" << oendl;
00729 send( "213 1024");
00730 }
00731 else {
00732 QString size = out.left( out.find("\t") );
00733 int guess = size.toInt()/5;
00734 if ( filePath.contains("doc") )
00735 guess *= 1000;
00736 odebug << "sending back gzip guess of " << guess << "" << oendl;
00737 send( "213 " + QString::number(guess) );
00738 }
00739 }
00740 }
00741 }
00742
00743 else if ( cmd == "NLST" ) {
00744 send( "502 Command not implemented" );
00745 }
00746
00747
00748 else if ( cmd == "SITE" ) {
00749 send( "502 Command not implemented" );
00750 }
00751
00752
00753 else if ( cmd == "SYST" ) {
00754 send( "215 UNIX Type: L8" );
00755 }
00756
00757
00758 else if ( cmd == "STAT" ) {
00759 send( "502 Command not implemented" );
00760 }
00761
00762
00763 else if ( cmd == "HELP" ) {
00764 send( "502 Command not implemented" );
00765 }
00766
00767
00768 else if ( cmd == "NOOP" ) {
00769 send( "200 Command okay" );
00770 }
00771
00772
00773 else
00774 send( "502 Command not implemented" );
00775
00776 lastCommand = cmd;
00777 }
00778
00779 bool ServerPI::backupRestoreGzip( const QString &file )
00780 {
00781 return (file.find( "backup" ) != -1 &&
00782 file.findRev( ".tgz" ) == (int)file.length()-4 );
00783 }
00784
00785 bool ServerPI::backupRestoreGzip( const QString &file, QStringList &targets )
00786 {
00787 if ( file.find( "backup" ) != -1 &&
00788 file.findRev( ".tgz" ) == (int)file.length()-4 ) {
00789 QFileInfo info( file );
00790 targets = info.dirPath( TRUE );
00791 odebug << "ServerPI::backupRestoreGzip for " << file.latin1() << " = " << targets.join(" ").latin1() << oendl;
00792 return true;
00793 }
00794 return false;
00795 }
00796
00797 void ServerPI::sendFile( const QString& file )
00798 {
00799 if ( passiv ) {
00800 wait[SendFile] = TRUE;
00801 waitfile = file;
00802 if ( waitsocket )
00803 newConnection( waitsocket );
00804 }
00805 else {
00806 QStringList targets;
00807 if ( backupRestoreGzip( file, targets ) )
00808 dtp->sendGzipFile( file, targets, peeraddress, peerport );
00809 else dtp->sendFile( file, peeraddress, peerport );
00810 }
00811 }
00812
00813 void ServerPI::retrieveFile( const QString& file )
00814 {
00815 if ( passiv ) {
00816 wait[RetrieveFile] = TRUE;
00817 waitfile = file;
00818 if ( waitsocket )
00819 newConnection( waitsocket );
00820 }
00821 else {
00822 QStringList targets;
00823 if ( backupRestoreGzip( file, targets ) )
00824 dtp->retrieveGzipFile( file, peeraddress, peerport );
00825 else
00826 dtp->retrieveFile( file, peeraddress, peerport, storFileSize );
00827 }
00828 }
00829
00830 bool ServerPI::parsePort( const QString& pp )
00831 {
00832 QStringList p = QStringList::split( ",", pp );
00833 if ( p.count() != 6 ) return FALSE;
00834
00835
00836 peeraddress = QHostAddress( ( p[0].toInt() << 24 ) + ( p[1].toInt() << 16 ) +
00837 ( p[2].toInt() << 8 ) + p[3].toInt() );
00838 peerport = ( p[4].toInt() << 8 ) + p[5].toInt();
00839 return TRUE;
00840 }
00841
00842 void ServerPI::dtpCompleted()
00843 {
00844 send( "226 Closing data connection, file transfer successful" );
00845 if ( dtp->dtpMode() == ServerDTP::RetrieveFile ) {
00846 QString fn = dtp->fileName();
00847 if ( fn.right(8)==".desktop" && fn.find("/Documents/")>=0 ) {
00848 QCopEnvelope e("QPE/System", "linkChanged(QString)" );
00849 e << fn;
00850 }
00851 }
00852 waitsocket = 0;
00853 dtp->close();
00854 storFileSize = -1;
00855 }
00856
00857 void ServerPI::dtpFailed()
00858 {
00859 dtp->close();
00860 waitsocket = 0;
00861 send( "451 Requested action aborted: local error in processing" );
00862 storFileSize = -1;
00863 }
00864
00865 void ServerPI::dtpError( int )
00866 {
00867 dtp->close();
00868 waitsocket = 0;
00869 send( "451 Requested action aborted: local error in processing" );
00870 storFileSize = -1;
00871 }
00872
00873 bool ServerPI::sendList( const QString& arg )
00874 {
00875 QByteArray listing;
00876 QBuffer buffer( listing );
00877
00878 if ( !buffer.open( IO_WriteOnly ) )
00879 return FALSE;
00880
00881 QTextStream ts( &buffer );
00882 QString fn = arg;
00883
00884 if ( fn.isEmpty() )
00885 fn = directory.path();
00886
00887 QFileInfo fi( fn );
00888 if ( !fi.exists() ) return FALSE;
00889
00890
00891 if ( fi.isFile() ) {
00892 ts << fileListing( &fi ) << endl;
00893 }
00894
00895
00896 else if ( fi.isDir() ) {
00897 QDir dir( fn );
00898 const QFileInfoList *list = dir.entryInfoList( QDir::All | QDir::Hidden );
00899
00900 QFileInfoListIterator it( *list );
00901 QFileInfo *info;
00902
00903 unsigned long total = 0;
00904 while ( ( info = it.current() ) ) {
00905 if ( info->fileName() != "." && info->fileName() != ".." )
00906 total += info->size();
00907 ++it;
00908 }
00909
00910 ts << "total " << QString::number( total / 1024 ) << endl;
00911
00912 it.toFirst();
00913 while ( ( info = it.current() ) ) {
00914 if ( info->fileName() == "." || info->fileName() == ".." ) {
00915 ++it;
00916 continue;
00917 }
00918 ts << fileListing( info ) << endl;
00919 ++it;
00920 }
00921 }
00922
00923 if ( passiv ) {
00924 waitarray = buffer.buffer();
00925 wait[SendByteArray] = TRUE;
00926 if ( waitsocket )
00927 newConnection( waitsocket );
00928 }
00929 else
00930 dtp->sendByteArray( buffer.buffer(), peeraddress, peerport );
00931 return TRUE;
00932 }
00933
00934 QString ServerPI::fileListing( QFileInfo *info )
00935 {
00936 if ( !info ) return QString::null;
00937 QString s;
00938
00939
00940 if ( info->isDir() )
00941 s += "d";
00942 else if ( info->isSymLink() )
00943 s += "l";
00944 else
00945 s += "-";
00946
00947
00948 s += permissionString( info ) + " ";
00949
00950
00951 int subdirs = 1;
00952
00953 if ( info->isDir() )
00954 subdirs = 2;
00955
00956
00957
00958
00959 s += QString::number( subdirs ).rightJustify( 3, ' ', TRUE ) + " ";
00960
00961
00962 QString o = info->owner();
00963 if ( o.isEmpty() )
00964 o = QString::number(info->ownerId());
00965 s += o.leftJustify( 8, ' ', TRUE ) + " ";
00966
00967
00968 QString g = info->group();
00969 if ( g.isEmpty() )
00970 g = QString::number(info->groupId());
00971 s += g.leftJustify( 8, ' ', TRUE ) + " ";
00972
00973
00974 s += QString::number( info->size() ).rightJustify( 9, ' ', TRUE ) + " ";
00975
00976
00977 QDate date = info->lastModified().date();
00978 QTime time = info->lastModified().time();
00979 s += date.monthName( date.month() ) + " "
00980 + QString::number( date.day() ).rightJustify( 2, ' ', TRUE ) + " "
00981 + QString::number( time.hour() ).rightJustify( 2, '0', TRUE ) + ":"
00982 + QString::number( time.minute() ).rightJustify( 2,'0', TRUE ) + " ";
00983
00984
00985 s += info->fileName();
00986
00987 return s;
00988 }
00989
00990 QString ServerPI::permissionString( QFileInfo *info )
00991 {
00992 if ( !info ) return QString( "---------" );
00993 QString s;
00994
00995
00996 if ( info->permission( QFileInfo::ReadUser ) ) s += "r";
00997 else s += "-";
00998 if ( info->permission( QFileInfo::WriteUser ) ) s += "w";
00999 else s += "-";
01000 if ( info->permission( QFileInfo::ExeUser ) ) s += "x";
01001 else s += "-";
01002
01003
01004 if ( info->permission( QFileInfo::ReadGroup ) ) s += "r";
01005 else s += "-";
01006 if ( info->permission( QFileInfo::WriteGroup ) )s += "w";
01007 else s += "-";
01008 if ( info->permission( QFileInfo::ExeGroup ) ) s += "x";
01009 else s += "-";
01010
01011
01012 if ( info->permission( QFileInfo::ReadOther ) ) s += "r";
01013 else s += "-";
01014 if ( info->permission( QFileInfo::WriteOther ) ) s += "w";
01015 else s += "-";
01016 if ( info->permission( QFileInfo::ExeOther ) ) s += "x";
01017 else s += "-";
01018
01019 return s;
01020 }
01021
01022 void ServerPI::newConnection( int socket )
01023 {
01024
01025
01026 if ( !passiv ) return;
01027
01028 if ( wait[SendFile] ) {
01029 QStringList targets;
01030 if ( backupRestoreGzip( waitfile, targets ) )
01031 dtp->sendGzipFile( waitfile, targets );
01032 else
01033 dtp->sendFile( waitfile );
01034 dtp->setSocket( socket );
01035 }
01036 else if ( wait[RetrieveFile] ) {
01037 odebug << "check retrieve file" << oendl;
01038 if ( backupRestoreGzip( waitfile ) )
01039 dtp->retrieveGzipFile( waitfile );
01040 else
01041 dtp->retrieveFile( waitfile, storFileSize );
01042 dtp->setSocket( socket );
01043 }
01044 else if ( wait[SendByteArray] ) {
01045 dtp->sendByteArray( waitarray );
01046 dtp->setSocket( socket );
01047 }
01048 else if ( wait[RetrieveByteArray] ) {
01049 odebug << "retrieve byte array" << oendl;
01050 dtp->retrieveByteArray();
01051 dtp->setSocket( socket );
01052 }
01053 else
01054 waitsocket = socket;
01055
01056 for( int i = 0; i < 4; i++ )
01057 wait[i] = FALSE;
01058 }
01059
01060 QString ServerPI::absFilePath( const QString& file )
01061 {
01062 if ( file.isEmpty() ) return file;
01063
01064 QString filepath( file );
01065 if ( file[0] != "/" )
01066 filepath = directory.path() + "/" + file;
01067
01068 return filepath;
01069 }
01070
01071
01072 void ServerPI::timerEvent( QTimerEvent * )
01073 {
01074 connectionClosed();
01075 }
01076
01077
01078 ServerDTP::ServerDTP( QObject *parent, const char* name)
01079 : QSocket( parent, name ), mode( Idle ), createTargzProc( 0 ),
01080 retrieveTargzProc( 0 )
01081 {
01082
01083 connect( this, SIGNAL( connected() ), SLOT( connected() ) );
01084 connect( this, SIGNAL( connectionClosed() ), SLOT( connectionClosed() ) );
01085 connect( this, SIGNAL( bytesWritten(int) ), SLOT( bytesWritten(int) ) );
01086 connect( this, SIGNAL( readyRead() ), SLOT( readyRead() ) );
01087
01088 createTargzProc = new QProcess( QString("tar"), this, "createTargzProc");
01089 createTargzProc->setCommunication( QProcess::Stdout );
01090 createTargzProc->setWorkingDirectory( QDir::rootDirPath() );
01091 connect( createTargzProc, SIGNAL( processExited() ), SLOT( targzDone() ) );
01092
01093 retrieveTargzProc = new QProcess( this, "retrieveTargzProc" );
01094 retrieveTargzProc->setCommunication( QProcess::Stdin );
01095 retrieveTargzProc->setWorkingDirectory( QDir::rootDirPath() );
01096 connect( retrieveTargzProc, SIGNAL( processExited() ),
01097 SIGNAL( completed() ) );
01098 connect( retrieveTargzProc, SIGNAL( processExited() ),
01099 SLOT( extractTarDone() ) );
01100 }
01101
01102 ServerDTP::~ServerDTP()
01103 {
01104 buf.close();
01105 if ( RetrieveFile == mode && file.isOpen() ) {
01106
01107 file.close();
01108 if ( recvFileSize >= 0 && (int)file.size() != recvFileSize ) {
01109 odebug << "STOR incomplete" << oendl;
01110 file.remove();
01111 }
01112 } else {
01113 file.close();
01114 }
01115 createTargzProc->kill();
01116 }
01117
01118 void ServerDTP::extractTarDone()
01119 {
01120 odebug << "extract done" << oendl;
01121 #ifndef QT_NO_COP
01122 QCopEnvelope e( "QPE/System", "restoreDone(QString)" );
01123 e << file.name();
01124 #endif
01125 }
01126
01127 void ServerDTP::connected()
01128 {
01129
01130 switch ( mode ) {
01131 case SendFile :
01132 if ( !file.exists() || !file.open( IO_ReadOnly) ) {
01133 emit failed();
01134 mode = Idle;
01135 return;
01136 }
01137
01138
01139
01140 bytes_written = 0;
01141 if ( file.size() == 0 ) {
01142
01143 file.close();
01144 emit completed();
01145 mode = Idle;
01146 } else {
01147
01148 if ( bytesToWrite() <= block_size && !file.atEnd() ) {
01149 QCString s;
01150 s.resize( block_size );
01151 int bytes = file.readBlock( s.data(), block_size );
01152 writeBlock( s.data(), bytes );
01153 }
01154 }
01155 break;
01156 case SendGzipFile:
01157 if ( createTargzProc->isRunning() ) {
01158
01159 owarn << "Previous tar --gzip process is still running; killing it..." << oendl;
01160 createTargzProc->kill();
01161 }
01162
01163 bytes_written = 0;
01164 odebug << "==>start send tar process" << oendl;
01165 if ( !createTargzProc->start() )
01166 owarn << "Error starting " << createTargzProc->arguments().join(" ").latin1() << oendl;
01167 break;
01168 case SendBuffer:
01169 if ( !buf.open( IO_ReadOnly) ) {
01170 emit failed();
01171 mode = Idle;
01172 return;
01173 }
01174
01175
01176 bytes_written = 0;
01177 while( !buf.atEnd() )
01178 putch( buf.getch() );
01179 buf.close();
01180 break;
01181 case RetrieveFile:
01182
01183 if ( file.exists() && !file.remove() ) {
01184 emit failed();
01185 mode = Idle;
01186 return;
01187 }
01188
01189 if ( !file.open( IO_WriteOnly) ) {
01190 emit failed();
01191 mode = Idle;
01192 return;
01193 }
01194
01195 break;
01196 case RetrieveGzipFile:
01197 odebug << "=-> starting tar process to receive .tgz file" << oendl;
01198 break;
01199 case RetrieveBuffer:
01200
01201 if ( !buf.open( IO_WriteOnly) ) {
01202 emit failed();
01203 mode = Idle;
01204 return;
01205 }
01206
01207 break;
01208 case Idle:
01209 odebug << "connection established but mode set to Idle; BUG!" << oendl;
01210 break;
01211 }
01212 }
01213
01214 void ServerDTP::connectionClosed()
01215 {
01216
01217
01218
01219 if ( SendFile == mode ) {
01220 if ( bytes_written == file.size() )
01221 emit completed();
01222 else
01223 emit failed();
01224 }
01225
01226
01227 else if ( SendBuffer == mode ) {
01228 if ( bytes_written == buf.size() )
01229 emit completed();
01230 else
01231 emit failed();
01232 }
01233
01234
01235 else if ( RetrieveFile == mode ) {
01236 file.close();
01237 if ( recvFileSize >= 0 && (int)file.size() != recvFileSize ) {
01238 odebug << "STOR incomplete" << oendl;
01239 file.remove();
01240 emit failed();
01241 } else {
01242 emit completed();
01243 }
01244 }
01245
01246 else if ( RetrieveGzipFile == mode ) {
01247 odebug << "Done writing ungzip file; closing input" << oendl;
01248 retrieveTargzProc->flushStdin();
01249 retrieveTargzProc->closeStdin();
01250 }
01251
01252
01253 else if ( RetrieveBuffer == mode ) {
01254 buf.close();
01255 emit completed();
01256 }
01257
01258 mode = Idle;
01259 }
01260
01261 void ServerDTP::bytesWritten( int bytes )
01262 {
01263 bytes_written += bytes;
01264
01265
01266 if ( SendFile == mode ) {
01267
01268 if ( bytes_written == file.size() ) {
01269
01270 file.close();
01271 emit completed();
01272 mode = Idle;
01273 }
01274 else if( !file.atEnd() ) {
01275 QCString s;
01276 s.resize( block_size );
01277 int bytes = file.readBlock( s.data(), block_size );
01278 writeBlock( s.data(), bytes );
01279 }
01280 }
01281
01282
01283 if ( SendBuffer == mode ) {
01284
01285 if ( bytes_written == buf.size() ) {
01286
01287 emit completed();
01288 mode = Idle;
01289 }
01290 }
01291 }
01292
01293 void ServerDTP::readyRead()
01294 {
01295
01296 if ( RetrieveFile == mode ) {
01297 QCString s;
01298 s.resize( bytesAvailable() );
01299 readBlock( s.data(), bytesAvailable() );
01300 file.writeBlock( s.data(), s.size() );
01301 }
01302 else if ( RetrieveGzipFile == mode ) {
01303 if ( !retrieveTargzProc->isRunning() )
01304 retrieveTargzProc->start();
01305
01306 QByteArray s;
01307 s.resize( bytesAvailable() );
01308 readBlock( s.data(), bytesAvailable() );
01309 retrieveTargzProc->writeToStdin( s );
01310 odebug << "wrote " << s.size() << " bytes to ungzip " << oendl;
01311 }
01312
01313 else if ( RetrieveBuffer == mode ) {
01314 QCString s;
01315 s.resize( bytesAvailable() );
01316 readBlock( s.data(), bytesAvailable() );
01317 buf.writeBlock( s.data(), s.size() );
01318 }
01319 }
01320
01321 void ServerDTP::writeTargzBlock()
01322 {
01323 QByteArray block = createTargzProc->readStdout();
01324 writeBlock( block.data(), block.size() );
01325 odebug << "writeTargzBlock " << block.size() << "" << oendl;
01326 }
01327
01328 void ServerDTP::targzDone()
01329 {
01330 odebug << "tar and gzip done" << oendl;
01331 emit completed();
01332 mode = Idle;
01333 disconnect( createTargzProc, SIGNAL( readyReadStdout() ),
01334 this, SLOT( writeTargzBlock() ) );
01335 }
01336
01337 void ServerDTP::sendFile( const QString fn, const QHostAddress& host, Q_UINT16 port )
01338 {
01339 file.setName( fn );
01340 mode = SendFile;
01341 connectToHost( host.toString(), port );
01342 }
01343
01344 void ServerDTP::sendFile( const QString fn )
01345 {
01346 file.setName( fn );
01347 mode = SendFile;
01348 }
01349
01350 void ServerDTP::sendGzipFile( const QString &fn,
01351 const QStringList &archiveTargets,
01352 const QHostAddress& host, Q_UINT16 port )
01353 {
01354 sendGzipFile( fn, archiveTargets );
01355 connectToHost( host.toString(), port );
01356 }
01357
01358 void ServerDTP::sendGzipFile( const QString &fn,
01359 const QStringList &archiveTargets )
01360 {
01361 mode = SendGzipFile;
01362 file.setName( fn );
01363
01364 QStringList args = "targzip";
01365
01366 args += archiveTargets;
01367 odebug << "sendGzipFile " << args.join(" ") << "" << oendl;
01368 createTargzProc->setArguments( args );
01369 connect( createTargzProc,
01370 SIGNAL( readyReadStdout() ), SLOT( writeTargzBlock() ) );
01371 }
01372
01373 void ServerDTP::retrieveFile( const QString fn, const QHostAddress& host, Q_UINT16 port, int fileSize )
01374 {
01375 recvFileSize = fileSize;
01376 file.setName( fn );
01377 mode = RetrieveFile;
01378 connectToHost( host.toString(), port );
01379 }
01380
01381 void ServerDTP::retrieveFile( const QString fn, int fileSize )
01382 {
01383 recvFileSize = fileSize;
01384 file.setName( fn );
01385 mode = RetrieveFile;
01386 }
01387
01388 void ServerDTP::retrieveGzipFile( const QString &fn )
01389 {
01390 odebug << "retrieveGzipFile " << fn << "" << oendl;
01391 file.setName( fn );
01392 mode = RetrieveGzipFile;
01393
01394 retrieveTargzProc->setArguments( "targunzip" );
01395 connect( retrieveTargzProc, SIGNAL( processExited() ),
01396 SLOT( extractTarDone() ) );
01397 }
01398
01399 void ServerDTP::retrieveGzipFile( const QString &fn, const QHostAddress& host, Q_UINT16 port )
01400 {
01401 retrieveGzipFile( fn );
01402 connectToHost( host.toString(), port );
01403 }
01404
01405 void ServerDTP::sendByteArray( const QByteArray& array, const QHostAddress& host, Q_UINT16 port )
01406 {
01407 buf.setBuffer( array );
01408 mode = SendBuffer;
01409 connectToHost( host.toString(), port );
01410 }
01411
01412 void ServerDTP::sendByteArray( const QByteArray& array )
01413 {
01414 buf.setBuffer( array );
01415 mode = SendBuffer;
01416 }
01417
01418 void ServerDTP::retrieveByteArray( const QHostAddress& host, Q_UINT16 port )
01419 {
01420 buf.setBuffer( QByteArray() );
01421 mode = RetrieveBuffer;
01422 connectToHost( host.toString(), port );
01423 }
01424
01425 void ServerDTP::retrieveByteArray()
01426 {
01427 buf.setBuffer( QByteArray() );
01428 mode = RetrieveBuffer;
01429 }
01430
01431 void ServerDTP::setSocket( int socket )
01432 {
01433 QSocket::setSocket( socket );
01434 connected();
01435 }
01436