5 #include <netinet/in.h>
7 #include <sys/socket.h>
15 #include <boost/lexical_cast.hpp>
16 #include "Session.hpp"
17 #include "SessionController.hpp"
19 #include "Exceptions.hpp"
23 int Session::m_counter
= 0;
25 Session::Session( int controlSock
, const SessionControllerPtr
& sessionController
, const AuthPtr
& auth
, const std::string
& ip
, const ServerWPtr
& server
)
26 : m_control( Telnet::Create( controlSock
) )
27 , m_controlSock( controlSock
)
31 , m_state( S_GREETING
)
32 , m_sessionController( sessionController
)
38 socklen_t size
= sizeof( sockaddr_in
);
40 if( getpeername( m_controlSock
, (sockaddr
*)&addr
, &size
) == -1 )
42 g_log
->Print( strerror( errno
) );
43 throw ServerCrashException
;
46 m_dataAddress
= inet_ntoa( addr
.sin_addr
);
48 g_log
->Print( std::string("[Session] Initializing session ") + boost::lexical_cast
<std::string
>( m_id
) + " for " + m_dataAddress
);
53 if( m_controlSock
!= 0 )
55 g_log
->Print( std::string("[Session] Closing control socket ") + boost::lexical_cast
<std::string
>( m_id
) );
57 closesocket( m_controlSock
);
59 close( m_controlSock
);
63 if( m_listenSock
!= 0 )
66 closesocket( m_listenSock
);
68 close( m_listenSock
);
72 // Data (RFile) needs to be freed before Filesystem (RFs) on symbian
77 SessionPtr
Session::Create( int controlSock
, const SessionControllerPtr
& sessionController
, const AuthPtr
& auth
, const std::string
& ip
, const ServerWPtr
& server
)
79 SessionPtr
ret( new Session( controlSock
, sessionController
, auth
, ip
, server
) );
101 m_state
= S_PASSWORD
;
107 PassState state
= AwaitPassword();
109 if( state
== PS_LOGGEDIN
)
113 else if( state
== PS_BADPASS
)
133 catch( SyntaxError
& e
)
137 catch( QuitRequested
& e
)
139 m_control
->Write( "221 Bye" );
142 catch( SessionError
& e
)
144 g_log
->Print( std::string("[Session] ") + boost::lexical_cast
<std::string
>( m_id
) + " encountered problems (" + strerror( errno
) + ")" );
148 catch( ConnectionTerminated
& e
)
150 g_log
->Print( std::string("[Session] Connection ") + boost::lexical_cast
<std::string
>( m_id
) + " terminated" );
155 void Session::Remove()
157 SessionControllerPtr sessionController
= m_sessionController
.lock();
158 sessionController
->Remove( m_this
.lock() );
161 void Session::SendGreeting()
163 ServerPtr server
= m_server
.lock();
164 const std::list
<std::string
> welcome
= server
->GetWelcomeMessage();
166 for( std::list
<std::string
>::const_iterator it
= welcome
.begin(); it
!= welcome
.end(); ++it
)
168 m_control
->Write( "220-" + *it
);
171 m_control
->Write( "220 Dumb FTP Server ready" );
174 void Session::SendSyntaxError()
176 m_control
->Write( "500 Syntax error" );
179 void Session::SendNotLoggedIn()
181 m_control
->Write( "530 Not logged in" );
184 void Session::SendDataConnectionBusy()
186 // Is it the correct answer?
187 m_control
->Write( "425 File transfer already takes place" );
190 void Session::SendSyst()
192 // Is it even relevant?
193 m_control
->Write( "215 UNIX" );
196 bool Session::AwaitLogin()
198 if( m_control
->Read() )
200 Command cmd
= GetCommand();
202 if( cmd
[0] == "USER" )
204 if( cmd
.size() != 2 )
210 if( m_auth
->Login( cmd
[1] ) )
212 m_control
->Write( "331 Need password" );
222 else if( cmd
[0] == "QUIT" )
224 throw QuitRequestedException
;
235 Session::PassState
Session::AwaitPassword()
237 if( m_control
->Read() )
239 Command cmd
= GetCommand();
241 if( cmd
[0] == "PASS" )
243 if( cmd
.size() != 2 )
249 if( m_auth
->Password( m_user
, cmd
[1] ) )
251 m_control
->Write( "230 Logged in" );
253 m_filesystem
.reset( new Filesystem( m_auth
->GetRoot( m_user
) ) );
255 g_log
->Print( std::string("[Session] User ") + m_user
+ " logged in on session " + boost::lexical_cast
<std::string
>( m_id
) );
265 else if( cmd
[0] == "QUIT" )
267 throw QuitRequestedException
;
279 void Session::AwaitReady()
281 if( m_control
->Read() )
283 Command cmd
= GetCommand();
285 if( cmd
[0] == "QUIT" )
287 throw QuitRequestedException
;
289 else if( cmd
[0] == "NOOP" )
291 m_control
->Write( "200 OK" );
293 else if( cmd
[0] == "MODE" )
297 else if( cmd
[0] == "TYPE" )
301 else if( cmd
[0] == "STRU" )
305 else if( cmd
[0] == "PWD" )
309 else if( cmd
[0] == "CWD" )
311 ChangeDirectory( cmd
);
313 else if( cmd
[0] == "CDUP" )
315 ChangeDirectory( ".." );
317 else if( cmd
[0] == "SYST" )
321 else if( cmd
[0] == "PORT" )
325 else if( cmd
[0] == "RETR" )
329 else if( cmd
[0] == "STOR" )
333 else if( cmd
[0] == "ABOR" )
337 else if( cmd
[0] == "LIST" )
341 else if( cmd
[0] == "PASV" )
345 else if( cmd
[0] == "DELE" )
349 else if( cmd
[0] == "MKD" )
353 else if( cmd
[0] == "RMD" )
359 throw SyntaxErrorException
;
364 void Session::HandleMode( const Command
& cmd
)
366 if( cmd
.size() != 2 )
368 throw SyntaxErrorException
;
371 std::string param
= cmd
[1];
376 m_control
->Write( "200 OK" );
378 else if( param
== "B" || param
== "C" )
380 m_control
->Write( "504 Not implemented" );
384 throw SyntaxErrorException
;
388 void Session::HandleType( const Command
& cmd
)
390 if( cmd
.size() < 2 || cmd
.size() > 3)
392 throw SyntaxErrorException
;
395 std::string param
= cmd
[1];
400 if( cmd
.size() == 3 )
402 std::string param2
= cmd
[2];
407 m_control
->Write( "504 Not implemented" );
412 m_control
->Write( "200 OK" );
414 else if( param
== "I" )
416 m_control
->Write( "200 OK" );
418 else if( param
== "E" || param
== "L" )
420 m_control
->Write( "504 Not implemented" );
424 throw SyntaxErrorException
;
428 void Session::HandleStru( const Command
& cmd
)
430 if( cmd
.size() != 2 )
432 throw SyntaxErrorException
;
435 std::string param
= cmd
[1];
440 m_control
->Write( "200 OK" );
442 else if( param
== "R" )
444 throw SessionErrorException
;
446 else if( param
== "P" )
448 m_control
->Write( "504 Not implemented" );
452 throw SyntaxErrorException
;
456 void Session::HandlePort( const Command
& cmd
)
458 if( cmd
.size() != 2 )
460 throw SyntaxErrorException
;
465 close( m_listenSock
);
469 PortVector pv
= SplitPort( cmd
[1] );
472 throw SyntaxErrorException
;
475 m_dataAddress
= pv
[0] + "." + pv
[1] + "." + pv
[2] + "." + pv
[3];
476 m_dataPort
= ( boost::lexical_cast
<int>( pv
[4] ) << 8 ) + boost::lexical_cast
<int>( pv
[5] );
478 m_control
->Write( "200 OK" );
481 void Session::HandleRetr( const Command
& cmd
)
485 SendDataConnectionBusy();
493 void Session::HandleStor( const Command
& cmd
)
497 SendDataConnectionBusy();
505 void Session::HandleAbor()
509 m_control
->Write( "225 No data connection" );
513 g_log
->Print( std::string("[Session] Data connection aborted on session ") + boost::lexical_cast
<std::string
>( m_id
) );
517 m_control
->Write( "426 File transfer aborted" );
518 m_control
->Write( "226 Data connection closed" );
522 void Session::HandleList( const Command
& cmd
)
524 std::string
path( "." );
526 if( cmd
.size() > 1 && cmd
[1].size() > 0 && cmd
[1][0] != '-' )
531 std::list
<std::string
> list
= m_filesystem
->GetListing( path
);
535 m_control
->Write( "450 Some problems" );
539 m_data
.reset( new Data( m_this
, list
) );
541 if( !OpenDataConnection() )
543 m_control
->Write( "425 Can't open data connection" );
548 m_control
->Write( std::string( "150 Listing " ) + path
);
549 g_log
->Print( std::string("[Session] Sending listing on session ") + boost::lexical_cast
<std::string
>( m_id
) );
553 void Session::HandlePasv( const Command
& cmd
)
555 if( cmd
.size() != 1 )
557 throw SyntaxErrorException
;
562 if( ( m_listenSock
= socket( PF_INET
, SOCK_STREAM
, 0 ) ) == -1 )
564 throw SessionErrorException
;
568 addr
.sin_family
= AF_INET
;
571 addr
.sin_addr
.s_addr
= inet_addr( m_ip
.c_str() );
573 inet_aton( m_ip
.c_str(), &addr
.sin_addr
);
575 memset( addr
.sin_zero
, 0, sizeof( addr
.sin_zero
) );
577 if( bind( m_listenSock
, (sockaddr
*)&addr
, sizeof( addr
) ) == -1 )
579 throw SessionErrorException
;
582 if( listen( m_listenSock
, 1 ) == -1 )
584 throw SessionErrorException
;
589 socklen_t len
= sizeof( addr
);
590 if( getsockname( m_listenSock
, (sockaddr
*)&addr
, &len
) == -1 )
592 throw SessionErrorException
;
595 int port
= ntohs( addr
.sin_port
);
596 std::string ip
= inet_ntoa( addr
.sin_addr
);
598 std::replace( ip
.begin(), ip
.end(), '.', ',' );
600 m_control
->Write( std::string( "227 Entering passive mode (" ) + ip
+
601 "," + boost::lexical_cast
<std::string
>( port
>> 8 ) +
602 "," + boost::lexical_cast
<std::string
>( port
& 0xFF ) + ")" );
605 void Session::HandleDele( const Command
& cmd
)
607 if( cmd
.size() != 2 )
609 throw SyntaxErrorException
;
612 if( m_filesystem
->Delete( cmd
[1] ) )
614 m_control
->Write( std::string( "250 Deleted " ) + cmd
[1] );
618 m_control
->Write( std::string( "550 No access to " ) + cmd
[1] + " (" + strerror( errno
) + ")" );
622 void Session::HandleMkd( const Command
& cmd
)
624 if( cmd
.size() != 2 )
626 throw SyntaxErrorException
;
629 std::string ret
= m_filesystem
->MkDir( cmd
[1] );
633 m_control
->Write( std::string( "257 \"" + ret
+ "\" created" ) );
637 m_control
->Write( "550 Directory not created" );
641 void Session::HandleRmd( const Command
& cmd
)
643 if( cmd
.size() != 2 )
645 throw SyntaxErrorException
;
648 if( m_filesystem
->RmDir( cmd
[1] ) )
650 m_control
->Write( "250 OK" );
654 m_control
->Write( "550 No access" );
658 void Session::PrintDirectory()
660 m_control
->Write( std::string( "257 " ) + m_filesystem
->GetPath() );
663 void Session::ChangeDirectory( const Command
& cmd
)
665 if( cmd
.size() != 2 )
667 throw SyntaxErrorException
;
670 ChangeDirectory( cmd
[1] );
673 void Session::ChangeDirectory( const std::string
& cd
)
675 if( m_filesystem
->ChangeDirectory( cd
) )
677 m_control
->Write( std::string( "200 Changed directory to " ) + m_filesystem
->GetPath() );
681 m_control
->Write( "550 Requested action not taken" );
685 void Session::Upload( const Command
& cmd
)
693 if( cmd
.size() != 2 )
695 throw SyntaxErrorException
;
698 if( !m_filesystem
->FileExists( cmd
[1] ) )
700 m_control
->Write( std::string( "550 File " ) + cmd
[1] + " not found" );
705 if( ( f
= m_filesystem
->FileOpenSymbian( cmd
[1], Filesystem::M_READ
) ) == NULL
)
707 if( ( f
= m_filesystem
->FileOpen( cmd
[1], Filesystem::M_READ
) ) == NULL
)
710 m_control
->Write( std::string( "450 File " ) + cmd
[1] + " not accessible" );
714 m_data
.reset( new Data( m_this
, f
, Data::M_UPLOAD
) );
716 if( !OpenDataConnection() )
718 m_control
->Write( "425 Can't open data connection" );
723 m_control
->Write( std::string( "150 Sending " ) + cmd
[1] );
724 g_log
->Print( std::string("[Session] Opened new upload on session ") + boost::lexical_cast
<std::string
>( m_id
) );
728 void Session::Download( const Command
& cmd
)
736 if( cmd
.size() != 2 )
738 throw SyntaxErrorException
;
742 if( ( f
= m_filesystem
->FileOpenSymbian( cmd
[1], Filesystem::M_WRITE
) ) == NULL
)
744 if( ( f
= m_filesystem
->FileOpen( cmd
[1], Filesystem::M_WRITE
) ) == NULL
)
747 m_control
->Write( std::string( "450 File " ) + cmd
[1] + " not accessible" );
751 m_data
.reset( new Data( m_this
, f
, Data::M_DOWNLOAD
) );
753 if( !OpenDataConnection() )
755 m_control
->Write( "425 Can't open data connection" );
760 m_control
->Write( std::string( "150 Receiving " ) + cmd
[1] );
761 g_log
->Print( std::string("[Session] Opened new download on session ") + boost::lexical_cast
<std::string
>( m_id
) );
765 void Session::DataConnectionFinished()
767 g_log
->Print( std::string("[Session] Data connection closed on session ") + boost::lexical_cast
<std::string
>( m_id
) );
769 m_control
->Write( "226 File transfer completed" );
774 void Session::DataConnectionError()
776 g_log
->Print( std::string("[Session] Data connection error on session ") + boost::lexical_cast
<std::string
>( m_id
) );
778 m_control
->Write( "426 Data connection lost" );
783 void Session::OutOfSpace()
785 g_log
->Print( std::string("[Session] Out of space on session ") + boost::lexical_cast
<std::string
>( m_id
) );
787 m_control
->Write( "552 No space left" );
792 bool Session::OpenDataConnection()
798 ok
= m_data
->Accept( m_listenSock
);
799 close( m_listenSock
);
804 ok
= m_data
->Connect( m_dataAddress
, m_dataPort
);
810 std::list
<int> Session::GetFds() const
814 // Server starts by sending greeting, not waiting for data
815 if( m_state
== S_GREETING
)
817 ret
.push_back( -m_controlSock
);
821 ret
.push_back( m_controlSock
);
824 if( m_listenSock
!= 0 )
826 ret
.push_back( m_listenSock
);
829 if( m_data
&& m_data
->GetSock() != 0 )
831 // Hack, mark write descriptors as negative
832 if( m_data
->GetMode() == Data::M_DOWNLOAD
)
834 ret
.push_back( m_data
->GetSock() );
836 else // M_UPLOAD, M_LISTING
838 ret
.push_back( -m_data
->GetSock() );