6 #include <netinet/in.h>
8 #include <sys/socket.h>
9 #include <boost/lexical_cast.hpp>
10 #include "Session.hpp"
11 #include "SessionController.hpp"
13 #include "Exceptions.hpp"
17 int Session::m_counter
= 0;
19 Session::Session( int controlSock
, const SessionControllerPtr
& sessionController
, const AuthPtr
& auth
, const std::string
& ip
, const ServerWPtr
& server
)
20 : m_control( Telnet::Create( controlSock
) )
21 , m_controlSock( controlSock
)
25 , m_state( S_GREETING
)
26 , m_sessionController( sessionController
)
32 socklen_t size
= sizeof( sockaddr_in
);
34 if( getpeername( m_controlSock
, (sockaddr
*)&addr
, &size
) == -1 )
36 g_log
->Print( strerror( errno
) );
37 throw ServerCrashException
;
40 m_dataAddress
= inet_ntoa( addr
.sin_addr
);
42 g_log
->Print( std::string("[Session] Initializing session ") + boost::lexical_cast
<std::string
>( m_id
) + " for " + m_dataAddress
);
47 if( m_controlSock
!= 0 )
49 g_log
->Print( std::string("[Session] Closing control socket ") + boost::lexical_cast
<std::string
>( m_id
) );
50 close( m_controlSock
);
53 if( m_listenSock
!= 0 )
55 close( m_listenSock
);
58 // Data (RFile) needs to be freed before Filesystem (RFs) on symbian
63 SessionPtr
Session::Create( int controlSock
, const SessionControllerPtr
& sessionController
, const AuthPtr
& auth
, const std::string
& ip
, const ServerWPtr
& server
)
65 SessionPtr
ret( new Session( controlSock
, sessionController
, auth
, ip
, server
) );
93 PassState state
= AwaitPassword();
95 if( state
== PS_LOGGEDIN
)
99 else if( state
== PS_BADPASS
)
119 catch( SyntaxError
& e
)
123 catch( QuitRequested
& e
)
125 m_control
->Write( "221 Bye" );
128 catch( SessionError
& e
)
130 g_log
->Print( std::string("[Session] ") + boost::lexical_cast
<std::string
>( m_id
) + " encountered problems (" + strerror( errno
) + ")" );
134 catch( ConnectionTerminated
& e
)
136 g_log
->Print( std::string("[Session] Connection ") + boost::lexical_cast
<std::string
>( m_id
) + " terminated" );
141 void Session::Remove()
143 SessionControllerPtr sessionController
= m_sessionController
.lock();
144 sessionController
->Remove( m_this
.lock() );
147 void Session::SendGreeting()
149 ServerPtr server
= m_server
.lock();
150 const std::list
<std::string
> welcome
= server
->GetWelcomeMessage();
152 for( std::list
<std::string
>::const_iterator it
= welcome
.begin(); it
!= welcome
.end(); ++it
)
154 m_control
->Write( "220-" + *it
);
157 m_control
->Write( "220 Dumb FTP Server ready" );
160 void Session::SendSyntaxError()
162 m_control
->Write( "500 Syntax error" );
165 void Session::SendNotLoggedIn()
167 m_control
->Write( "530 Not logged in" );
170 void Session::SendDataConnectionBusy()
172 // Is it the correct answer?
173 m_control
->Write( "425 File transfer already takes place" );
176 void Session::SendSyst()
178 // Is it even relevant?
179 m_control
->Write( "215 UNIX" );
182 bool Session::AwaitLogin()
184 if( m_control
->Read() )
186 Command cmd
= GetCommand();
188 if( cmd
[0] == "USER" )
190 if( cmd
.size() != 2 )
196 if( m_auth
->Login( cmd
[1] ) )
198 m_control
->Write( "331 Need password" );
208 else if( cmd
[0] == "QUIT" )
210 throw QuitRequestedException
;
221 Session::PassState
Session::AwaitPassword()
223 if( m_control
->Read() )
225 Command cmd
= GetCommand();
227 if( cmd
[0] == "PASS" )
229 if( cmd
.size() != 2 )
235 if( m_auth
->Password( m_user
, cmd
[1] ) )
237 m_control
->Write( "230 Logged in" );
239 m_filesystem
.reset( new Filesystem( m_auth
->GetRoot( m_user
) ) );
241 g_log
->Print( std::string("[Session] User ") + m_user
+ " logged in on session " + boost::lexical_cast
<std::string
>( m_id
) );
251 else if( cmd
[0] == "QUIT" )
253 throw QuitRequestedException
;
265 void Session::AwaitReady()
267 if( m_control
->Read() )
269 Command cmd
= GetCommand();
271 if( cmd
[0] == "QUIT" )
273 throw QuitRequestedException
;
275 else if( cmd
[0] == "NOOP" )
277 m_control
->Write( "200 OK" );
279 else if( cmd
[0] == "MODE" )
283 else if( cmd
[0] == "TYPE" )
287 else if( cmd
[0] == "STRU" )
291 else if( cmd
[0] == "PWD" )
295 else if( cmd
[0] == "CWD" )
297 ChangeDirectory( cmd
);
299 else if( cmd
[0] == "CDUP" )
301 ChangeDirectory( ".." );
303 else if( cmd
[0] == "SYST" )
307 else if( cmd
[0] == "PORT" )
311 else if( cmd
[0] == "RETR" )
315 else if( cmd
[0] == "STOR" )
319 else if( cmd
[0] == "ABOR" )
323 else if( cmd
[0] == "LIST" )
327 else if( cmd
[0] == "PASV" )
331 else if( cmd
[0] == "DELE" )
335 else if( cmd
[0] == "MKD" )
339 else if( cmd
[0] == "RMD" )
345 throw SyntaxErrorException
;
350 void Session::HandleMode( const Command
& cmd
)
352 if( cmd
.size() != 2 )
354 throw SyntaxErrorException
;
357 std::string param
= cmd
[1];
362 m_control
->Write( "200 OK" );
364 else if( param
== "B" || param
== "C" )
366 m_control
->Write( "504 Not implemented" );
370 throw SyntaxErrorException
;
374 void Session::HandleType( const Command
& cmd
)
376 if( cmd
.size() < 2 || cmd
.size() > 3)
378 throw SyntaxErrorException
;
381 std::string param
= cmd
[1];
386 if( cmd
.size() == 3 )
388 std::string param2
= cmd
[2];
393 m_control
->Write( "504 Not implemented" );
398 m_control
->Write( "200 OK" );
400 else if( param
== "I" )
402 m_control
->Write( "200 OK" );
404 else if( param
== "E" || param
== "L" )
406 m_control
->Write( "504 Not implemented" );
410 throw SyntaxErrorException
;
414 void Session::HandleStru( const Command
& cmd
)
416 if( cmd
.size() != 2 )
418 throw SyntaxErrorException
;
421 std::string param
= cmd
[1];
426 m_control
->Write( "200 OK" );
428 else if( param
== "R" )
430 throw SessionErrorException
;
432 else if( param
== "P" )
434 m_control
->Write( "504 Not implemented" );
438 throw SyntaxErrorException
;
442 void Session::HandlePort( const Command
& cmd
)
444 if( cmd
.size() != 2 )
446 throw SyntaxErrorException
;
451 close( m_listenSock
);
455 PortVector pv
= SplitPort( cmd
[1] );
458 throw SyntaxErrorException
;
461 m_dataAddress
= pv
[0] + "." + pv
[1] + "." + pv
[2] + "." + pv
[3];
462 m_dataPort
= ( boost::lexical_cast
<int>( pv
[4] ) << 8 ) + boost::lexical_cast
<int>( pv
[5] );
464 m_control
->Write( "200 OK" );
467 void Session::HandleRetr( const Command
& cmd
)
471 SendDataConnectionBusy();
479 void Session::HandleStor( const Command
& cmd
)
483 SendDataConnectionBusy();
491 void Session::HandleAbor()
495 m_control
->Write( "225 No data connection" );
499 g_log
->Print( std::string("[Session] Data connection aborted on session ") + boost::lexical_cast
<std::string
>( m_id
) );
503 m_control
->Write( "426 File transfer aborted" );
504 m_control
->Write( "226 Data connection closed" );
508 void Session::HandleList( const Command
& cmd
)
510 std::string
path( "." );
512 if( cmd
.size() > 1 && cmd
[1].size() > 0 && cmd
[1][0] != '-' )
517 std::list
<std::string
> list
= m_filesystem
->GetListing( path
);
521 m_control
->Write( "450 Some problems" );
525 m_data
.reset( new Data( m_this
, list
) );
527 if( !OpenDataConnection() )
529 m_control
->Write( "425 Can't open data connection" );
534 m_control
->Write( std::string( "150 Listing " ) + path
);
535 g_log
->Print( std::string("[Session] Sending listing on session ") + boost::lexical_cast
<std::string
>( m_id
) );
539 void Session::HandlePasv( const Command
& cmd
)
541 if( cmd
.size() != 1 )
543 throw SyntaxErrorException
;
548 if( ( m_listenSock
= socket( PF_INET
, SOCK_STREAM
, 0 ) ) == -1 )
550 throw SessionErrorException
;
554 addr
.sin_family
= AF_INET
;
556 inet_aton( m_ip
.c_str(), &addr
.sin_addr
);
557 memset( addr
.sin_zero
, 0, sizeof( addr
.sin_zero
) );
559 if( bind( m_listenSock
, (sockaddr
*)&addr
, sizeof( addr
) ) == -1 )
561 throw SessionErrorException
;
564 if( listen( m_listenSock
, 1 ) == -1 )
566 throw SessionErrorException
;
571 socklen_t len
= sizeof( addr
);
572 if( getsockname( m_listenSock
, (sockaddr
*)&addr
, &len
) == -1 )
574 throw SessionErrorException
;
577 int port
= ntohs( addr
.sin_port
);
578 std::string ip
= inet_ntoa( addr
.sin_addr
);
580 std::replace( ip
.begin(), ip
.end(), '.', ',' );
582 m_control
->Write( std::string( "227 Entering passive mode " ) + ip
+
583 "," + boost::lexical_cast
<std::string
>( port
>> 8 ) +
584 "," + boost::lexical_cast
<std::string
>( port
& 0xFF ) );
587 void Session::HandleDele( const Command
& cmd
)
589 if( cmd
.size() != 2 )
591 throw SyntaxErrorException
;
594 if( m_filesystem
->Delete( cmd
[1] ) )
596 m_control
->Write( std::string( "250 Deleted " ) + cmd
[1] );
600 m_control
->Write( std::string( "550 No access to " ) + cmd
[1] + " (" + strerror( errno
) + ")" );
604 void Session::HandleMkd( const Command
& cmd
)
606 if( cmd
.size() != 2 )
608 throw SyntaxErrorException
;
611 std::string ret
= m_filesystem
->MkDir( cmd
[1] );
615 m_control
->Write( std::string( "257 \"" + ret
+ "\" created" ) );
619 m_control
->Write( "550 Directory not created" );
623 void Session::HandleRmd( const Command
& cmd
)
625 if( cmd
.size() != 2 )
627 throw SyntaxErrorException
;
630 if( m_filesystem
->RmDir( cmd
[1] ) )
632 m_control
->Write( "250 OK" );
636 m_control
->Write( "550 No access" );
640 void Session::PrintDirectory()
642 m_control
->Write( std::string( "257 " ) + m_filesystem
->GetPath() );
645 void Session::ChangeDirectory( const Command
& cmd
)
647 if( cmd
.size() != 2 )
649 throw SyntaxErrorException
;
652 ChangeDirectory( cmd
[1] );
655 void Session::ChangeDirectory( const std::string
& cd
)
657 if( m_filesystem
->ChangeDirectory( cd
) )
659 m_control
->Write( std::string( "200 Changed directory to " ) + m_filesystem
->GetPath() );
663 m_control
->Write( "550 Requested action not taken" );
667 void Session::Upload( const Command
& cmd
)
675 if( cmd
.size() != 2 )
677 throw SyntaxErrorException
;
680 if( !m_filesystem
->FileExists( cmd
[1] ) )
682 m_control
->Write( std::string( "550 File " ) + cmd
[1] + " not found" );
687 if( ( f
= m_filesystem
->FileOpenSymbian( cmd
[1], Filesystem::M_READ
) ) == NULL
)
689 if( ( f
= m_filesystem
->FileOpen( cmd
[1], Filesystem::M_READ
) ) == NULL
)
692 m_control
->Write( std::string( "450 File " ) + cmd
[1] + " not accessible" );
696 m_data
.reset( new Data( m_this
, f
, Data::M_UPLOAD
) );
698 if( !OpenDataConnection() )
700 m_control
->Write( "425 Can't open data connection" );
705 m_control
->Write( std::string( "150 Sending " ) + cmd
[1] );
706 g_log
->Print( std::string("[Session] Opened new upload on session ") + boost::lexical_cast
<std::string
>( m_id
) );
710 void Session::Download( const Command
& cmd
)
718 if( cmd
.size() != 2 )
720 throw SyntaxErrorException
;
724 if( ( f
= m_filesystem
->FileOpenSymbian( cmd
[1], Filesystem::M_WRITE
) ) == NULL
)
726 if( ( f
= m_filesystem
->FileOpen( cmd
[1], Filesystem::M_WRITE
) ) == NULL
)
729 m_control
->Write( std::string( "450 File " ) + cmd
[1] + " not accessible" );
733 m_data
.reset( new Data( m_this
, f
, Data::M_DOWNLOAD
) );
735 if( !OpenDataConnection() )
737 m_control
->Write( "425 Can't open data connection" );
742 m_control
->Write( std::string( "150 Receiving " ) + cmd
[1] );
743 g_log
->Print( std::string("[Session] Opened new download on session ") + boost::lexical_cast
<std::string
>( m_id
) );
747 void Session::DataConnectionFinished()
749 g_log
->Print( std::string("[Session] Data connection closed on session ") + boost::lexical_cast
<std::string
>( m_id
) );
751 m_control
->Write( "226 File transfer completed" );
756 void Session::DataConnectionError()
758 g_log
->Print( std::string("[Session] Data connection error on session ") + boost::lexical_cast
<std::string
>( m_id
) );
760 m_control
->Write( "426 Data connection lost" );
765 void Session::OutOfSpace()
767 g_log
->Print( std::string("[Session] Out of space on session ") + boost::lexical_cast
<std::string
>( m_id
) );
769 m_control
->Write( "552 No space left" );
774 bool Session::OpenDataConnection()
780 ok
= m_data
->Accept( m_listenSock
);
781 close( m_listenSock
);
786 ok
= m_data
->Connect( m_dataAddress
, m_dataPort
);
792 std::list
<int> Session::GetFds() const
796 // Server starts by sending greeting, not waiting for data
797 if( m_state
== S_GREETING
)
799 ret
.push_back( -m_controlSock
);
803 ret
.push_back( m_controlSock
);
806 if( m_listenSock
!= 0 )
808 ret
.push_back( m_listenSock
);
811 if( m_data
&& m_data
->GetSock() != 0 )
813 // Hack, mark write descriptors as negative
814 if( m_data
->GetMode() == Data::M_DOWNLOAD
)
816 ret
.push_back( m_data
->GetSock() );
818 else // M_UPLOAD, M_LISTING
820 ret
.push_back( -m_data
->GetSock() );