New test.
[mono.git] / mcs / class / System / System.Net / FtpWebRequest.cs
1 //
2 // System.Net.FtpWebRequest.cs
3 //
4 // Authors:
5 //      Carlos Alberto Cortez (calberto.cortez@gmail.com)
6 //
7 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
8 //
9
10 using System;
11 using System.IO;
12 using System.Net.Sockets;
13 using System.Text;
14 using System.Threading;
15 #if NET_2_0
16 using System.Net.Cache;
17 #endif
18
19 #if NET_2_0
20
21 namespace System.Net
22 {
23         [Serializable]
24         public class FtpWebRequest : WebRequest
25         {
26                 Uri requestUri;
27                 ServicePoint servicePoint;
28                 Socket dataSocket;
29                 NetworkStream controlStream;
30                 StreamReader controlReader;
31                 NetworkCredential credentials;
32                 IPHostEntry hostEntry;
33                 IPEndPoint localEndPoint;
34                 IWebProxy proxy;
35                 int timeout = 100000;
36                 int rwTimeout = 300000;
37                 long offset;
38                 bool binary = true;
39                 bool enableSsl;
40                 bool requestInProgress;
41                 bool usePassive = true;
42                 bool keepAlive = true;
43                 bool aborted;
44                 bool transferCompleted;
45                 bool gotRequestStream;
46                 string method = WebRequestMethods.Ftp.DownloadFile;
47                 string renameTo;
48                 object locker = new object ();
49
50                 FtpStatusCode statusCode;
51                 string statusDescription = String.Empty;
52
53                 FtpAsyncResult asyncRead;
54                 FtpAsyncResult asyncWrite;
55
56                 FtpWebResponse ftpResponse;
57                 Stream requestStream = Stream.Null;
58
59                 const string UserCommand = "USER";
60                 const string PasswordCommand = "PASS";
61                 const string TypeCommand = "TYPE";
62                 const string PassiveCommand = "PASV";
63                 const string PortCommand = "PORT";
64                 const string AbortCommand = "ABOR";
65                 const string AuthCommand = "AUTH";
66                 const string RestCommand = "REST";
67                 const string RenameFromCommand = "RNFR";
68                 const string RenameToCommand = "RNTO";
69                 const string EOL = "\r\n"; // Special end of line
70
71                 // sorted commands
72                 static readonly string [] supportedCommands = new string [] {
73                         WebRequestMethods.Ftp.AppendFile, // APPE
74                         WebRequestMethods.Ftp.DeleteFile, // DELE
75                         WebRequestMethods.Ftp.ListDirectoryDetails, // LIST
76                         WebRequestMethods.Ftp.GetDateTimestamps, // MDTM
77                         WebRequestMethods.Ftp.MakeDirectory, // MKD
78                         WebRequestMethods.Ftp.ListDirectory, // NLST
79                         WebRequestMethods.Ftp.PrintWorkingDirectory, // PWD
80                         WebRequestMethods.Ftp.Rename, // RENAME
81                         WebRequestMethods.Ftp.DownloadFile, // RETR
82                         WebRequestMethods.Ftp.RemoveDirectory, // RMD
83                         WebRequestMethods.Ftp.GetFileSize, // SIZE
84                         WebRequestMethods.Ftp.UploadFile, // STOR
85                         WebRequestMethods.Ftp.UploadFileWithUniqueName // STUR
86                         };
87
88                 internal FtpWebRequest (Uri uri) 
89                 {
90                         this.requestUri = uri;
91                         this.proxy = GlobalProxySelection.Select;
92                 }
93
94                 public override string ContentType {
95                         get {
96                                 throw new NotSupportedException ();
97                         }
98                         set {
99                                 throw new NotSupportedException ();
100                         }
101                 }
102
103                 public override long ContentLength {
104                         get {
105                                 return 0;
106                         } 
107                         set {
108                                 // DO nothing
109                         }
110                 }
111
112                 public long ContentOffset {
113                         get {
114                                 return offset;
115                         }
116                         set {
117                                 CheckRequestStarted ();
118                                 if (value < 0)
119                                         throw new ArgumentOutOfRangeException ();
120
121                                 offset = value;
122                         }
123                 }
124
125                 public override ICredentials Credentials {
126                         get {
127                                 return credentials;
128                         }
129                         set {
130                                 CheckRequestStarted ();
131                                 if (value == null)
132                                         throw new ArgumentNullException ();
133                                 if (!(value is NetworkCredential))
134                                         throw new ArgumentException ();
135
136                                 credentials = value as NetworkCredential;
137                         }
138                 }
139
140                 public bool EnableSsl {
141                         get {
142                                 return enableSsl;
143                         }
144                         set {
145                                 CheckRequestStarted ();
146                                 enableSsl = value;
147                         }
148                 }
149
150                 public bool KeepAlive {
151                         get {
152                                 return keepAlive;
153                         }
154                         set {
155                                 CheckRequestStarted ();
156                                 keepAlive = value;
157                         }
158                 }
159
160                 public override string Method {
161                         get {
162                                 return method;
163                         }
164                         set {
165                                 CheckRequestStarted ();
166                                 if (value == null)
167                                         throw new ArgumentNullException ("method");
168
169                                 if (value.Length == 0 || Array.BinarySearch (supportedCommands, value) < 0)
170                                         throw new ArgumentException ("Method not supported", "value");
171                                 
172                                 method = value;
173                         }
174                 }
175
176                 public override bool PreAuthenticate {
177                         get {
178                                 throw new NotSupportedException ();
179                         }
180                         set {
181                                 throw new NotSupportedException ();
182                         }
183                 }
184
185                 public override IWebProxy Proxy {
186                         get {
187                                 return proxy;
188                         }
189                         set {
190                                 CheckRequestStarted ();
191                                 if (value == null)
192                                         throw new ArgumentNullException ();
193
194                                 proxy = value;
195                         }
196                 }
197
198                 public int ReadWriteTimeout {
199                         get {
200                                 return rwTimeout;
201                         }
202                         set {
203                                 CheckRequestStarted ();
204
205                                 if (value < - 1)
206                                         throw new ArgumentOutOfRangeException ();
207                                 else
208                                         rwTimeout = value;
209                         }
210                 }
211
212                 public string RenameTo {
213                         get {
214                                 return renameTo;
215                         }
216                         set {
217                                 CheckRequestStarted ();
218                                 if (value == null || value.Length == 0)
219                                         throw new ArgumentException ("RenameTo value can't be null or empty", "RenameTo");
220
221                                 renameTo = value;
222                         }
223                 }
224
225                 public override Uri RequestUri {
226                         get {
227                                 return requestUri;
228                         }
229                 }
230
231                 public ServicePoint ServicePoint {
232                         get {
233                                 return GetServicePoint ();
234                         }
235                 }
236
237                 public bool UsePassive {
238                         get {
239                                 return usePassive;
240                         }
241                         set {
242                                 CheckRequestStarted ();
243                                 usePassive = value;
244                         }
245                 }
246                 
247                 public bool UseBinary {
248                         get {
249                                 return binary;
250                         } set {
251                                 CheckRequestStarted ();
252                                 binary = value;
253                         }
254                 }
255
256                 public override int Timeout {
257                         get {
258                                 return timeout;
259                         }
260                         set {
261                                 CheckRequestStarted ();
262
263                                 if (value < -1)
264                                         throw new ArgumentOutOfRangeException ();
265                                 else
266                                         timeout = value;
267                         }
268                 }
269
270                 string DataType {
271                         get {
272                                 return binary ? "I" : "A";
273                         }
274                 }
275
276                 ServicePoint GetServicePoint ()
277                 {
278                         if (servicePoint == null)
279                                 servicePoint = ServicePointManager.FindServicePoint (requestUri, proxy);
280
281                         return servicePoint;
282                 }
283
284                 // Probably move some code of command connection here
285                 bool ResolveHost ()
286                 {
287                         hostEntry = GetServicePoint ().HostEntry;
288                         if (hostEntry == null)
289                                 return false;
290                         
291                         return true;
292                 }
293
294                 public override void Abort ()
295                 {
296                         FtpStatusCode status = SendCommand (AbortCommand);
297                         if (status != FtpStatusCode.ClosingData)
298                                 throw CreateExceptionFromResponse (); // Probably ignore it by now
299
300                         aborted = true;
301                         if (asyncRead != null) {
302                                 FtpAsyncResult r = asyncRead;
303                                 WebException wexc = new WebException ("Request aborted", WebExceptionStatus.RequestCanceled);
304                                 r.SetCompleted (false, wexc);
305                                 r.DoCallback ();
306                                 asyncRead = null;
307                         }
308                         if (asyncWrite != null) {
309                                 FtpAsyncResult r = asyncWrite;
310                                 WebException wexc = new WebException ("Request aborted", WebExceptionStatus.RequestCanceled);
311                                 r.SetCompleted (false, wexc);
312                                 r.DoCallback ();
313                                 asyncWrite = null;
314                         }
315                 }
316
317                 void ProcessRequest ()
318                 {
319                         ftpResponse = new FtpWebResponse (requestUri, method, keepAlive);
320
321                         if (!ResolveHost ()) {
322                                 SetResponseError (new WebException ("The remote server name could not be resolved: " + requestUri,
323                                                 null, WebExceptionStatus.NameResolutionFailure, ftpResponse));
324                                 return;
325                         }
326                         
327                         if (!OpenControlConnection ())
328                                 return;
329
330                         switch (method) {
331                                 // Open data connection and receive data
332                                 case WebRequestMethods.Ftp.DownloadFile:
333                                 case WebRequestMethods.Ftp.ListDirectory:
334                                 case WebRequestMethods.Ftp.ListDirectoryDetails:
335                                         DownloadData ();
336                                         break;
337                                 // Open data connection and send data
338                                 case WebRequestMethods.Ftp.AppendFile:
339                                 case WebRequestMethods.Ftp.UploadFile:
340                                 case WebRequestMethods.Ftp.UploadFileWithUniqueName:
341                                         UploadData ();
342                                         break;
343                                 // Get info from control connection
344                                 case WebRequestMethods.Ftp.GetFileSize:
345                                 case WebRequestMethods.Ftp.GetDateTimestamps:
346                                         GetInfoFromControl ();
347                                         break;
348                                 case WebRequestMethods.Ftp.Rename:
349                                         RenameFile ();
350                                         break;
351                                 case WebRequestMethods.Ftp.MakeDirectory:
352                                         ProcessSimpleRequest ();
353                                         break;
354                                 default: // What to do here?
355                                         throw new Exception ("Support for command not implemented yet");
356                         }
357                 }
358
359                 // Currently I use this only for MKD 
360                 // (Commands that don't need any parsing in command connection
361                 // for open data connection)
362                 void ProcessSimpleRequest ()
363                 {
364                         if (SendCommand (method, requestUri.LocalPath) != FtpStatusCode.PathnameCreated) {
365                                 asyncRead.SetCompleted (true, CreateExceptionFromResponse ());
366                                 return;
367                         }
368
369                         asyncRead.SetCompleted (true, ftpResponse);
370                 }
371
372                 // It would be good to have a SetCompleted method for
373                 // settting asyncRead as completed (some code is here and there, repeated)
374                 void GetInfoFromControl ()
375                 {
376                         FtpStatusCode status = SendCommand (method, requestUri.LocalPath);
377                         if (status != FtpStatusCode.FileStatus) {
378                                 asyncRead.SetCompleted (true, CreateExceptionFromResponse ());
379                                 return;
380                         }
381
382                         string desc = statusDescription;
383                         Console.WriteLine ("Desc = " + desc);
384                         if (method == WebRequestMethods.Ftp.GetFileSize) {
385                                 int i, len;
386                                 long size;
387                                 for (i = 4, len = 0; i < desc.Length && Char.IsDigit (desc [i]); i++, len++)
388                                         ;
389
390                                 if (len == 0) {
391                                         asyncRead.SetCompleted (true, new WebException ("Bad format for server response in " + method));
392                                         return;
393                                 }
394
395                                 if (!Int64.TryParse (desc.Substring (4, len), out size)) {
396                                         asyncRead.SetCompleted (true, new WebException ("Bad format for server response in " + method));
397                                         return;
398                                 }
399
400                                 ftpResponse.contentLength = size;
401                                 asyncRead.SetCompleted (true, ftpResponse);
402                                 return;
403                         }
404                         
405                         if (method == WebRequestMethods.Ftp.GetDateTimestamps) {
406                                 // Here parse the format the date time (different formats)
407                                 asyncRead.SetCompleted (true, ftpResponse);
408                                 return;
409                         }
410
411                         throw new Exception ("You shouldn't reach this point");
412                 }
413
414                 void RenameFile ()
415                 {
416                         FtpStatusCode status = SendCommand (RenameFromCommand, requestUri.LocalPath);
417                         if (status == FtpStatusCode.FileCommandPending) {
418                                 // Pass an empty string if RenameTo wasn't specified
419                                 status = SendCommand (RenameToCommand, renameTo != null ? renameTo : String.Empty);
420                                 
421                                 if (status == FtpStatusCode.FileActionOK) {
422                                         ftpResponse.UpdateStatus (statusCode, statusDescription);
423                                         asyncRead.SetCompleted (true, ftpResponse);
424                                         return;
425                                 }
426                         }
427
428                         ftpResponse.UpdateStatus (statusCode, statusDescription);
429                         asyncRead.SetCompleted (true, CreateExceptionFromResponse ());
430                 }
431
432                 void UploadData ()
433                 {
434                         if (gotRequestStream) {
435                                 if (GetResponseCode () != FtpStatusCode.ClosingData)
436                                         asyncRead.SetCompleted (true, CreateExceptionFromResponse ());
437                                 
438                                 return;
439                         }
440                         
441                         if (!OpenDataConnection ())
442                                 return;
443
444                         gotRequestStream = true;
445                         requestStream = new FtpDataStream (this, dataSocket, false);
446                         asyncWrite.SetCompleted (true, requestStream);
447                 }
448
449                 void DownloadData ()
450                 {
451                         FtpStatusCode status;
452
453                         // Handle content offset
454                         if (offset > 0) {
455                                 status = SendCommand (RestCommand, offset.ToString ());
456                                 if (status != FtpStatusCode.FileCommandPending) {
457                                         asyncRead.SetCompleted (true, CreateExceptionFromResponse ());
458                                         return;
459                                 }
460                         }
461
462                         if (!OpenDataConnection ())
463                                 return;
464
465                         ftpResponse.Stream = new FtpDataStream (this, dataSocket, true);
466                         ftpResponse.StatusDescription = statusDescription;
467                         ftpResponse.StatusCode = statusCode;
468                         asyncRead.SetCompleted (true, ftpResponse);
469                 }
470
471                 public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
472                 {
473                         if (aborted)
474                                 throw new WebException ("Request was previously aborted.");
475                         
476                         Monitor.Enter (this);
477                         if (asyncRead != null) {
478                                 Monitor.Exit (this);
479                                 throw new InvalidOperationException ();
480                         }
481
482                         requestInProgress = true;
483                         asyncRead = new FtpAsyncResult (callback, state);
484                         Thread thread = new Thread (ProcessRequest);
485                         thread.Start ();
486
487                         Monitor.Exit (this);
488                         return asyncRead;
489                 }
490
491                 public override WebResponse EndGetResponse (IAsyncResult asyncResult)
492                 {
493                         if (asyncResult == null)
494                                 throw new ArgumentNullException ("asyncResult");
495
496                         if (!(asyncResult is FtpAsyncResult) || asyncResult != asyncRead)
497                                 throw new ArgumentException ("asyncResult");
498
499                         FtpAsyncResult asyncFtpResult = (FtpAsyncResult) asyncResult;
500                         if (!asyncFtpResult.WaitUntilComplete (timeout, false)) {
501                                 Abort ();
502                                 throw new WebException ("Transfer timed out.", WebExceptionStatus.Timeout);
503                         }
504
505                         if (asyncFtpResult.GotException)
506                                 throw asyncFtpResult.Exception;
507
508                         return asyncFtpResult.Response;
509                 }
510
511                 public override WebResponse GetResponse ()
512                 {
513                         IAsyncResult asyncResult = BeginGetResponse (null, null);
514                         return EndGetResponse (asyncResult);
515                 }
516
517                 public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state)
518                 {
519                         if (aborted)
520                                 throw new WebException ("Request was previously aborted.");
521                         
522                         if (method != WebRequestMethods.Ftp.UploadFile && method != WebRequestMethods.Ftp.UploadFileWithUniqueName &&
523                                         method != WebRequestMethods.Ftp.AppendFile)
524                                 throw new ProtocolViolationException ();
525
526                         lock (locker) {
527                                 if (asyncWrite != null || asyncRead != null)
528                                         throw new InvalidOperationException ();
529                                 
530                                 requestInProgress = true;
531                                 asyncWrite = new FtpAsyncResult (callback, state);
532                                 Thread thread = new Thread (ProcessRequest);
533                                 thread.Start ();
534
535                                 return asyncWrite;
536                         }
537                 }
538
539                 public override Stream EndGetRequestStream (IAsyncResult asyncResult)
540                 {
541                         if (asyncResult == null)
542                                 throw new ArgumentNullException ("asyncResult");
543
544                         if (!(asyncResult is FtpAsyncResult))
545                                 throw new ArgumentException ("asyncResult");
546
547                         FtpAsyncResult res = (FtpAsyncResult) asyncResult;
548                         if (!res.WaitUntilComplete (timeout, false)) {
549                                 Abort ();
550                                 throw new WebException ("Request timeod out");
551                         }
552
553                         if (res.GotException)
554                                 throw res.Exception;
555
556                         return res.Stream;
557                 }
558
559                 public override Stream GetRequestStream ()
560                 {
561                         IAsyncResult asyncResult = BeginGetRequestStream (null, null);
562                         return EndGetRequestStream (asyncResult);
563                 }
564
565                 void CheckRequestStarted ()
566                 {
567                         if (requestInProgress)
568                                 throw new InvalidOperationException ("request in progress");
569                 }
570
571                 bool OpenControlConnection ()
572                 {
573                         Socket sock = null;
574                         foreach (IPAddress address in hostEntry.AddressList) {
575                                 sock = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
576                                 try {
577                                         sock.Connect (new IPEndPoint (address, requestUri.Port));
578                                         localEndPoint = (IPEndPoint) sock.LocalEndPoint;
579                                         break;
580                                 } catch (SocketException e) {
581                                         sock.Close ();
582                                         sock = null;
583                                 }
584                         }
585
586                         // Couldn't connect to any address
587                         if (sock == null) {
588                                 SetResponseError (new WebException ("Unable to connect to remote server", null, 
589                                                 WebExceptionStatus.UnknownError, ftpResponse));
590                                 return false;
591                         }
592
593                         controlStream = new NetworkStream (sock);
594                         controlReader = new StreamReader (controlStream, Encoding.ASCII);
595
596                         if (!Authenticate ()) {
597                                 SetResponseError (CreateExceptionFromResponse ());
598                                 return false;
599                         }
600
601                         return true;
602                 }
603
604                 // Probably we could do better having here a regex
605                 Socket SetupPassiveConnection ()
606                 {
607                         // Current response string
608                         string response = statusDescription;
609                         if (response.Length < 4)
610                                 return null;
611                         
612                         // Look for first digit after code
613                         int i;
614                         for (i = 3; i < response.Length && !Char.IsDigit (response [i]); i++)
615                                 ;
616                         if (i >= response.Length)
617                                 return null;
618
619                         // Get six elements
620                         string [] digits = response.Substring (i).Split (new char [] {','}, 6);
621                         if (digits.Length != 6)
622                                 return null;
623
624                         // Clean non-digits at the end of last element
625                         int j;
626                         for (j = digits [5].Length - 1; j >= 0 && !Char.IsDigit (digits [5][j]); j--)
627                                 ;
628                         if (j < 0)
629                                 return null;
630                         
631                         digits [5] = digits [5].Substring (0, j + 1);
632
633                         IPAddress ip;
634                         try {
635                                 ip = IPAddress.Parse (String.Join (".", digits, 0, 4));
636                         } catch (FormatException) {
637                                 return null;
638                         }
639
640                         // Get the port
641                         int p1, p2, port;
642                         if (!Int32.TryParse (digits [4], out p1) || !Int32.TryParse (digits [5], out p2))
643                                 return null;
644
645                         port = (p1 << 8) + p2; // p1 * 256 + p2
646                         //port = p1 * 256 + p2;
647                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
648                                 return null;
649
650                         IPEndPoint ep = new IPEndPoint (ip, port);
651                         Socket sock = new Socket (ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
652                         try {
653                                 sock.Connect (ep);
654                         } catch (SocketException exc) {
655                                 sock.Close ();
656                                 return null;
657                         }
658
659                         return sock;
660                 }
661
662                 Exception CreateExceptionFromResponse ()
663                 {
664                         WebException exc = new WebException ("Server returned an error: " + statusDescription, null, 
665                                         WebExceptionStatus.ProtocolError, ftpResponse);
666                         return exc;
667                 }
668                 
669                 // Here we could also get a server error, so be cautious
670                 internal void SetTransferCompleted ()
671                 {
672                         if (transferCompleted)
673                                 return;
674                         
675                         transferCompleted = true;
676                         
677                         FtpStatusCode status = GetResponseCode ();
678                         ftpResponse.StatusCode = status;
679                         ftpResponse.StatusDescription = statusDescription;
680                 }
681
682                 internal void SetResponseError (Exception exc)
683                 {
684                         FtpAsyncResult ar = asyncRead;
685                         if (ar == null)
686                                 ar = asyncWrite;
687
688                         ar.SetCompleted (true, exc);
689                         ar.DoCallback ();
690                 }
691
692                 Socket InitDataConnection ()
693                 {
694                         FtpStatusCode status;
695                         
696                         if (usePassive) {
697                                 status = SendCommand (PassiveCommand);
698                                 if (status != FtpStatusCode.EnteringPassive) {
699                                         SetResponseError (CreateExceptionFromResponse ());
700                                         return null;
701                                 }
702                                 
703                                 Socket retval = SetupPassiveConnection ();
704                                 if (retval == null)
705                                         SetResponseError (new WebException ("Couldn't setup passive connection"));
706                                         
707                                 return retval;
708                         }
709
710                         // Open a socket to listen the server's connection
711                         Socket sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
712                         try {
713                                 sock.Bind (new IPEndPoint (localEndPoint.Address, 0));
714                                 sock.Listen (1); // We only expect a connection from server
715
716                         } catch (SocketException e) {
717                                 sock.Close ();
718
719                                 SetResponseError (new WebException ("Couldn't open listening socket on client", e));
720                                 return null;
721                         }
722
723                         IPEndPoint ep = (IPEndPoint) sock.LocalEndPoint;
724                         string ipString = ep.Address.ToString ().Replace (".", ",");
725                         int h1 = ep.Port >> 8; // ep.Port / 256
726                         int h2 = ep.Port % 256;
727
728                         string portParam = ipString + "," + h1 + "," + h2;
729                         status = SendCommand (PortCommand, portParam);
730                         if (status != FtpStatusCode.CommandOK) {
731                                 sock.Close ();
732                                 
733                                 SetResponseError (CreateExceptionFromResponse ());
734                                 return null;
735                         }
736
737                         return sock;
738                 }
739
740                 bool OpenDataConnection ()
741                 {
742                         FtpStatusCode status;
743                         Socket s = InitDataConnection ();
744                         if (s == null)
745                                 return false;
746
747                         // TODO - Check that this command is only used for data connection based commands
748                         if (method != WebRequestMethods.Ftp.ListDirectory && method != WebRequestMethods.Ftp.ListDirectoryDetails) {
749                                 status = SendCommand (TypeCommand, DataType);
750                                 
751                                 if (status != FtpStatusCode.CommandOK) {
752                                         SetResponseError (CreateExceptionFromResponse ());
753                                         return false;
754                                 }
755                         }
756
757                         status = SendCommand (method, requestUri.LocalPath);
758                         if (status != FtpStatusCode.OpeningData) {
759                                 SetResponseError (CreateExceptionFromResponse ());
760                                 return false;
761                         }
762                         
763                         if (usePassive) {
764                                 dataSocket = s;
765                                 return true;
766                         }
767
768                         // Active connection (use Socket.Blocking to true)
769                         Socket incoming = null;
770                         try {
771                                 incoming = s.Accept ();
772                         } catch (SocketException e) {
773                                 s.Close ();
774                                 if (incoming != null)
775                                         incoming.Close ();
776                                 
777                                 SetResponseError (new ProtocolViolationException ("Server commited a protocol violation."));
778                                 return false;
779                         } 
780
781                         s.Close ();
782                         dataSocket = incoming;
783                         return true;
784                 }
785
786                 // Take in count 'account' case
787                 bool Authenticate ()
788                 {
789                         string username = null;
790                         string password = null;
791                         
792                         if (credentials != null) {
793                                 username = credentials.UserName;
794                                 password = credentials.Password;
795                                 // account = credentials.Domain;
796                         }
797
798                         if (username == null)
799                                 username = "anonymous";
800                         if (password == null)
801                                 password = "@anonymous";
802
803                         // Connect to server and get banner message
804                         FtpStatusCode status = GetResponseCode ();
805                         ftpResponse.BannerMessage = statusDescription;
806                         if (status != FtpStatusCode.SendUserCommand)
807                                 return false;
808
809                         status = SendCommand (UserCommand, username);
810                         if (status == FtpStatusCode.LoggedInProceed) {
811                                 ftpResponse.WelcomeMessage = statusDescription;
812                                 return true;
813                         }
814                         if (status == FtpStatusCode.SendPasswordCommand) {
815                                 status = SendCommand (PasswordCommand, password);
816                                 if (status == FtpStatusCode.LoggedInProceed) {
817                                         ftpResponse.WelcomeMessage = statusDescription;
818                                         return true;
819                                 }
820
821                                 return false;
822                         }
823
824                         return false;
825                 }
826
827                 FtpStatusCode SendCommand (string command, params string [] parameters)
828                 {
829                         byte [] cmd;
830                         string commandString = command;
831                         if (parameters.Length > 0)
832                                 commandString += " " + String.Join (" ", parameters);
833
834                         commandString += EOL;
835                         cmd = Encoding.ASCII.GetBytes (commandString);
836                         try {
837                                 controlStream.Write (cmd, 0, cmd.Length);
838                         } catch (IOException) {
839                                 //controlStream.Close ();
840                                 return FtpStatusCode.ServiceNotAvalaible;
841                         }
842
843                         return GetResponseCode ();
844                 }
845
846                 internal FtpStatusCode GetResponseCode ()
847                 {
848                         string responseString = null;
849                         try {
850                                 responseString = controlReader.ReadLine ();
851                         } catch (IOException exc) {
852                                 // controlReader.Close ();
853                         }
854
855                         if (responseString == null || responseString.Length < 3)
856                                 return FtpStatusCode.ServiceNotAvalaible;
857
858                         string codeString = responseString.Substring (0, 3);
859                         int code;
860                         if (!Int32.TryParse (codeString, out code))
861                                 return FtpStatusCode.ServiceNotAvalaible;
862
863                         statusDescription = responseString;
864                         return statusCode = (FtpStatusCode) code;
865                 }
866
867         }
868 }
869
870 #endif
871