Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

transferserver.cpp

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

Generated on Sat Nov 5 16:15:34 2005 for OPIE by  doxygen 1.4.2