FtpSession.cs
上传用户:xuelanruo
上传日期:2015-04-02
资源大小:163k
文件大小:35k
源码类别:

Ftp服务器

开发平台:

C#

  1. /*
  2. Ftp Dot Net FtpSession Class : manage each client-session
  3. Copyright (C) 
  4. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
  5. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  6. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  7. Last modification : 04/26/2004 by Simon FERQUEL
  8. */
  9. using System;
  10. using System.IO;
  11. using System.Data;
  12. using System.Collections;
  13. using System.Net;
  14. using System.Net.Sockets;
  15. using System.Threading;
  16. namespace FtpServerLibrary
  17. {
  18. /// <summary>
  19. /// Description r閟um閑 de FtpSession.
  20. /// </summary>
  21. public class FtpSession
  22. {
  23. private Socket      m_pClientSocket    = null;  // Socket du client
  24. private FtpServer   m_pServer          = null;  // Server parent
  25. private long        m_SessionID ; // Identificateur de la session
  26. private string      m_UserName         = "";    // Nom de l'utilisateur
  27. private string      m_CurrentDir       = "/"; // R閜ertoire courrant
  28. private string      m_renameFrom       = "";
  29. private bool        m_PassiveMode      = false; // Flag mode passif
  30. private TcpListener m_pPassiveListener = null; // Listener en cas de mode passif
  31. private IPEndPoint  m_pDataConEndPoint = null; // "EndPoint" pour la connection de donn閑s
  32. private bool        m_Authenticated    = false; // Flag utilisateur authentifi?
  33. private int         m_BadCmdCount      = 0;     // Nombre de commandes erron閑s
  34. private DateTime    m_SessionStartTime; // Date de d閎ut de session
  35. private EndPoint m_rmtEndPoint; // adresse du client
  36. private int m_offset    = 0; // offset utile en cas d'usage de l'option "resume"
  37. private Hashtable m_vPaths; // Liste des r閜ertoire virtuels appliqu閟 ?l'utilisateur
  38. public void Close()
  39. {
  40. try{this.m_pClientSocket.Close();}
  41. catch{}
  42. try{this.m_pPassiveListener.Stop();}
  43. catch{}
  44. }
  45. #region Propri閠閟
  46. public long SessionID
  47. {
  48. get{return m_SessionID;}
  49. }
  50. public EndPoint RemoteEndPoint
  51. {
  52. get{return this.m_rmtEndPoint;}
  53. }
  54. private string m_ftproot    = "";
  55. public Socket ClientSocket
  56. {
  57. get{return this.m_pClientSocket;}
  58. }
  59. #endregion
  60. /// <summary>
  61. /// Constructeur par defaut
  62. /// </summary>
  63. /// <param name="clientSocket">Socket du client connect?/param>
  64. /// <param name="server">instance ded FtpServer</param>
  65. /// <param name="sessionID">Num閞o de session</param>
  66. public FtpSession(Socket clientSocket,FtpServer server,long sessionID)
  67. {
  68. this.m_pClientSocket=clientSocket;
  69. this.m_rmtEndPoint=clientSocket.RemoteEndPoint;
  70. this.m_pServer=server;
  71. this.m_SessionID=sessionID;
  72. this.m_SessionStartTime=System.DateTime.Now;
  73. }
  74. /// <summary>
  75. /// M閠hode permettant d'envoyer un message au client
  76. /// </summary>
  77. /// <param name="data">Message ?envoyer</param>
  78. private void SendData(string data)
  79. {
  80. Byte[] byte_data = System.Text.Encoding.Default.GetBytes(data.ToCharArray());
  81. m_pClientSocket.Send(byte_data,byte_data.Length,0);
  82. }
  83. /// <summary>
  84. /// Fonction permettant de lire une commande tap閑 par le client.
  85. /// </summary>
  86. /// <param name="clientSocket">Socket du client</param>
  87. /// <param name="timeOut">Nombre de secondes maximum pour la lecture</param>
  88. /// <returns></returns>
  89. private string ReadLine(Socket clientSocket,int timeOut)
  90. {
  91. long lastDataTime   = DateTime.Now.Ticks; //date de derni鑢e r閏eption
  92. ArrayList lineBuf   = new ArrayList(); //Buffer
  93. byte      prevByte  = 0;
  94. while(true)
  95. {
  96. if(clientSocket.Available > 0)
  97. {
  98. // On lit un octet
  99. byte[] currByte = new byte[1];
  100. int countRecieved = clientSocket.Receive(currByte,1,SocketFlags.None);
  101. // Si un octet est re鐄 (parfois ?la premi鑢e tentative, le client n'envoie rien)
  102. if(countRecieved == 1)
  103. {
  104. //on stocke l'octet dans le buffer
  105. lineBuf.Add(currByte[0]);
  106. // Si on arrive en fin de ligne, on renvoie la commande
  107. if((prevByte == (byte)'r' && currByte[0] == (byte)'n'))
  108. {
  109. byte[] retVal = new byte[lineBuf.Count-2];    // On enl鑦e les caract鑢es de fin de ligne 
  110. lineBuf.CopyTo(0,retVal,0,lineBuf.Count-2);
  111. return System.Text.Encoding.Default.GetString(retVal).Trim();
  112. }
  113. // on update la valeur de prevByte
  114. prevByte = currByte[0];
  115. //On met ?jour la date de derni鑢e r閏eption
  116. lastDataTime = DateTime.Now.Ticks;
  117. }
  118. }
  119. else
  120. {
  121. //Si le Timeout est atteint on lance une excepion
  122. if(DateTime.Now.Ticks > lastDataTime + ((long)(timeOut)) * 10000)
  123. {
  124. throw new ReadException(ReadReplyCode.TimeOut,"Read timeout");
  125. }
  126. System.Threading.Thread.Sleep(100);//N閏essaire pour des raisons 関identes de performance
  127. }
  128. }
  129. }
  130. /// <summary>
  131. /// Boucle de lecture et traitement des messages du client
  132. /// </summary>
  133. public void StartProcessing()
  134. {
  135. //le serveur est pr阾
  136. this.SendData(Messages.MessReady());
  137. long lastCmdTime = DateTime.Now.Ticks; //Date de 閏eption de la derni鑢e commande
  138. string lastCmd  = "";
  139. //Tant que le session time out n'est pas atteint ou que la commande "QUIT" n'est pas demand閑, on boucle 
  140. try
  141. {
  142. while(true)
  143. {
  144. //Si des donn閑s arrivent, on commence ?les lire.
  145. if (this.m_pClientSocket.Available >0)
  146. {
  147. try
  148. {
  149. lastCmd=this.ReadLine(m_pClientSocket,this.m_pServer.SessionIdleTimeOut);//Lecture de la Commande
  150. if(!SwitchCommand(lastCmd))//Si la commande est "QUIT" on quitte la boucle de traitement
  151. {
  152. break;
  153. }
  154. }
  155. catch(ReadException rX)
  156. {
  157. //On verifie que le nombre maximum d'erreurs de commandes n'est pas atteint
  158. if(m_BadCmdCount > m_pServer.MaxBadCommands-1)
  159. {
  160. SendData(Messages.MessTooManyBadCmds());//On envoie un message
  161. break;//et on quitte la boucle de traitement
  162. }
  163. m_BadCmdCount++;
  164. switch(rX.ReadReplyCode)//En fonction du type d'ereur (pour l'instant TimeOut et Unknown) on envoie un message
  165. {
  166. case ReadReplyCode.TimeOut:
  167. SendData(Messages.CmdTimeOut());
  168. break;
  169. case ReadReplyCode.UnknownError:
  170. SendData(Messages.UnknownError());
  171. break;
  172. }
  173. }
  174. catch
  175. {
  176. if(!this.m_pClientSocket.Connected)break;//Si le socket a 閠?d閏onnect? on quitte la boucle
  177. SendData(Messages.UnknownError());//Sinon on envoie un message d'erreur inconnu
  178. }
  179. //On update la date de la derni鑢e commande
  180. lastCmdTime = DateTime.Now.Ticks;
  181. }
  182. else
  183. {
  184. //On verifie qu'on n'aie pas atteint le timeOut
  185. if(DateTime.Now.Ticks > lastCmdTime + ((long)(m_pServer.SessionIdleTimeOut)) * 10000)
  186. {
  187. // Notification de fermeture de session
  188. SendData("500 Session timeout, OK FTP server signing offrn");
  189. break;
  190. }
  191. // On attends 100 ms afin d'閏onomiser le cpu (sinon la boucle mange trop de ressources). 
  192. Thread.Sleep(100);
  193. }
  194. }
  195. //Fermeture du Socket
  196. if(m_pClientSocket.Connected)
  197. {
  198. m_pClientSocket.Close();
  199. }
  200. //Destruction de la session
  201. this.m_pServer.RemoveSession(this.m_SessionID);
  202. }
  203. catch
  204. {
  205. this.m_pServer.RemoveSession(this.m_SessionID);
  206. }
  207. }
  208. /// <summary>
  209. /// En fonction de la commande utilisateur donn閑, cette fonction appelle la fonction ad閝uate
  210. /// </summary>
  211. /// <param name="commandTxt">Commande envoy閑</param>
  212. /// <returns>True si la session doit continuer, false si elle doit s'arr阾er (commande QUIT par exemple)</returns>
  213. private bool SwitchCommand(string commandTxt)
  214. {
  215. string[] cmdParts = commandTxt.TrimStart().Split(new char[]{' '});
  216. string   command  = cmdParts[0].ToUpper().Trim();
  217. switch(command)
  218. {
  219. case "QUIT":
  220. QUIT();
  221. return false;
  222. case "USER":
  223. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  224. else
  225. {
  226. if (cmdParts.Length>2)
  227. {
  228. string args="";
  229. for(int i=1;i<cmdParts.Length;i++)
  230. {
  231. args+=cmdParts[i]+" ";
  232. }
  233. USER(args);
  234. }
  235. else
  236. {
  237. USER(cmdParts[1]);
  238. }
  239. }
  240. break;
  241. case "TYPE":
  242. if (cmdParts.Length!=2)SendData(Messages.UnknownError());
  243. else TYPE(cmdParts[1]);
  244. break;
  245. case "PASS":
  246. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  247. else
  248. {
  249. if (cmdParts.Length>2)
  250. {
  251. string args="";
  252. for(int i=1;i<cmdParts.Length;i++)
  253. {
  254. args+=cmdParts[i]+" ";
  255. }
  256. PASS(args);
  257. }
  258. else
  259. {
  260. PASS(cmdParts[1]);
  261. }
  262. }
  263. break;
  264. case "PWD":
  265. PWD();
  266. break;
  267. case "XPWD":
  268. PWD();
  269. break;
  270. case "SYST":
  271. SYST();
  272. break;
  273. case "PORT":
  274. if (cmdParts.Length!=2)SendData(Messages.UnknownError());
  275. else PORT(cmdParts[1]);
  276. break;
  277. case "PASV":
  278. PASV();
  279. break;
  280. case "LIST":
  281. if (cmdParts.Length>1){
  282. if (cmdParts[1].Trim().ToLower()=="-a"||cmdParts[1].Trim().ToLower()=="-l"||cmdParts[1].Trim().ToLower()=="-al")
  283. {
  284. string[] parts=new string[cmdParts.Length-1];
  285. parts[0]=cmdParts[0];
  286. for (int i=2;i<cmdParts.Length;i++)
  287. parts[i-1]=cmdParts[i];
  288. cmdParts=parts;
  289. }
  290. }
  291. if (cmdParts.Length>2)
  292. {
  293. string args="";
  294. for(int i=1;i<cmdParts.Length;i++)
  295. {
  296. args+=cmdParts[i]+" ";
  297. }
  298. LIST(args);
  299. }
  300. else if(cmdParts.Length==2)LIST(cmdParts[1]);
  301. else LIST(String.Empty);
  302. break;
  303. case "NLST":
  304. if (cmdParts.Length>1)
  305. {
  306. if (cmdParts[1].Trim().ToLower()=="-a"||cmdParts[1].Trim().ToLower()=="-l"||cmdParts[1].Trim().ToLower()=="-al")
  307. {
  308. string[] parts=new string[cmdParts.Length-1];
  309. parts[0]=cmdParts[0];
  310. for (int i=2;i<cmdParts.Length;i++)
  311. parts[i-1]=cmdParts[i];
  312. cmdParts=parts;
  313. }
  314. }
  315. if (cmdParts.Length>2)
  316. {
  317. string args="";
  318. for(int i=1;i<cmdParts.Length;i++)
  319. {
  320. args+=cmdParts[i]+" ";
  321. }
  322. LIST(args);
  323. }
  324. else if(cmdParts.Length==2)NLST(cmdParts[1]);
  325. else NLST(String.Empty);
  326. break;
  327. case "CWD":
  328. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  329. else
  330. {
  331. if (cmdParts.Length>2)
  332. {
  333. string args="";
  334. for(int i=1;i<cmdParts.Length;i++)
  335. {
  336. args+=cmdParts[i]+" ";
  337. }
  338. CWD(args);
  339. }
  340. else
  341. {
  342. CWD(cmdParts[1]);
  343. }
  344. }
  345. break;
  346. case "RETR":
  347. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  348. else
  349. {
  350. if (cmdParts.Length>2)
  351. {
  352. string args="";
  353. for(int i=1;i<cmdParts.Length;i++)
  354. {
  355. args+=cmdParts[i]+" ";
  356. }
  357. RETR(args);
  358. }
  359. else
  360. {
  361. RETR(cmdParts[1]);
  362. }
  363. }
  364. break;
  365. case "STOR":
  366. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  367. else
  368. {
  369. if (cmdParts.Length>2)
  370. {
  371. string args="";
  372. for(int i=1;i<cmdParts.Length;i++)
  373. {
  374. args+=cmdParts[i]+" ";
  375. }
  376. STOR(args);
  377. }
  378. else
  379. {
  380. STOR(cmdParts[1]);
  381. }
  382. }
  383. break;
  384. case "DELE":
  385. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  386. else
  387. {
  388. if (cmdParts.Length>2)
  389. {
  390. string args="";
  391. for(int i=1;i<cmdParts.Length;i++)
  392. {
  393. args+=cmdParts[i]+" ";
  394. }
  395. DELE(args);
  396. }
  397. else
  398. {
  399. DELE(cmdParts[1]);
  400. }
  401. }
  402. break;
  403. case "RMD":
  404. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  405. else
  406. {
  407. if (cmdParts.Length>2)
  408. {
  409. string args="";
  410. for(int i=1;i<cmdParts.Length;i++)
  411. {
  412. args+=cmdParts[i]+" ";
  413. }
  414. RMD(args);
  415. }
  416. else
  417. {
  418. RMD(cmdParts[1]);
  419. }
  420. }
  421. break;
  422. case "APPE":
  423. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  424. else
  425. {
  426. if (cmdParts.Length>2)
  427. {
  428. string args="";
  429. for(int i=1;i<cmdParts.Length;i++)
  430. {
  431. args+=cmdParts[i]+" ";
  432. }
  433. APPE(args);
  434. }
  435. else
  436. {
  437. APPE(cmdParts[1]);
  438. }
  439. }
  440. break;
  441. case "RNFR":
  442. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  443. else
  444. {
  445. if (cmdParts.Length>2)
  446. {
  447. string args="";
  448. for(int i=1;i<cmdParts.Length;i++)
  449. {
  450. args+=cmdParts[i]+" ";
  451. }
  452. RNFR(args);
  453. }
  454. else
  455. {
  456. RNFR(cmdParts[1]);
  457. }
  458. }
  459. break;
  460. case "RNTO":
  461. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  462. else
  463. {
  464. if (cmdParts.Length>2)
  465. {
  466. string args="";
  467. for(int i=1;i<cmdParts.Length;i++)
  468. {
  469. args+=cmdParts[i]+" ";
  470. }
  471. RNTO(args);
  472. }
  473. else
  474. {
  475. RNTO(cmdParts[1]);
  476. }
  477. }
  478. break;
  479. case "MKD":
  480. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  481. else
  482. {
  483. if (cmdParts.Length>2)
  484. {
  485. string args="";
  486. for(int i=1;i<cmdParts.Length;i++)
  487. {
  488. args+=cmdParts[i]+" ";
  489. }
  490. MKD(args);
  491. }
  492. else
  493. {
  494. MKD(cmdParts[1]);
  495. }
  496. }
  497. break;
  498. case "REST":
  499. if (cmdParts.Length<2)SendData(Messages.UnknownError());
  500. else
  501. {
  502. if (cmdParts.Length>2)
  503. {
  504. string args="";
  505. for(int i=1;i<cmdParts.Length;i++)
  506. {
  507. args+=cmdParts[i]+" ";
  508. }
  509. REST(args);
  510. }
  511. else
  512. {
  513. REST(cmdParts[1]);
  514. }
  515. }
  516. break;
  517. case "CDUP" :
  518. CWD("..");
  519. break;
  520. case "NOOP" :
  521. NOOP();
  522. break;
  523.                     
  524. default:
  525. SendData("500 Invalid command " + command + "rn");
  526. break;
  527. }
  528. return true;
  529. }
  530. /// <summary>
  531. /// Cette fonction ne fait absoluement rien
  532. /// </summary>
  533. private void NOOP()
  534. {
  535. SendData("200 OK rn");
  536. }
  537. /// <summary>
  538. /// Cr閍tion d'un nouveau r閜ertoire
  539. /// </summary>
  540. /// <param name="argsText">nom du r閜ertoire</param>
  541. private void MKD(string argsText)
  542. {
  543. if(!m_Authenticated)
  544. {
  545. SendData(Messages.AuthReq());
  546. return;
  547. }
  548. string file;
  549. string newCurdir;
  550. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir))
  551. {
  552. SendData(Messages.AccesDenied());
  553. return;
  554. }
  555. try
  556. {
  557. new DirectoryInfo(file).Create();
  558. SendData("257 Directory Created. rn");
  559. }
  560. catch
  561. {
  562. SendData(Messages.AccesDenied());
  563. return;
  564. }
  565. }
  566. /// <summary>
  567. /// Juste avant un RNTO permet de sp閏ifi?le nom du fichier ?renomer
  568. /// </summary>
  569. /// <param name="argsText"></param>
  570. private void RNFR(string argsText)
  571. {
  572. if(!m_Authenticated)
  573. {
  574. SendData(Messages.AuthReq());
  575. return;
  576. }
  577. string file;
  578. string newCurdir;
  579. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir)||!(System.IO.File.Exists(file)||System.IO.Directory.Exists(file)))
  580. {
  581. SendData(Messages.AccesDenied());
  582. return;
  583. }
  584. this.m_renameFrom=file;
  585. SendData("350 Please specify destination name.rn");
  586. }
  587. /// <summary>
  588. /// Etape suivant le RNFR
  589. /// </summary>
  590. /// <param name="argsText"></param>
  591. private void RNTO(string argsText)
  592. {
  593. if(!m_Authenticated)
  594. {
  595. SendData(Messages.AuthReq());
  596. return;
  597. }
  598. if(this.m_renameFrom.Length<1)
  599. {
  600. SendData("503 Bad sequence of commands. rn");
  601. return;
  602. }
  603. string file;
  604. string newCurdir;
  605. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir))
  606. {
  607. SendData(Messages.AccesDenied());
  608. return;
  609. }
  610. try
  611. {
  612. (new System.IO.FileInfo(this.m_renameFrom)).MoveTo(file);
  613. SendData("250 Directory renamed.rn");
  614. }
  615. catch
  616. {
  617. try
  618. {
  619. (new System.IO.DirectoryInfo(this.m_renameFrom)).MoveTo(file);
  620. SendData("250 Directory renamed.rn");
  621. }
  622. catch
  623. {
  624. SendData("550 Error renameing directory or file .rn");
  625. }
  626. }
  627. this.m_renameFrom="";
  628. }
  629. /// <summary>
  630. /// Cette m閠hode permet de d閒inir l'offset a partir duquel un fichier est t閘閏harg?
  631. /// (n閏essaire pour la fonction resume)
  632. /// </summary>
  633. /// <param name="argsText">offset</param>
  634. private void REST(string argsText)
  635. {
  636. if(!m_Authenticated)
  637. {
  638. SendData(Messages.AuthReq());
  639. return;
  640. }
  641. try
  642. {
  643. this.m_offset=Convert.ToInt32(argsText);
  644. SendData("350 Restarting at "+this.m_offset.ToString()+"rn");
  645. }
  646. catch
  647. {
  648. SendData("554 bad argument for RESTrn");
  649. }
  650. }
  651. /// <summary>
  652. /// Cette m閠hode permet ?l'utilisateur d'envoyer la "fin" d'un fichier
  653. /// </summary>
  654. /// <param name="argsText">fichier ?compl閠er</param>
  655. private void APPE(string argsText)
  656. {
  657. if(!m_Authenticated)
  658. {
  659. SendData(Messages.AuthReq());
  660. return;
  661. }
  662. string file;
  663. string newCurdir;
  664. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir))
  665. {
  666. SendData(Messages.AccesDenied());
  667. return;
  668. }
  669. FileStream fs;
  670. try
  671. {
  672. fs=new FileStream(file,FileMode.OpenOrCreate,FileAccess.Write);
  673. fs.Position=fs.Length;
  674. }
  675. catch
  676. {
  677. SendData(Messages.AccesDenied());
  678. return;
  679. }
  680. Socket socket=this.GetDataConnection();
  681. try
  682. {
  683. if(socket==null||fs==null)
  684. {
  685. throw new Exception();
  686. }
  687. int readed = 1;
  688. byte[] buffer=new byte[4096];
  689. while(readed>0)
  690. {
  691. readed=socket.Receive(buffer);
  692. fs.Write(buffer,0,readed);
  693. }
  694. socket.Shutdown(SocketShutdown.Both);
  695. socket.Close();
  696. SendData(Messages.TrComplete());
  697. }
  698. catch
  699. {
  700. SendData(Messages.TrFailed());
  701. socket.Close();
  702. }
  703. fs.Close();
  704. }
  705. /// <summary>
  706. /// Cette m閠hode permet au client de suprimer un fichier
  707. /// </summary>
  708. /// <param name="argsText">fichier ?supprimer</param>
  709. private void DELE(string argsText)
  710. {
  711. if(!m_Authenticated)
  712. {
  713. SendData(Messages.AuthReq());
  714. return;
  715. }
  716. string file;
  717. string newCurdir;
  718. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir)||!System.IO.File.Exists(file))
  719. {
  720. SendData(Messages.AccesDenied());
  721. return;
  722. }
  723. try 
  724. {
  725. new FileInfo(file).Delete();
  726. SendData(Messages.DeleOk());
  727. }
  728. catch
  729. {
  730. SendData(Messages.AccesDenied());
  731. }
  732. }
  733. /// <summary>
  734. /// Suppression d'un r閜ertoire
  735. /// </summary>
  736. /// <param name="argsText"></param>
  737. private void RMD(string argsText)
  738. {
  739. if(!m_Authenticated)
  740. {
  741. SendData(Messages.AuthReq());
  742. return;
  743. }
  744. string file;
  745. string newCurdir;
  746. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir)||!System.IO.Directory.Exists(file))
  747. {
  748. SendData(Messages.AccesDenied());
  749. return;
  750. }
  751. try 
  752. {
  753. new DirectoryInfo(file).Delete();
  754. SendData(Messages.DeleOk());
  755. }
  756. catch
  757. {
  758. SendData(Messages.AccesDenied());
  759. }
  760. }
  761. /// <summary>
  762. /// Cette m閠hode permet au client d'envoyer un fichier
  763. /// </summary>
  764. /// <param name="argsText">Nom du fichier envoy?/param>
  765. private void STOR(string argsText)
  766. {
  767. if(!m_Authenticated)
  768. {
  769. SendData(Messages.AuthReq());
  770. return;
  771. }
  772. string file;
  773. string newCurdir;
  774. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file, out newCurdir))
  775. {
  776. SendData(Messages.AccesDenied());
  777. return;
  778. }
  779. FileStream fs;
  780. try
  781. {
  782. fs=new FileStream(file,FileMode.OpenOrCreate,FileAccess.Write);
  783. fs.Position=this.m_offset;
  784. fs.SetLength(this.m_offset);
  785. }
  786. catch
  787. {
  788. SendData(Messages.AccesDenied());
  789. return;
  790. }
  791. Socket socket=this.GetDataConnection();
  792. try
  793. {
  794. if(socket==null||fs==null)
  795. {
  796. throw new Exception();
  797. }
  798. int readed = 1;
  799. byte[] buffer=new byte[4096];
  800. while(readed>0)
  801. {
  802. readed=socket.Receive(buffer);
  803. fs.Write(buffer,0,readed);
  804. }
  805. socket.Shutdown(SocketShutdown.Both);
  806. socket.Close();
  807. SendData(Messages.TrComplete());
  808. }
  809. catch
  810. {
  811. SendData(Messages.TrFailed());
  812. socket.Close();
  813. }
  814. fs.Close();
  815. }
  816. /// <summary>
  817. /// Cette m閠hode permet au client de t閘閏harger un fichier
  818. /// </summary>
  819. /// <param name="argsText">Fichier ?t閘閏harger (relatif au r閜ertoire courant)</param>
  820. private void RETR(string argsText)
  821. {
  822. if(!m_Authenticated)
  823. {
  824. SendData(Messages.AuthReq());
  825. return;
  826. }
  827. string file;
  828. string newCurdir;
  829. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,argsText,this.m_vPaths,out file,out newCurdir)||!System.IO.File.Exists(file))
  830. {
  831. SendData(Messages.AccesDenied());
  832. return;
  833. }
  834. FileStream fs;
  835. try
  836. {
  837. Socket socket=this.GetDataConnection();
  838. fs=new FileStream(file,FileMode.Open,FileAccess.Read);
  839. fs.Position=this.m_offset;
  840. if(socket==null || fs==null)
  841. {
  842. throw new Exception();
  843. }
  844. int readed = 1;
  845. byte[] buffer=new byte[4096];
  846. while(readed > 0)
  847. {
  848. readed=fs.Read(buffer,0,buffer.Length);
  849. socket.Send(buffer,readed,SocketFlags.None);
  850. }
  851. socket.Shutdown(SocketShutdown.Both);
  852. socket.Close();
  853. SendData(Messages.TrComplete());
  854. }
  855. catch
  856. {
  857. SendData(Messages.TrFailed());
  858. return;
  859. }
  860. fs.Close();
  861. }
  862. /// <summary>
  863. /// Cette m閠hode envoie un message comme quoi la connection va 阾re ferm閑 
  864. /// (elle correspond ?la commande client "QUIT")
  865. /// </summary>
  866. private void QUIT()
  867. {
  868. SendData(Messages.SignOff());
  869. }
  870. /// <summary>
  871. /// Cette m閠hode permet ?l'utilisateur de rentrer son login
  872. /// </summary>
  873. /// <param name="argsText">login</param>
  874. private void USER(string argsText)
  875. {
  876. if(m_Authenticated)
  877. {
  878. SendData(Messages.AlreadyAuth());
  879. return;
  880. }
  881. if(m_UserName.Length > 0)
  882. {
  883. SendData(Messages.UserButNotPass());
  884. return;
  885. }
  886. string userName = argsText;
  887. SendData(Messages.PassReq(argsText));
  888. m_UserName = userName;
  889. }
  890. /// <summary>
  891. /// Cette m閠hode permet ?l'utilisateur de saisir son mot de passe
  892. /// </summary>
  893. /// <param name="argsText">Mot de passe</param>
  894. private void PASS(string argsText)
  895. {
  896. if(m_Authenticated)
  897. {
  898. SendData(Messages.AlreadyAuth());
  899. return;
  900. }
  901. if(m_UserName.Length == 0)
  902. {
  903. SendData(Messages.EnterUser());
  904. return;
  905. }
  906. try
  907. {
  908. if((this.m_ftproot=Helpers.GetFtpRoot(this.m_UserName))==string.Empty)
  909. {
  910. SendData("530 Bad Home");
  911. return;
  912. }
  913. }
  914. catch(Exception e)
  915. {
  916. SendData(e.Message);
  917. }
  918. this.m_vPaths=Helpers.VPaths(this.m_UserName);
  919. // Authentification de l'utilisateur
  920. if(WinAuth.Auth(m_UserName,argsText))
  921. {
  922. m_Authenticated = true;
  923. SendData(Messages.PassOk());
  924. }
  925. else
  926. {
  927. SendData(Messages.AuthFailed());
  928. m_UserName = ""; // R閕nitialisation du nom d'utilisateur
  929. }
  930. }
  931. /// <summary>
  932. /// Informe l'utilisateur sur le r閜ertoire courrant
  933. /// </summary>
  934. private void PWD()
  935. {
  936. if(!m_Authenticated)
  937. {
  938. SendData(Messages.AuthReq());
  939. return;
  940. }
  941. SendData(Messages.Pwd(m_CurrentDir));
  942. }
  943. /// <summary>
  944. /// Informe l'utilisateur sur le syst鑝e surlequel tourne le serveur
  945. /// </summary>
  946. private void SYST()
  947. {
  948. if(!m_Authenticated)
  949. {
  950. SendData(Messages.AuthReq());
  951. return;
  952. }
  953. SendData("215 Windows_NTrn");
  954. }
  955. /// <summary>
  956. /// D閒init le type de donn閑 transitant entre client et serveur
  957. /// </summary>
  958. /// <param name="argsText">type</param>
  959. private void TYPE(string argsText)
  960. {
  961. if(!m_Authenticated)
  962. {
  963. SendData(Messages.AuthReq());
  964. return;
  965. }
  966. if(argsText.Trim().ToUpper() == "A" || argsText.Trim().ToUpper() == "I")
  967. {
  968. SendData(Messages.TypeSet(argsText));
  969. }
  970. else
  971. {
  972. SendData(Messages.InvalidType(argsText));
  973. }
  974. }
  975. /// <summary>
  976. /// La m閠hode port permet de d閒inir l'adresse TCP/IP de la connection de donn閑 avec le client.
  977. /// En mode PORT, c'est le client qui 閏oute la connection de donn閑, en mode PASV, c'est le serveur.
  978. /// </summary>
  979. /// <param name="argsText">suite de 6 octets (sous forme d閏imale) repr閟entant l'ip et le port. i1,i2,i3,i4,p1,p2  (avec p1 octet de poid fort et p2 octet de poid faible)</param>
  980. private void PORT(string argsText)
  981. {
  982. if(!m_Authenticated)
  983. {
  984. SendData(Messages.AuthReq());
  985. return;
  986. }
  987. //on s閜are d'abord les diff閞entes parties de l'adresse
  988. string[] parts = argsText.Split(',');
  989. //on v閞ifie la syntaxe
  990. if(parts.Length != 6)
  991. {
  992. SendData(Messages.SyntaxError());
  993. return;
  994. }
  995. //On d閒init l'adresse ip
  996. string ip=parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3];
  997. //le port est d閒init par un calcul au niveau bit : la premi鑢e partie est d閏al閑 de 8bits vers l gauche et addition閑 avec la deuxi鑝e partie.
  998. int    port = (Convert.ToInt32(parts[4]) << 8) | Convert.ToInt32(parts[5]);
  999. m_pDataConEndPoint = new IPEndPoint(System.Net.Dns.GetHostByAddress(ip).AddressList[0],port);
  1000. this.m_PassiveMode=false;
  1001. SendData(Messages.PortCmdSuccess());
  1002. }
  1003. /// <summary>
  1004. /// La m閠hode pasv permet de d閒inir l'adresse TCP/IP de la connection de donn閑 avec le client.
  1005. /// En mode PORT, c'est le client qui 閏oute la connection de donn閑, en mode PASV, c'est le serveur.
  1006. /// </summary>
  1007. private void PASV()
  1008. {
  1009. if(!m_Authenticated)
  1010. {
  1011. SendData(Messages.AuthReq());
  1012. return;
  1013. }
  1014. //On commence par chercher un port libre pour lancer l'閏oute
  1015. int port = 1025;
  1016. while(true)
  1017. {
  1018. try
  1019. {
  1020. m_pPassiveListener = new TcpListener(IPAddress.Any,port);
  1021. m_pPassiveListener.Start();
  1022. //Si on arrive jusqu'ici, c'est que le port est libre
  1023. break;
  1024. }
  1025. catch
  1026. {
  1027. port++;
  1028. }
  1029. }
  1030. //A partir du socket, on recr?une chaine de type i1,i2,i3,i4,p1,p2
  1031. string ip = m_pClientSocket.LocalEndPoint.ToString();
  1032. ip = ip.Substring(0,ip.IndexOf(":"));
  1033. ip=ip.Replace(".",",");
  1034. //la premi鑢e partie du port est cr殚e en d閜la鏰nt les 8 bits de poids fort vers la droite,
  1035. //la deuxi鑝e est cr閑 en ne prenant que les 8 bits de poids faible (2^8=256)
  1036. ip += "," + (port >> 8) + "," + (port & 255);
  1037. SendData(Messages.PasvCmdSuccess(ip));
  1038. m_PassiveMode = true;
  1039. }
  1040. /// <summary>
  1041. /// Retourne au client la liste du contenu du r閜ertoire courant (ou de celui donn?en argument) avec des d閠ails
  1042. /// </summary>
  1043. private void LIST(string args)
  1044. {
  1045. //l'utilisateur doit 阾re identifi?
  1046. if(!this.m_Authenticated)
  1047. {
  1048. SendData(Messages.AuthReq());
  1049. return;
  1050. }
  1051. try
  1052. {
  1053. string cdir;
  1054. string newCurdir;
  1055. //on formate le r閜ertoire voulu
  1056. //On v閞ifie la validit?du r閜ertoire et on r閏up鑢e le chemin physique que l'on teste ensuite
  1057. //la fct?IsValidDir ne v閞ifie aps la pr閟ence du r閜ertoire car elle est aussi utilis閑 pour cr閑r des fichiers ou des r閜ertoires.
  1058. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,args,this.m_vPaths,out cdir,out newCurdir)||!System.IO.Directory.Exists(cdir))
  1059. {
  1060. SendData(Messages.AccesDenied());
  1061. return;
  1062. }
  1063. //on r閏up鑢e ensuite tous les fichiers et r閜ertoires contenus ainsi que leur propri閠閟
  1064. //pour ensuite les envoyer par le sockets
  1065. ArrayList files=new ArrayList();
  1066. try
  1067. {
  1068. files.AddRange(System.IO.Directory.GetDirectories(cdir));
  1069. files.AddRange(Helpers.VPathsInDir(this.m_CurrentDir,this.m_vPaths));
  1070. files.AddRange(System.IO.Directory.GetFiles(cdir));
  1071. }
  1072. catch
  1073. {
  1074. SendData(Messages.AccesDenied());
  1075. return;
  1076. }
  1077. //On r閏up鑢e le socket de donn閑s
  1078. Socket socket=this.GetDataConnection();
  1079. string msg="";
  1080. foreach (string file in files)
  1081. {
  1082. if(System.IO.File.Exists(file))
  1083. {
  1084. /*msg+=System.IO.File.GetLastWriteTime(file).ToString("MM-dd-yy hh:mm")+" ";
  1085. msg+=new System.IO.FileInfo(file).Length.ToString()+" ";
  1086. msg+=System.IO.Path.GetFileName(file)+"rn";
  1087. */
  1088. msg+="-rwxrwxrwx    0    0    0    ";
  1089. msg+=new System.IO.FileInfo(file).Length.ToString()+" ";
  1090. msg+=MonthFromInt(System.IO.File.GetLastWriteTime(file).Month)+" "+System.IO.File.GetLastWriteTime(file).ToString("dd  hh:mm")+" ";
  1091. msg+=System.IO.Path.GetFileName(file)+"rn";
  1092. }
  1093. else if(System.IO.Directory.Exists(file))
  1094. {
  1095. /*
  1096. msg+=System.IO.Directory.GetLastWriteTime(file).ToString("MM-dd-yy hh:mm")+" ";
  1097. msg+=" <DIR> "+System.IO.Path.GetFileName(file)+"rn";
  1098. */
  1099. msg+="drwxrwxrwx    0    0    0    ";
  1100. msg+=" 4096 ";
  1101. msg+=MonthFromInt(System.IO.Directory.GetLastWriteTime(file).Month)+" "+System.IO.Directory.GetLastWriteTime(file).ToString("dd  hh:mm")+" ";
  1102. msg+=System.IO.Path.GetFileName(file)+"rn";
  1103. }
  1104. else
  1105. {
  1106. msg+="drwxrwxrwx    0    0    0    ";
  1107. msg+=" 4096 ";
  1108. msg+=MonthFromInt(System.DateTime.Now.Month)+" "+System.DateTime.Now.ToString("dd  hh:mm")+" ";
  1109. msg+=file+"rn";
  1110. }
  1111. }
  1112. byte[] toSend=System.Text.Encoding.Default.GetBytes(msg);
  1113. socket.Send(toSend);
  1114. socket.Shutdown(SocketShutdown.Both);
  1115. socket.Close();
  1116. SendData(Messages.TrComplete());
  1117. }
  1118. catch
  1119. {
  1120. SendData(Messages.TrFailed());
  1121. }
  1122. }
  1123. private string MonthFromInt(int month)
  1124. {
  1125. switch(month)
  1126. {
  1127. case 1 :
  1128. return "jan";
  1129. break;
  1130. case 2 :
  1131. return "feb";
  1132. break;
  1133. case 3 :
  1134. return "mar";
  1135. break;
  1136. case 4 :
  1137. return "apr";
  1138. break;
  1139. case 5 :
  1140. return "may";
  1141. break;
  1142. case 6 :
  1143. return "jun";
  1144. break;
  1145. case 7 :
  1146. return "jul";
  1147. break;
  1148. case 8 :
  1149. return "aug";
  1150. break;
  1151. case 9 :
  1152. return "sep";
  1153. break;
  1154. case 10 :
  1155. return "oct";
  1156. break;
  1157. case 11 :
  1158. return "nov";
  1159. break;
  1160. case 12 :
  1161. return "dec";
  1162. break;
  1163. default :
  1164. return "";
  1165. }
  1166. }
  1167. /// <summary>
  1168. /// Retourne au client la liste du contenu du r閜ertoire courant (ou de celui donn?en argument) sans d閠ails
  1169. /// </summary>
  1170. private void NLST(string args)
  1171. {
  1172. //l'utilisateur doit 阾re identifi?
  1173. if(!this.m_Authenticated)
  1174. {
  1175. SendData(Messages.AuthReq());
  1176. return;
  1177. }
  1178. try
  1179. {
  1180. string cdir;
  1181. string newCurdir;
  1182. //on formate le r閜ertoire voulu
  1183. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,args,this.m_vPaths,out cdir, out newCurdir)||!System.IO.Directory.Exists(cdir))
  1184. {
  1185. SendData(Messages.AccesDenied());
  1186. return;
  1187. }
  1188. ArrayList files=new ArrayList();
  1189. try
  1190. {
  1191. files.AddRange(System.IO.Directory.GetDirectories(cdir));
  1192. files.AddRange(Helpers.VPathsInDir(this.m_CurrentDir,this.m_vPaths));
  1193. files.AddRange(System.IO.Directory.GetFiles(cdir));
  1194. }
  1195. catch
  1196. {
  1197. SendData(Messages.AccesDenied());
  1198. return;
  1199. }
  1200. //On r閏up鑢e le socket de donn閑s
  1201. Socket socket=this.GetDataConnection();
  1202. string msg="";
  1203. foreach (string file in files)
  1204. {
  1205. if(System.IO.File.Exists(file))
  1206. {
  1207. msg+=System.IO.Path.GetFileName(file)+"rn";
  1208. }
  1209. else if(System.IO.Directory.Exists(file))
  1210. {
  1211. msg+=System.IO.Path.GetFileName(file)+"rn";
  1212. }
  1213. else
  1214. {
  1215. msg+=file+"rn";
  1216. }
  1217. }
  1218. byte[] toSend=System.Text.Encoding.Default.GetBytes(msg);
  1219. socket.Send(toSend);
  1220. socket.Shutdown(SocketShutdown.Both);
  1221. socket.Close();
  1222. SendData(Messages.TrComplete());
  1223. }
  1224. catch
  1225. {
  1226. SendData(Messages.TrFailed());
  1227. }
  1228. }
  1229. /// <summary>
  1230. /// Changement de r閜ertoire courrant
  1231. /// </summary>
  1232. /// <param name="args">R閜ertoire</param>
  1233. private void CWD(string args)
  1234. {
  1235. try
  1236. {
  1237. if(!this.m_Authenticated)
  1238. {
  1239. SendData(Messages.AuthReq());
  1240. return;
  1241. }
  1242. string cdir;
  1243. string newCurdir;
  1244. if(!Helpers.IsValidDir(this.m_ftproot,this.m_CurrentDir,args.Trim(),this.m_vPaths,out cdir, out newCurdir)||
  1245. !System.IO.Directory.Exists(cdir))
  1246. {
  1247. SendData(Messages.AccesDenied());
  1248. return;
  1249. }
  1250. this.m_CurrentDir=newCurdir;
  1251. SendData(Messages.CwdOk());
  1252. }
  1253. catch
  1254. {
  1255. SendData(Messages.AccesDenied());
  1256. }
  1257. }
  1258. /// <summary>
  1259. /// Fonction permettant d'obtenir le socket de donn閑
  1260. /// </summary>
  1261. /// <returns>Socket de donn閑s</returns>
  1262. private Socket GetDataConnection()
  1263. {
  1264. Socket socket = null;
  1265. try
  1266. {
  1267. //si on est en mode passif
  1268. if(m_PassiveMode)
  1269. {
  1270. long startTime = DateTime.Now.Ticks;
  1271. // On attend que le server est re鐄 une requ鑤e de connection
  1272. while(!m_pPassiveListener.Pending())
  1273. {
  1274. System.Threading.Thread.Sleep(50);
  1275. // Time out apr鑣 30 secondes
  1276. if((DateTime.Now.Ticks - startTime) / 10000 > 20000)
  1277. {
  1278. throw new Exception("Ftp server didn't respond !");
  1279. }
  1280. }
  1281. //On accepte le socket
  1282. socket = m_pPassiveListener.AcceptSocket();
  1283. SendData(Messages.DataOpen());
  1284. }
  1285. else //Si en mode actif
  1286. {
  1287. SendData(Messages.DataOpening());
  1288. socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
  1289. socket.Connect(m_pDataConEndPoint);
  1290. }
  1291. }
  1292. catch
  1293. {
  1294. SendData("425 Can't open data connection.rn");
  1295. return null;
  1296. }
  1297. m_PassiveMode = false;
  1298. return socket;
  1299. }
  1300. }
  1301. }