Ftp, problem z pobieraniem [Delphi]

0

Witam,
wprowadzono zmiany na ftp.bossa.pl
Teraz trzeba się logować [anonymous/anonymous].
Okazuje się, że część programów sobie z tym nie radzi np. firefox, mój program również ma problemy z zalogowaniem. Google chrome, total commander nie mają żadnych problemów.
Bardzo proszę o pomoc co robię źle - poniżej kod.
Problem występuje na etapie funkcji InternetConnect.
Niestety nie umiem obsłużyć w delphi InternetGetLastResponseInfo aby dowiedzieć się co jest nie tak :/.
Proszę o jakieś wskazówki. Z góry uprzejmie dziękuję.

 

procedure TForm1.PobierzPlikFTP(ftpDir, ftpFile, TargetFile: string);
const
  READ_BUFFERSIZE                 = 4096;
  strHost                         = 'ftp.bossa.pl';
  port                            = 21;
  strUser                         = 'anonymous';
  strPwd                          = 'anonymous';

var
  hNet, hFTP, hFile               : HINTERNET;
  buffer                          : array [0..READ_BUFFERSIZE - 1] of Char;
  bufsize, dwBytesRead, fileSize  : DWORD;
  sRec                            : TWin32FindData;
  LocalFile                       : file;
  bSuccess                        : Boolean;


begin
  dwBytesRead:= 0;

  { Open an internet session }
  hNet := InternetOpen('Moj program', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  { See if connection handle is valid }
  if hNet = nil then
  begin
    ShowMessage('Brak dostępu do: WinInet.Dll');
    Exit;
  end;
  { Connect to the FTP Server }
  hFTP := InternetConnect(hNet, PWideChar(strHost), port, PWideChar(StrUser), PWideChar(strPwd), INTERNET_SERVICE_FTP, 0, 0);
  if hFTP = nil then
  begin
    InternetCloseHandle(hNet);
    ShowMessage(Format('Host: "%s" jest niedostępny',[strHost]));
    Exit;
  end;

  { Change directory }
  bSuccess := FtpSetCurrentDirectory(hFTP, PChar(ftpDir));

  if not bSuccess then
  begin
    InternetCloseHandle(hFTP);
    InternetCloseHandle(hNet);
    ShowMessage(Format('Brak folderu: %s.',[ftpDir]));
    Exit;
  end;

  { Read size of file }
  if FtpFindFirstFile(hFTP, PChar(ftpFile), sRec, 0, 0) <> nil then
  begin
    //ponizej zly sposob pobierania wielkosci pliku !!!
    //fileSize := sRec.nFileSizeLow;
    //Label2.Caption:= DateTimeToStr(FileTimeToDateTime(sRec.ftlastWriteTime));
  end else
  begin
    InternetCloseHandle(hFTP);
    InternetCloseHandle(hNet);
    ShowMessage(Format('Brak pliku: %s',[ftpFile]));
    Exit;
  end;

  { Open the file }
  hFile := FtpOpenFile(hFTP, PChar(ftpFile), GENERIC_READ, FTP_TRANSFER_TYPE_BINARY, 0);

  if hFile = nil then
  begin
    InternetCloseHandle(hFTP);
    InternetCloseHandle(hNet);
    Exit;
  end;

  fileSize:= FtpGetFileSize(hFile, @dwBytesRead);

  { Create a new local file }
  AssignFile(LocalFile, TargetFile);
  {$i-}
  Rewrite(LocalFile, 1);
  {$i+}

  if IOResult <> 0 then
  begin
    ShowMessage(Format('Nie można utworzyc pliku: %s.',[TargetFile]));
    InternetCloseHandle(hFile);
    InternetCloseHandle(hFTP);
    InternetCloseHandle(hNet);
    Exit;
  end;

  //dwBytesRead := 0;
  bufsize := READ_BUFFERSIZE;

  while (bufsize > 0) do
  begin
    Application.ProcessMessages;
    if not InternetReadFile(hFile, @buffer, READ_BUFFERSIZE, bufsize) then Break;
    if (bufsize > 0) and (bufsize <= READ_BUFFERSIZE) then BlockWrite(LocalFile, buffer, bufsize);
    dwBytesRead := dwBytesRead + bufsize;

  end;

  CloseFile(LocalFile);
  InternetCloseHandle(hFile);
  InternetCloseHandle(hFTP);
  InternetCloseHandle(hNet);

end;

0

Dzięki misiekd - troszkę pomogło, jestem jeden krok do przodu:
zrobiłem:

    LpdwBufferLength:= 1024;
    GetMem(lpszBuffer, LpdwBufferLength);
    InternetGetLastResponseInfo(lpdwError, lpszBuffer, lpdwBufferLength);
    ShowMessage(lpszBuffer);
    FreeMem(lpszBuffer);
 

Funkcja InternetGetLastResponseInfo zwróciła mi:

220 ProFTPD 1.3.3a Server ready.
331 Anonymous login ok, send your complete email address as your password

problem w tym, że nie wiem teraz jakiej funkcji użyć aby wysłać to hasło.
Kiedy użyłem mojej procedury z argumentem dla hasła mój e-mail niestety eftep
dał taki sam rezultat jak powyżej :/

Może jeszcze jakieś pomysły? :)

