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
) );
56 close( m_controlSock
);
59 if( m_listenSock
!= 0 )
61 close( m_listenSock
);
64 // Data (RFile) needs to be freed before Filesystem (RFs) on symbian
69 SessionPtr
Session::Create( int controlSock
, const SessionControllerPtr
& sessionController
, const AuthPtr
& auth
, const std::string
& ip
, const ServerWPtr
& server
)
71 SessionPtr
ret( new Session( controlSock
, sessionController
, auth
, ip
, server
) );
99 PassState state
= AwaitPassword();
101 if( state
== PS_LOGGEDIN
)
105 else if( state
== PS_BADPASS
)
125 catch( SyntaxError
& e
)
129 catch( QuitRequested
& e
)
131 m_control
->Write( "221 Bye" );
134 catch( SessionError
& e
)
136 g_log
->Print( std::string("[Session] ") + boost::lexical_cast
<std::string
>( m_id
) + " encountered problems (" + strerror( errno
) + ")" );
140 catch( ConnectionTerminated
& e
)
142 g_log
->Print( std::string("[Session] Connection ") + boost::lexical_cast
<std::string
>( m_id
) + " terminated" );
147 void Session::Remove()
149 SessionControllerPtr sessionController
= m_sessionController
.lock();
150 sessionController
->Remove( m_this
.lock() );
153 void Session::SendGreeting()
155 ServerPtr server
= m_server
.lock();
156 const std::list
<std::string
> welcome
= server
->GetWelcomeMessage();
158 for( std::list
<std::string
>::const_iterator it
= welcome
.begin(); it
!= welcome
.end(); ++it
)
160 m_control
->Write( "220-" + *it
);
163 m_control
->Write( "220 Dumb FTP Server ready" );
166 void Session::SendSyntaxError()
168 m_control
->Write( "500 Syntax error" );
171 void Session::SendNotLoggedIn()
173 m_control
->Write( "530 Not logged in" );
176 void Session::SendDataConnectionBusy()
178 // Is it the correct answer?
179 m_control
->Write( "425 File transfer already takes place" );
182 void Session::SendSyst()
184 // Is it even relevant?
185 m_control
->Write( "215 UNIX" );
188 bool Session::AwaitLogin()
190 if( m_control
->Read() )
192 Command cmd
= GetCommand();
194 if( cmd
[0] == "USER" )
196 if( cmd
.size() != 2 )
202 if( m_auth
->Login( cmd
[1] ) )
204 m_control
->Write( "331 Need password" );
214 else if( cmd
[0] == "QUIT" )
216 throw QuitRequestedException
;
227 Session::PassState
Session::AwaitPassword()
229 if( m_control
->Read() )
231 Command cmd
= GetCommand();
233 if( cmd
[0] == "PASS" )
235 if( cmd
.size() != 2 )
241 if( m_auth
->Password( m_user
, cmd
[1] ) )
243 m_control
->Write( "230 Logged in" );
245 m_filesystem
.reset( new Filesystem( m_auth
->GetRoot( m_user
) ) );
247 g_log
->Print( std::string("[Session] User ") + m_user
+ " logged in on session " + boost::lexical_cast
<std::string
>( m_id
) );
257 else if( cmd
[0] == "QUIT" )
259 throw QuitRequestedException
;
271 void Session::AwaitReady()
273 if( m_control
->Read() )
275 Command cmd
= GetCommand();
277 if( cmd
[0] == "QUIT" )
279 throw QuitRequestedException
;
281 else if( cmd
[0] == "NOOP" )
283 m_control
->Write( "200 OK" );
285 else if( cmd
[0] == "MODE" )
289 else if( cmd
[0] == "TYPE" )
293 else if( cmd
[0] == "STRU" )
297 else if( cmd
[0] == "PWD" )
301 else if( cmd
[0] == "CWD" )
303 ChangeDirectory( cmd
);
305 else if( cmd
[0] == "CDUP" )
307 ChangeDirectory( ".." );
309 else if( cmd
[0] == "SYST" )
313 else if( cmd
[0] == "PORT" )
317 else if( cmd
[0] == "RETR" )
321 else if( cmd
[0] == "STOR" )
325 else if( cmd
[0] == "ABOR" )
329 else if( cmd
[0] == "LIST" )
333 else if( cmd
[0] == "PASV" )
337 else if( cmd
[0] == "DELE" )
341 else if( cmd
[0] == "MKD" )
345 else if( cmd
[0] == "RMD" )
351 throw SyntaxErrorException
;
356 void Session::HandleMode( const Command
& cmd
)
358 if( cmd
.size() != 2 )
360 throw SyntaxErrorException
;
363 std::string param
= cmd
[1];
368 m_control
->Write( "200 OK" );
370 else if( param
== "B" || param
== "C" )
372 m_control
->Write( "504 Not implemented" );
376 throw SyntaxErrorException
;
380 void Session::HandleType( const Command
& cmd
)
382 if( cmd
.size() < 2 || cmd
.size() > 3)
384 throw SyntaxErrorException
;
387 std::string param
= cmd
[1];
392 if( cmd
.size() == 3 )
394 std::string param2
= cmd
[2];
399 m_control
->Write( "504 Not implemented" );
404 m_control
->Write( "200 OK" );
406 else if( param
== "I" )
408 m_control
->Write( "200 OK" );
410 else if( param
== "E" || param
== "L" )
412 m_control
->Write( "504 Not implemented" );
416 throw SyntaxErrorException
;
420 void Session::HandleStru( const Command
& cmd
)
422 if( cmd
.size() != 2 )
424 throw SyntaxErrorException
;
427 std::string param
= cmd
[1];
432 m_control
->Write( "200 OK" );
434 else if( param
== "R" )
436 throw SessionErrorException
;
438 else if( param
== "P" )
440 m_control
->Write( "504 Not implemented" );
444 throw SyntaxErrorException
;
448 void Session::HandlePort( const Command
& cmd
)
450 if( cmd
.size() != 2 )
452 throw SyntaxErrorException
;
457 close( m_listenSock
);
461 PortVector pv
= SplitPort( cmd
[1] );
464 throw SyntaxErrorException
;
467 m_dataAddress
= pv
[0] + "." + pv
[1] + "." + pv
[2] + "." + pv
[3];
468 m_dataPort
= ( boost::lexical_cast
<int>( pv
[4] ) << 8 ) + boost::lexical_cast
<int>( pv
[5] );
470 m_control
->Write( "200 OK" );
473 void Session::HandleRetr( const Command
& cmd
)
477 SendDataConnectionBusy();
485 void Session::HandleStor( const Command
& cmd
)
489 SendDataConnectionBusy();
497 void Session::HandleAbor()
501 m_control
->Write( "225 No data connection" );
505 g_log
->Print( std::string("[Session] Data connection aborted on session ") + boost::lexical_cast
<std::string
>( m_id
) );
509 m_control
->Write( "426 File transfer aborted" );
510 m_control
->Write( "226 Data connection closed" );
514 void Session::HandleList( const Command
& cmd
)
516 std::string
path( "." );
518 if( cmd
.size() > 1 && cmd
[1].size() > 0 && cmd
[1][0] != '-' )
523 std::list
<std::string
> list
= m_filesystem
->GetListing( path
);
527 m_control
->Write( "450 Some problems" );
531 m_data
.reset( new Data( m_this
, list
) );
533 if( !OpenDataConnection() )
535 m_control
->Write( "425 Can't open data connection" );
540 m_control
->Write( std::string( "150 Listing " ) + path
);
541 g_log
->Print( std::string("[Session] Sending listing on session ") + boost::lexical_cast
<std::string
>( m_id
) );
545 void Session::HandlePasv( const Command
& cmd
)
547 if( cmd
.size() != 1 )
549 throw SyntaxErrorException
;
554 if( ( m_listenSock
= socket( PF_INET
, SOCK_STREAM
, 0 ) ) == -1 )
556 throw SessionErrorException
;
560 addr
.sin_family
= AF_INET
;
563 addr
.sin_addr
.s_addr
= inet_addr( m_ip
.c_str() );
565 inet_aton( m_ip
.c_str(), &addr
.sin_addr
);
567 memset( addr
.sin_zero
, 0, sizeof( addr
.sin_zero
) );
569 if( bind( m_listenSock
, (sockaddr
*)&addr
, sizeof( addr
) ) == -1 )
571 throw SessionErrorException
;
574 if( listen( m_listenSock
, 1 ) == -1 )
576 throw SessionErrorException
;
581 socklen_t len
= sizeof( addr
);
582 if( getsockname( m_listenSock
, (sockaddr
*)&addr
, &len
) == -1 )
584 throw SessionErrorException
;
587 int port
= ntohs( addr
.sin_port
);
588 std::string ip
= inet_ntoa( addr
.sin_addr
);
590 std::replace( ip
.begin(), ip
.end(), '.', ',' );
592 m_control
->Write( std::string( "227 Entering passive mode " ) + ip
+
593 "," + boost::lexical_cast
<std::string
>( port
>> 8 ) +
594 "," + boost::lexical_cast
<std::string
>( port
& 0xFF ) );
597 void Session::HandleDele( const Command
& cmd
)
599 if( cmd
.size() != 2 )
601 throw SyntaxErrorException
;
604 if( m_filesystem
->Delete( cmd
[1] ) )
606 m_control
->Write( std::string( "250 Deleted " ) + cmd
[1] );
610 m_control
->Write( std::string( "550 No access to " ) + cmd
[1] + " (" + strerror( errno
) + ")" );
614 void Session::HandleMkd( const Command
& cmd
)
616 if( cmd
.size() != 2 )
618 throw SyntaxErrorException
;
621 std::string ret
= m_filesystem
->MkDir( cmd
[1] );
625 m_control
->Write( std::string( "257 \"" + ret
+ "\" created" ) );
629 m_control
->Write( "550 Directory not created" );
633 void Session::HandleRmd( const Command
& cmd
)
635 if( cmd
.size() != 2 )
637 throw SyntaxErrorException
;
640 if( m_filesystem
->RmDir( cmd
[1] ) )
642 m_control
->Write( "250 OK" );
646 m_control
->Write( "550 No access" );
650 void Session::PrintDirectory()
652 m_control
->Write( std::string( "257 " ) + m_filesystem
->GetPath() );
655 void Session::ChangeDirectory( const Command
& cmd
)
657 if( cmd
.size() != 2 )
659 throw SyntaxErrorException
;
662 ChangeDirectory( cmd
[1] );
665 void Session::ChangeDirectory( const std::string
& cd
)
667 if( m_filesystem
->ChangeDirectory( cd
) )
669 m_control
->Write( std::string( "200 Changed directory to " ) + m_filesystem
->GetPath() );
673 m_control
->Write( "550 Requested action not taken" );
677 void Session::Upload( const Command
& cmd
)
685 if( cmd
.size() != 2 )
687 throw SyntaxErrorException
;
690 if( !m_filesystem
->FileExists( cmd
[1] ) )
692 m_control
->Write( std::string( "550 File " ) + cmd
[1] + " not found" );
697 if( ( f
= m_filesystem
->FileOpenSymbian( cmd
[1], Filesystem::M_READ
) ) == NULL
)
699 if( ( f
= m_filesystem
->FileOpen( cmd
[1], Filesystem::M_READ
) ) == NULL
)
702 m_control
->Write( std::string( "450 File " ) + cmd
[1] + " not accessible" );
706 m_data
.reset( new Data( m_this
, f
, Data::M_UPLOAD
) );
708 if( !OpenDataConnection() )
710 m_control
->Write( "425 Can't open data connection" );
715 m_control
->Write( std::string( "150 Sending " ) + cmd
[1] );
716 g_log
->Print( std::string("[Session] Opened new upload on session ") + boost::lexical_cast
<std::string
>( m_id
) );
720 void Session::Download( const Command
& cmd
)
728 if( cmd
.size() != 2 )
730 throw SyntaxErrorException
;
734 if( ( f
= m_filesystem
->FileOpenSymbian( cmd
[1], Filesystem::M_WRITE
) ) == NULL
)
736 if( ( f
= m_filesystem
->FileOpen( cmd
[1], Filesystem::M_WRITE
) ) == NULL
)
739 m_control
->Write( std::string( "450 File " ) + cmd
[1] + " not accessible" );
743 m_data
.reset( new Data( m_this
, f
, Data::M_DOWNLOAD
) );
745 if( !OpenDataConnection() )
747 m_control
->Write( "425 Can't open data connection" );
752 m_control
->Write( std::string( "150 Receiving " ) + cmd
[1] );
753 g_log
->Print( std::string("[Session] Opened new download on session ") + boost::lexical_cast
<std::string
>( m_id
) );
757 void Session::DataConnectionFinished()
759 g_log
->Print( std::string("[Session] Data connection closed on session ") + boost::lexical_cast
<std::string
>( m_id
) );
761 m_control
->Write( "226 File transfer completed" );
766 void Session::DataConnectionError()
768 g_log
->Print( std::string("[Session] Data connection error on session ") + boost::lexical_cast
<std::string
>( m_id
) );
770 m_control
->Write( "426 Data connection lost" );
775 void Session::OutOfSpace()
777 g_log
->Print( std::string("[Session] Out of space on session ") + boost::lexical_cast
<std::string
>( m_id
) );
779 m_control
->Write( "552 No space left" );
784 bool Session::OpenDataConnection()
790 ok
= m_data
->Accept( m_listenSock
);
791 close( m_listenSock
);
796 ok
= m_data
->Connect( m_dataAddress
, m_dataPort
);
802 std::list
<int> Session::GetFds() const
806 // Server starts by sending greeting, not waiting for data
807 if( m_state
== S_GREETING
)
809 ret
.push_back( -m_controlSock
);
813 ret
.push_back( m_controlSock
);
816 if( m_listenSock
!= 0 )
818 ret
.push_back( m_listenSock
);
821 if( m_data
&& m_data
->GetSock() != 0 )
823 // Hack, mark write descriptors as negative
824 if( m_data
->GetMode() == Data::M_DOWNLOAD
)
826 ret
.push_back( m_data
->GetSock() );
828 else // M_UPLOAD, M_LISTING
830 ret
.push_back( -m_data
->GetSock() );