%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(ftp_format_SUITE).
-author('[email protected]').
-include("test_server.hrl").
-include("test_server_line.hrl").
-include("ftp_internal.hrl").
%% Test server specific exports
-export([all/1, init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
-export([ftp_response/1, ftp_150/1,
ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1,
ftp_other_status_codes/1, ftp_multiple_lines/1,
ftp_multipel_ctrl_messages/1, format_error/1]).
all(doc) ->
["Test library functions for the ftp client."];
all(suite) ->
[ftp_response, format_error].
init_per_testcase(_, Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
NewConfig = lists:keydelete(watchdog, 1, Config),
[{watchdog, Dog} | NewConfig].
end_per_testcase(_, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
ftp_response(doc) ->
["Test ftp_response:parse_lines/3 and ftp_response:interpret/1."
" This test case will simulate that the "
"message will be recived a little at the time on a socket and the "
"package may be broken up into smaller parts at arbitrary point."];
ftp_response(suite) ->
[ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331, ftp_425,
ftp_other_status_codes, ftp_multiple_lines, ftp_multipel_ctrl_messages].
ftp_150(doc) ->
["Especially check that respons can be devided in a random place."];
ftp_150(suite) ->
[];
ftp_150(Config) when is_list(Config) ->
FtpResponse = ["150 ASCII data conn", "ection for /bin/ls ",
"(134.138.177", ".89,50434) (0 bytes).\r\n"],
"150 ASCII data connection for /bin/ls "
"(134.138.177.89,50434) (0 bytes).\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_prel, _} = ftp_response:interpret(Msg),
ok.
ftp_200(doc) ->
["Especially check that respons can be devided after the first status "
"code character and in the end delimiter."];
ftp_200(suite) ->
[];
ftp_200(Config) when is_list(Config) ->
FtpResponse = ["2", "00 PORT command successful.", [?CR], [?LF]],
"200 PORT command successful.\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
ftp_220(doc) ->
["Especially check that respons can be devided after the "
"first with space "];
ftp_220(suite) ->
[];
ftp_220(Config) when is_list(Config) ->
FtpResponse = ["220 ","fingon FTP server (SunOS 5.8) ready.\r\n"],
"220 fingon FTP server (SunOS 5.8) ready.\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
ftp_226(doc) ->
["Especially check that respons can be devided after second status code"
" character and in the end delimiter."];
ftp_226(suite) ->
[];
ftp_226(Config) when is_list(Config) ->
FtpResponse = ["22" "6 Transfer complete.\r", [?LF]],
"226 Transfer complete.\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
ftp_257(doc) ->
["Especially check that quoted chars do not cause a problem."];
ftp_257(suite) ->
[];
ftp_257(Config) when is_list(Config) ->
FtpResponse = ["257 \"/\" is current directory.\r\n"],
"257 \"/\" is current directory.\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_compl, _} = ftp_response:interpret(Msg),
ok.
ftp_331(doc) ->
["Especially check that respons can be devided after the third status "
" status code character."];
ftp_331(suite) ->
[];
ftp_331(Config) when is_list(Config) ->
%% Brake before white space after code
FtpResponse =
["331"," Guest login ok, send ient as password.\r\n"],
"331 Guest login ok, send ient as password.\r\n" = Msg =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_interm, _} = ftp_response:interpret(Msg),
ok.
ftp_425(doc) ->
["Especially check a message that was received in only one part."];
ftp_425(suite) ->
[];
ftp_425(Config) when is_list(Config) ->
FtpResponse =
["425 Can't build data connection: Connection refused.\r\n"],
"425 Can't build data connection: Connection refused.\r\n"
= Msg = parse(ftp_response, parse_lines, [[], start], FtpResponse),
{trans_neg_compl, _} = ftp_response:interpret(Msg),
ok.
ftp_multiple_lines(doc) ->
["Especially check multiple lines devided in significant places"];
ftp_multiple_lines(suite) ->
[];
ftp_multiple_lines(Config) when is_list(Config) ->
FtpResponse = ["21", "4","-The",
" following commands are recognized:\r\n"
" USER EPRT STRU MAIL* ALLO CWD",
" STAT* XRMD \r\n"
" PASS LPRT MODE MSND* "
" REST* XCWD HELP PWD ", [?CRLF],
" ACCT* EPSV RETR MSOM* RNFR LIST "
" NOOP XPWD \r\n",
" REIN* LPSV STOR MSAM* RNTO NLST "
" MKD CDUP \r\n"
" QUIT PASV APPE MRSQ* ABOR SITE* "
" XMKD XCUP \r\n"
" PORT TYPE MLFL* MRCP* DELE SYST "
" RMD STOU \r\n"
"214 (*'s => unimplemented)", [?CR], [?LF]],
FtpResponse1 = ["214-", "The",
" following commands are recognized:\r\n"
" USER EPRT STRU MAIL* ALLO CWD",
" STAT* XRMD \r\n"
" PASS LPRT MODE MSND* "
" REST* XCWD HELP PWD ", [?CRLF],
" ACCT* EPSV RETR MSOM* RNFR LIST "
" NOOP XPWD \r\n",
" REIN* LPSV STOR MSAM* RNTO NLST "
" MKD CDUP \r\n"
" QUIT PASV APPE MRSQ* ABOR SITE* "
" XMKD XCUP \r\n"
" PORT TYPE MLFL* MRCP* DELE SYST "
" RMD STOU \r\n"
"2", "14 (*'s => unimplemented)", [?CR], [?LF]],
FtpResponse2 = ["214-", "The",
" following commands are recognized:\r\n"
" USER EPRT STRU MAIL* ALLO CWD",
" STAT* XRMD \r\n"
" PASS LPRT MODE MSND* "
" REST* XCWD HELP PWD ", [?CRLF],
" ACCT* EPSV RETR MSOM* RNFR LIST "
" NOOP XPWD \r\n",
" REIN* LPSV STOR MSAM* RNTO NLST "
" MKD CDUP \r\n"
" QUIT PASV APPE MRSQ* ABOR SITE* "
" XMKD XCUP \r\n"
" PORT TYPE MLFL* MRCP* DELE SYST "
" RMD STOU \r\n"
"21", "4"," (*'s => unimplemented)", [?CR], [?LF]],
MultiLineResultStr =
"214-The following commands are recognized:\r\n"
" USER EPRT STRU MAIL* ALLO CWD STAT* "
"XRMD \r\n"
" PASS LPRT MODE MSND* REST* XCWD HELP "
"PWD \r\n"
" ACCT* EPSV RETR MSOM* RNFR LIST NOOP "
"XPWD \r\n"
" REIN* LPSV STOR MSAM* RNTO NLST MKD "
"CDUP \r\n"
" QUIT PASV APPE MRSQ* ABOR SITE* XMKD "
"XCUP \r\n"
" PORT TYPE MLFL* MRCP* DELE SYST RMD "
"STOU \r\n"
"214 (*'s => unimplemented)\r\n",
MultiLineResultStr =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_compl, _} = ftp_response:interpret(MultiLineResultStr),
MultiLineResultStr = parse(ftp_response, parse_lines, [[], start],
FtpResponse1),
MultiLineResultStr = parse(ftp_response, parse_lines, [[], start],
FtpResponse2),
ok.
ftp_other_status_codes(doc) ->
["Check that other valid status codes, than the ones above, are handled"
"by ftp_response:interpret/1. Note there are som ftp status codes"
"that will not be received with the current ftp instruction support,"
"they are not included here."];
ftp_other_status_codes(suite) ->
[];
ftp_other_status_codes(Config) when is_list(Config) ->
%% 1XX
{pos_prel, _ } = ftp_response:interpret("120 Foobar\r\n"),
%% 2XX
{pos_compl, _ } = ftp_response:interpret("202 Foobar\r\n"),
{pos_compl, _ } = ftp_response:interpret("221 Foobar\r\n"),
{pos_compl, _ } = ftp_response:interpret("227 Foobar\r\n"),
{pos_compl, _ } = ftp_response:interpret("230 Foobar\r\n"),
{pos_compl, _ } = ftp_response:interpret("250 Foobar\r\n"),
%% 3XX
{pos_interm_acct, _ } = ftp_response:interpret("332 Foobar\r\n"),
{pos_interm, _ } = ftp_response:interpret("350 Foobar\r\n"),
%% 4XX
{trans_neg_compl, _ } = ftp_response:interpret("421 Foobar\r\n"),
{trans_neg_compl, _ } = ftp_response:interpret("426 Foobar\r\n"),
{trans_neg_compl, _ } = ftp_response:interpret("450 Foobar\r\n"),
{trans_neg_compl, _ } = ftp_response:interpret("451 Foobar\r\n"),
{etnospc, _ } = ftp_response:interpret("452 Foobar\r\n"),
%% 5XX
{perm_neg_compl, _ } = ftp_response:interpret("500 Foobar\r\n"),
{perm_neg_compl, _ } = ftp_response:interpret("501 Foobar\r\n"),
{perm_neg_compl, _ } = ftp_response:interpret("503 Foobar\r\n"),
{perm_neg_compl, _ } = ftp_response:interpret("504 Foobar\r\n"),
{perm_neg_compl, _ } = ftp_response:interpret("530 Foobar\r\n"),
{perm_neg_compl, _ } = ftp_response:interpret("532 Foobar\r\n"),
{epath, _ } = ftp_response:interpret("550 Foobar\r\n"),
{epnospc, _ } = ftp_response:interpret("552 Foobar\r\n"),
{efnamena, _ } = ftp_response:interpret("553 Foobar\r\n"),
ok.
ftp_multipel_ctrl_messages(doc) ->
["The ftp server may send more than one control message as a reply,"
"check that they are handled one at the time."];
ftp_multipel_ctrl_messages(suite) ->
[];
ftp_multipel_ctrl_messages(Config) when is_list(Config) ->
FtpResponse = ["200 PORT command successful.\r\n200 Foobar\r\n"],
{"200 PORT command successful.\r\n" = Msg, NextMsg} =
parse(ftp_response, parse_lines, [[], start], FtpResponse),
{pos_compl, _} = ftp_response:interpret(Msg),
NewMsg = parse(ftp_response, parse_lines, [[], start], NextMsg),
{pos_compl, _} = ftp_response:interpret(NewMsg),
ok.
%%-------------------------------------------------------------------------
format_error(doc) ->
[""];
format_error(suite) ->
[];
format_error(Config) when is_list(Config) ->
"Synchronisation error during chunk sending." =
ftp:formaterror(echunk),
"Session has been closed." = ftp:formaterror(eclosed),
"Connection to remote server prematurely closed." =
ftp:formaterror(econn),
"File or directory already exists." = ftp:formaterror(eexists),
"Host not found, FTP server not found, or connection rejected." =
ftp:formaterror(ehost),
"User not logged in." = ftp:formaterror(elogin),
"Term is not a binary." = ftp:formaterror(enotbinary),
"No such file or directory, already exists, or permission denied."
= ftp:formaterror(epath),
"No such type." = ftp:formaterror(etype),
"User name or password not valid." = ftp:formaterror(euser),
"Insufficient storage space in system." = ftp:formaterror(etnospc),
"Exceeded storage allocation (for current directory or dataset)."
= ftp:formaterror(epnospc),
"File name not allowed." = ftp:formaterror(efnamena),
"Unknown error: foobar" = ftp:formaterror({error, foobar}).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
parse(Module, Function, Args, Bin) when is_binary(Bin) ->
parse(Module, Function, Args, [binary_to_list(Bin)]);
parse(Module, Function, [AccLines, StatusCode], [Data | Rest]) ->
case Module:Function(list_to_binary(Data), AccLines, StatusCode) of
{ok, Result, <<>>} ->
Result;
{ok, Result, Next} ->
{Result, Next};
{continue, {NewData, NewAccLines, NewStatusCode}} ->
case Rest of
[] ->
test_server:fail({wrong_input, Data, Rest});
[_ | _] ->
parse(Module, Function, [NewAccLines, NewStatusCode],
[binary_to_list(NewData) ++ hd(Rest) | tl(Rest)])
end
end.