0
grzesiekyogi napisał(a):

Może jeszcze jakieś pomysły? :)

Wszystko dotyczące protokołu FTP jest tutaj: http://tools.ietf.org/html/rfc959

0
-123oho napisał(a):
grzesiekyogi napisał(a):

Może jeszcze jakieś pomysły? :)

Wszystko dotyczące protokołu FTP jest tutaj: http://tools.ietf.org/html/rfc959

Niestety tutaj niewiele jest - owszem bogaty opis samego protokołu, statusy (mój status
331 - konieczne hasło). Wszystko gra. Problem w tym, że nie wiem jak wysłać to hasło
na serwer używając wininet :/ Próbowałem FtpCommand - ale coś mi nie wyszło :/.

0

Może tak
anonymous:[email protected]

0

Próbowałem FtpCommand - ale coś mi nie wyszło

To zrób tak żeby wyszło, z dokumentacji można pewnie dużo wyczytać na ten temat. Możesz też podejrzeć jakie komendy wysyła FileZilla.

1

przed

{ Change directory }
  bSuccess := FtpSetCurrentDirectory(hFTP, PChar(ftpDir));

wstaw

FtpCommand(hFTP, false, FTP_TRANSFER_TYPE_ASCII, PChar('PASS [email protected]'), 0, nil)
0

Powoli załamuje ręce:/

Może tak
anonymous:[email protected]

Niestety nie działa :( - efekt ten sam

To zrób tak żeby wyszło, z dokumentacji można pewnie dużo wyczytać na ten temat. Możesz też podejrzeć jakie komendy wysyła FileZilla.

Zainstalowałem FZ - program prowadzi rozmowę z serwerem i początek jest taki sam jak u mnie
również wyskakuje status 331 "Anonymous login ok, send your complete email address as your password"
Problem w tym że FZ umie wysłać polecenie PASS - ja niestety tego nie umiem :/

FtpCommand(hFTP, false, FTP_TRANSFER_TYPE_ASCII, PChar('PASS [email protected]'), 0, nil)

Misiekd - widzę, że się zagłębiłeś w składnię FtpCommand - też to próbowałem.
Problem jest w tym, że pierwszy argument hFTP pochodzi z problematycznej funkcji InternetConnect,
niestety on ma wartość nil :/ Dlatego funkcja FtpCommand nie działa (jest to błędne koło
nie mogę wysłać polecenia PASS na serwer bo nie mam do niego dostępu - a nie mam dostępu
bo nie mogę wysłać hasła). Jakoś musi dać się to ominąć za pomocą WinApi - niestety cały dzień
przesiedziałem i nie mam zielonego pojęcia jak.

0

Wróciłem z jak to określił @-123oho "wakacji", które były w sumie krótkim wyjazdem daleko poza rodzinne miasto, no i mam do autora wątku parę pytań:

  1. Czy ktoś zabronił Tobie sprawdzić jakie dokładnie dane do logowania podaje Total Commander pod jakimś Snifferem jak na przykład WireShark?

  2. Po co korzystasz z funkcji, które o ile się orientuje są zależne od zainstalowanego w systmie IE? Dlaczego zamiast tego nie skorzystać z Synapse, czy użycia tego pakietu również ktoś Tobie zabronił? Jest darmowy, posiada dokumentacje oraz przykłady użycia.

  3. Z podglądu logowania z pod Total Commandera dokonanego WireSharkiem z ustawionym fikcyjnym e-mailem w wcx_ftp.ini takim jak widać poniżej wszystko jasne:

220 ProFTPD 1.3.3a Server ready.

USER anonymous

331 Anonymous login ok, send your complete email address as your password

PASS [email protected]

Logowanie zakancza się powodzeniem. Również z prostego klienta FTP napisanego z użyciem Synapse. Źródła nie dostaniesz, bo źródła ode mnie się skończyły, bo rzekomo zacząłem wysyłać malware, ludziom przez to zaczęły wybuchać komputery. Ogólnie tragedia i chaos. Dlatego zaprzestałem takich praktyk, bo mimo nabicia sobie ponad 2000 postów w tematach dotyczących Delphi i Pascala głupimi i nic nie wnoszącymi odpowiedziami, które nigdy nikogo nie nawet ciutkę nie naprowadziły na rozwiązanie problemów nagle miałbym pisać z sensem. To nie dla mne ;> Także podsumowując. Spróbuj z Synapse. I koniecznie używaj TBrain. Do niezbędny składnik w Delphi lub FPĆ każdego programisty próbującego pisać w tym języku. I nie korzystaj z jakiś badziewnych funkcji. Czasami za dużo "nakładek" na inne systemowe funkcje może nie wyjść na dobre. Ewentualnie jeśli nie Synapse to pobierz sobie SimpleTCP Mateusza Piechnata i zrób logowanie oraz obsługę protokołu FTP w oparciu o sockety i ten świetny moduł. Linkami do RFĆ i opisu protokołu jeśli dobrze zauważyłem, już wspomogli poprzednicy.

0

Źródła nie dostaniesz, bo źródła ode mnie się skończyły, bo rzekomo zacząłem wysyłać malware

Jak wysyłasz im skompilowane .exe to może faktycznie masz syfa z kompilatorze? Poczytaj o tym, było jakieś teoretycznie niegroźne dziadostwo które podmieniało jeden ze standardowych modułów i automatycznie wkradało się tym sposobem do każdej skompilowanej aplikacji.

1

Zgadza się synapse bez problemu loguje (nawet hasło może być puste):

procedure TForm1.OnFTPStatus(Sender: TObject; Response: Boolean; const Value: string);
begin
  Memo1.Lines.Add(Value);
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  HOST = 'ftp.bossa.pl';
  USER = 'anonymous';
  PASS = ''; //'anonymous';
var
  ftp: TFTPSend;
  i: Integer;
begin
  ftp:= TFTPSend.Create;
  try
  ftp.TargetHost:= HOST;
  ftp.UserName:= USER;
  ftp.Password:= PASS;
  ftp.OnStatus:= OnFTPStatus;
  ftp.Login;
  ftp.ChangeWorkingDir('pub');
  ftp.List('', False);
  for i:=0 to ftp.FtpList.Count - 1 do
    Memo1.Lines.Add(ftp.FtpList.Items[i].FileName);
  ftp.Logout;
  finally
  ftp.Free;
  end;
end;

Co ciekawe Indy się na tym wykrzacza oczywiście jak by ktoś wnikał można poprawić w źródłach gdzieś zamiast kodu odpowiedzi otrzymuje (nul (dlatego że pobiera pierwsze 4 znaki jako kod odpowiedzi jak czwarty to myślnik wtedy go obcina) i sypie się z błędem "Reply Code is not valid: (nul" po czym rozłączenie... ale nie chce mi się myśleć jak to poprawić (zresztą nie wiem dlaczego ten serwer po wysłaniu komendy FEAT akurat tam zwraca to "(null)" i jeszcze bym coś innego przy okazji skopał co ciekawe w następnej linii odpowiedzi jest poprawnie "211-Features:")

EDIT//
Miałem trochę czasu i pownikałem w to Indy (wersja 10) więc do tego serwera (pewnie nie tylko do tego ale innego nie spotkałem na ktOrym nie działało) trzeba zrobić poprawkę w pliku IdReplyFTP.pas
w procedurze SetFormattedReply zaraz na początku wystarczy dodać:

  while (AValue.Count > 0) and (Copy(AValue[0], 1, 4) = '(nul') do
    AValue.Delete(0);

i śmiga jak złoto.

Odpowiednik powyższego kodu (Synapse) w Indy:

procedure TForm1.Button2Click(Sender: TObject);
const
  HOST = 'ftp.bossa.pl';
  USER = 'anonymous';
  PASS = ''; //'anonymous';
var
  i: Integer;
begin
  try
  IdFTP1.Host:= HOST;
  IdFTP1.Username:= USER;
  IdFTP1.Password:= PASS;
  IdFTP1.Passive:= True;
  IdFTP1.Connect(HOST, 21);
  IdFTP1.ChangeDir('pub');
  IdFTP1.List;
  for i:=0 to IdFTP1.ListResult.Count - 1 do
    Memo1.Lines.Add(IdFTP1.ListResult.Strings[i]);
  IdFTP1.Disconnect;
  finally
  IdFTP1.Free;
  end;
end;

procedure TForm1.IdFTP1Status(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: String);
begin
  Memo1.Lines.Add(AStatusText);
end;
1
Demonical Monk napisał(a):

Źródła nie dostaniesz, bo źródła ode mnie się skończyły, bo rzekomo zacząłem wysyłać malware

Jak wysyłasz im skompilowane .exe to może faktycznie masz syfa z kompilatorze? Poczytaj o tym, było jakieś teoretycznie niegroźne dziadostwo które podmieniało jeden ze standardowych modułów i automatycznie wkradało się tym sposobem do każdej skompilowanej aplikacji.

Wiem, kiedyś taki wirus "łyknąłem" bo jakiś czas miałem wyłaczony KAV 2010, ale teraz na pewno wirusa nie mam, a po prostu robię sobie jaja z kogoś kto sobie zrobił jaja ze mnie, zarzucając mi że malwarem w moim źródle jest plik build.bat o takiej zawartości. Jak widzisz nie ma tutaj szkodliwego kodu, jest to tylko ułatwienie. Można kompilować bez tego. Ale z pewnością nie ma tutaj grama szkodliwości. Osoba podejrzewająca tutaj malware ośmieszyła imo trochę siebie, ale i obraziła mnie, więc stwierdziłem że po co mam podsyłać gotowce jak się komuś i tak nie podoba, że dostał rozwiąznie "na tacy" to nie będzie miał żadnego ;/

@echo off
setlocal ENABLEDELAYEDEXPANSION
set dcu_output_path=.\DCU
for %%I in (*.exe) do (
  if exist %%~nI.dpr taskkill /im %%~nxI /f
  if exist %%~nI.dpr del %%~nxI
)
for %%I in (*.rc) do (
  del %%~nI.res
  brcc32 %%~nI.rc -fo %%~nI.res
  if not !errorlevel! == 0 goto show_error
)
for %%I in (*.dpr) do (
  dcc32 %%~nI.dpr
  if not !errorlevel! == 0 goto show_error
)
for %%I in (*.exe) do (
  if exist %%~nI.dpr (
    if exist *.dfm (
      upx --best --lzma %%~nxI
      if not !errorlevel! == 0 goto show_error
    ) else (
      upx --best %%~nxI
      if not !errorlevel! == 0 goto show_error
    )
  )
)
if exist %dcu_output_path% (
  cd %dcu_output_path%
  del *.dcu
  cd ..
) else (
  for %%I in (*.dcu) do (
    if exist %%~nI.pas del %%~nI.dcu
  )
)
goto the_end

:show_error
echo Error(s) - press any key
pause > nul

:the_end

A co do rozwiązania podał je na tacy @kAzek. Oby nikt też nie zarzucił mu malware "z d**y" ;)

0

Witam po kilku dniach przerwy spowodowanej przymusowym rozstaniem z komputerem i internetem, ale nie o tym tu mowa. Dlatego przejdę do rzeczy i uprzejmie podziękuję za wszystkie Wasze odpowiedzi - zwłaszcza kAzkowi i olesiowi. Faktycznie użyłem TIintuicja zamiast TBrain - stąd przytoczony na początku wątpliwej jakości kod. Faktycznie Wasze rozwiązania wyglądają lepiej i to je (a dokładniej kAzkowy kod z synapsem) zaimplementuję w moim programie. PS jak zwykle nie zawiodłem się na tym forum. Jeszcze raz dziękuję, pozdrawiam i stawiam wirtualnego browara <piwo>.

1 użytkowników online, w tym zalogowanych: 0, gości: 1